diff --git a/.github/codecov.yml b/.github/codecov.yml index 13e511f21af..af7439b56fc 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -5,3 +5,8 @@ coverage: target: 60% threshold: 5% if_ci_failed: error + patch: + default: + target: 60% + threshold: 5% + if_ci_failed: error diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2bbc1ff322f..4c9d1b7ccd5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,20 +13,28 @@ updates: directory: / schedule: interval: weekly + groups: + k8s.io: + patterns: + - "k8s.io/*" - package-ecosystem: pip directory: /tools/src/codespell schedule: interval: weekly - package-ecosystem: gomod - directory: /tools/src/golangci-lint + directory: /tools/src/helm-docs schedule: interval: weekly - package-ecosystem: gomod - directory: /tools/src/kind + directory: /tools/src/buf schedule: interval: weekly - package-ecosystem: gomod - directory: /tools/src/kustomize + directory: /tools/src/golangci-lint + schedule: + interval: weekly + - package-ecosystem: gomod + directory: /tools/src/kind schedule: interval: weekly - package-ecosystem: gomod diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index 71b2e49938c..c7265d52d15 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -16,7 +16,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps # Generate the install manifests first so it can checked # for errors while running `make -k lint` @@ -25,23 +25,23 @@ jobs: - run: make -k lint gen-check: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps - run: make -k gen-check license-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps - run: make -k licensecheck coverage-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps # test @@ -59,7 +59,7 @@ jobs: runs-on: ubuntu-latest needs: [lint, gen-check, license-check, coverage-test] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps - name: Build EG Multiarch Binaries @@ -76,9 +76,9 @@ jobs: needs: [build] strategy: matrix: - version: [ v1.25.8, v1.26.3, v1.27.0 ] + version: [ v1.26.6, v1.27.3, v1.28.0 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps - name: Download EG Binaries @@ -93,7 +93,7 @@ jobs: chmod +x bin/linux/arm64/envoy-gateway # conformance - - name: Run Conformance Tests + - name: Run Standard Conformance Tests env: KIND_NODE_TAG: ${{ matrix.version }} IMAGE_PULL_POLICY: IfNotPresent @@ -104,9 +104,9 @@ jobs: needs: [build] strategy: matrix: - version: [ v1.25.8, v1.26.3, v1.27.0 ] + version: [ v1.26.6, v1.27.3, v1.28.0 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps - name: Download EG Binaries @@ -131,7 +131,7 @@ jobs: runs-on: ubuntu-latest needs: [conformance-test, e2e-test] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./tools/github-actions/setup-deps - name: Download EG Binaries @@ -148,7 +148,7 @@ jobs: # build and push image - name: Login to DockerHub if: github.event_name == 'push' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} diff --git a/.github/workflows/cherrypick.yaml b/.github/workflows/cherrypick.yaml index 3ec9091a884..4b35693e9cc 100644 --- a/.github/workflows/cherrypick.yaml +++ b/.github/workflows/cherrypick.yaml @@ -7,12 +7,12 @@ on: jobs: cherry_pick_release_v0_5: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: Cherry pick into release-v0.5 if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.5') && github.event.pull_request.merged == true }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cherry pick into release/v0.5 diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 38c3dc7ee3e..ce9c9f9ce38 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -1,47 +1,103 @@ -name: Docs +name: Hugo Docs on: push: branches: - - "main" - paths-ignore: - - "**/*.png" - pull_request: - branches: - - "main" - paths-ignore: - - "**/*.png" + - "main" + - "release/v*" + paths: + - 'site/**' + pull_request_target: + types: [opened, synchronize, reopened] + paths: + - 'site/**' jobs: docs-lint: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - name: Check out code - uses: actions/checkout@v3 + - name: Check out code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - - name: Run markdown linter - uses: nosborn/github-action-markdown-cli@v3.3.0 - with: - files: docs/* - config_file: ".github/markdown_lint_config.json" + - name: Run markdown linter + uses: nosborn/github-action-markdown-cli@v3.3.0 + with: + files: site/content/* + config_file: ".github/markdown_lint_config.json" docs-build: runs-on: ubuntu-latest needs: docs-lint + permissions: + contents: write steps: - - uses: actions/checkout@v3 - - uses: ./tools/github-actions/setup-deps + - name: Git checkout + uses: actions/checkout@v4 + with: + submodules: true + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: 'latest' + extended: true - - name: Generate EG Pages - run: make docs + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' - # Upload docs for GitHub Pages - - name: Upload GitHub Pages artifact - uses: actions/upload-pages-artifact@v2.0.0 - with: - # Path of the directory containing the static assets. - path: docs/html - # Duration after which artifact will expire in days. - # retention-days: # optional, default is 1 + - name: Install Site Dependencies and Build Site + run: make docs + + # Upload docs for GitHub Pages + - name: Upload GitHub Pages artifact + uses: actions/upload-pages-artifact@v2.0.0 + with: + # Path of the directory containing the static assets. + path: site/public + # Duration after which artifact will expire in days. + # retention-days: # optional, default is 1 + + docs-preview: + if: "github.event_name == 'pull_request_target'" + needs: docs-build + runs-on: ubuntu-22.04 + steps: + - name: Git checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: 'latest' + extended: true + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' + - name: Install Site Dependencies and Build Site + run: make docs + - name: Deploy to Netlify + uses: nwtgck/actions-netlify@v2.1 + with: + publish-dir: 'site/public' + production-deploy: false + github-token: ${{ secrets.GITHUB_TOKEN }} + deploy-message: "Deploy from GitHub Actions" + alias: "${{ github.event.repository.name }}-pr-${{ github.event.pull_request.number }}-preview" + # these all default to 'true' + enable-pull-request-comment: true + enable-commit-comment: false + enable-commit-status: true + overwrites-pull-request-comment: true + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + timeout-minutes: 1 # This workflow contains a single job called "build" docs-publish: @@ -61,6 +117,6 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v2.0.3 + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v3.0.1 diff --git a/.github/workflows/experimental_conformance.yaml b/.github/workflows/experimental_conformance.yaml new file mode 100644 index 00000000000..fda6c8702c5 --- /dev/null +++ b/.github/workflows/experimental_conformance.yaml @@ -0,0 +1,35 @@ +name: Experimental Conformance Test +on: + push: + paths: + - 'charts/gateway-helm/crds/gatewayapi-crds.yaml' + pull_request: + paths: + - 'charts/gateway-helm/crds/gatewayapi-crds.yaml' + - 'test/conformance/*.go' + # Add workflow_dispatch to trigger this workflow manually by maintainers. + workflow_dispatch: + +jobs: + experimental-conformance-test: + runs-on: ubuntu-latest + strategy: + matrix: + version: [ v1.26.6, v1.27.3, v1.28.0 ] + steps: + - uses: actions/checkout@v4 + - uses: ./tools/github-actions/setup-deps + + # gateway api experimental conformance + - name: Run Experimental Conformance Tests + env: + CONFORMANCE_REPORT_PATH: conformance-report-k8s-${{ matrix.version }}.yaml + KIND_NODE_TAG: ${{ matrix.version }} + IMAGE_PULL_POLICY: IfNotPresent + run: make experimental-conformance + + - name: Upload Conformance Report + uses: actions/upload-artifact@v3 + with: + name: conformance-report-k8s-${{ matrix.version }} + path: ./test/conformance/conformance-report-k8s-${{ matrix.version }}.yaml diff --git a/.github/workflows/latest_release.yaml b/.github/workflows/latest_release.yaml index 6aa63facb86..f261e061786 100644 --- a/.github/workflows/latest_release.yaml +++ b/.github/workflows/latest_release.yaml @@ -9,9 +9,10 @@ on: jobs: latest-release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: ./tools/github-actions/setup-deps - name: Generate Release Manifests run: make generate-manifests IMAGE=envoyproxy/gateway-dev TAG=latest OUTPUT_DIR=release-artifacts diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 24d26f1e6c3..3879d6dd036 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,9 +7,9 @@ on: - "v*.*.*" jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Extract Release Tag and Commit SHA id: vars @@ -19,7 +19,7 @@ jobs: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} diff --git a/.github/workflows/retest.yaml b/.github/workflows/retest.yaml index cec2caf3021..21da1529924 100644 --- a/.github/workflows/retest.yaml +++ b/.github/workflows/retest.yaml @@ -22,6 +22,6 @@ jobs: pull-requests: write actions: write steps: - - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.0.10 + - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.17 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 3d96d5ab95d..f13fbcbec0e 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -9,13 +9,13 @@ jobs: issues: write # for actions/stale to close stale issues pull-requests: write # for actions/stale to close stale PRs name: Prune Stale - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # do not run it in forked repos if: github.repository == 'envoyproxy/gateway' steps: - name: Prune Stale - uses: actions/stale@v8 + uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Different amounts of days for issues/PRs are not currently supported but there is a PR diff --git a/.github/workflows/welcome.yaml b/.github/workflows/welcome.yaml new file mode 100644 index 00000000000..750c36f1c1d --- /dev/null +++ b/.github/workflows/welcome.yaml @@ -0,0 +1,40 @@ +name: Welcome + +on: + pull_request_target: + types: [opened] + +jobs: + comment: + permissions: + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Comment + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + 🚀 Thank you for contributing to the [Envoy Gateway](https://gateway.envoyproxy.io/) project! 🚀 + + Before merging, please ensure to follow the process below: + + 1. Requesting Reviews: + - cc @envoyproxy/gateway-reviewers team for an initial review. + - After the initial review, reviewers should request the @envoyproxy/gateway-maintainers team for further review. + 2. Review Approval: + - Your PR needs to receive at least two approvals. + - At least one approval must come from a member of the gateway-maintainers team. + + **NOTE**: Once your PR is under review, ***please do not rebase and force push it***. Otherwise, it will force your reviewers to review the PR from scratch rather than simply look at your latest changes. + +
+ What's more, you can help expedite the processing of your PR by +
+ + - Ensuring you have self-reviewed your work according to the project's [Contribution Guidelines](https://gateway.envoyproxy.io/latest/contributions/develop). + - If your PR addresses a specific issue, make sure to mention it in the PR description. + - Respond promptly if there are any test failures or suggestions for improvements that we comment on. + +
+ reactions: 'heart' diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..49b86533e91 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Code of Conduct + +Envoy Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/GOALS.md b/GOALS.md index 519be9f180f..8220a44de38 100644 --- a/GOALS.md +++ b/GOALS.md @@ -8,6 +8,7 @@ fundamental interactions. ## Objectives ### Expressive API + The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This @@ -23,6 +24,7 @@ This expressive API will not be implemented by Envoy Proxy, but rather an offici on top. ### Batteries included + Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on delivering core business value. @@ -37,6 +39,7 @@ will enjoy a simplified management model that doesn't require extensive knowledg operate. ### All environments + Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto @@ -44,6 +47,7 @@ standard for Kubernetes ingress supporting the [Gateway API][]. Additional goals include multi-cluster support and various runtime environments. ### Extensibility + Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation @@ -57,21 +61,26 @@ allowing vendors to shift to a higher management plane layer. ## Non-objectives ### Cannibalize vendor models + Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor monetization model, though some vendors may be affected by it. ### Disrupt current Envoy usage patterns + Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user with Envoy Proxy, xDS, or go-control-plane. ## Personas + _In order of priority_ ### 1. Application developer + The application developer spends the majority of their time developing business logic code. They require the ability to manage access to their application. ### 2. Infrastructure administrators + The infrastructure administrators are responsible for the installation, maintenance, and operation of API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. diff --git a/OWNERS b/OWNERS index 5339632bb76..4d581494fd7 100644 --- a/OWNERS +++ b/OWNERS @@ -11,15 +11,17 @@ maintainers: - AliceProxy - arkodg -- skriss - Xunzhuo -- youngnick - zirain +- qicz +- zhaohuabing reviewers: - chauhanshubham - kflynn - LanceEa -- qicz -- zhaohuabing +- tmsnan +- tanujd11 +- cnvergence +- shawnh2 diff --git a/README.md b/README.md index 03bbcf56da9..16afa3784a9 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ Kubernetes-based application gateway. * [Blog][blog] introducing Envoy Gateway. * [Goals](GOALS.md) -* [Quickstart](./docs/latest/user/quickstart.md) to use Envoy Gateway in a few simple steps. -* [Roadmap](./docs/latest/design/roadmap.md) +* [Quickstart](https://gateway.envoyproxy.io/latest/user/quickstart/) to use Envoy Gateway in a few simple steps. +* [Roadmap](https://gateway.envoyproxy.io/latest/contributions/roadmap/) ## Contact @@ -20,21 +20,19 @@ Kubernetes-based application gateway. ## Contributing -* [Code of conduct](./docs/latest/dev/CODE_OF_CONDUCT.md) -* [Contributing guide](./docs/latest/dev/CONTRIBUTING.md) -* [Developer guide](docs/latest/dev/README.md) +* [Code of conduct](https://gateway.envoyproxy.io/latest/contributions/code_of_conduct/) +* [Contributing guide](https://gateway.envoyproxy.io/latest/contributions/contributing/) +* [Developer guide](https://gateway.envoyproxy.io/latest/contributions/develop/) ## Community Meeting -The Envoy Gateway team meets every Tuesday and Thursday. We also have a separate meeting to be held in the +The Envoy Gateway team meets every Tuesday and Thursday. We also have a separate meeting to be held in the Chinese timezone every two weeks to better accommodate our Chinese community members who face scheduling difficulties for the weekly meetings. Please refer to the meeting details for additional information. * [Meeting details][meeting] - [meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing -[group]: https://groups.google.com/forum/#!forum/envoy-gateway-developers [blog]: https://blog.envoyproxy.io/introducing-envoy-gateway-ad385cc59532 [Envoy Slack workspace]: https://communityinviter.com/apps/envoyproxy/envoy [Envoy Gateway channel]: https://envoyproxy.slack.com/archives/C03E6NHLESV diff --git a/VERSION b/VERSION index 6250c604885..60f63432822 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.5.0-rc.1 +v0.6.0 diff --git a/api/config/doc.go b/api/config/doc.go deleted file mode 100644 index a9b235ea604..00000000000 --- a/api/config/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package config diff --git a/api/config/v1alpha1/doc.go b/api/config/v1alpha1/doc.go deleted file mode 100644 index cbf70c5e359..00000000000 --- a/api/config/v1alpha1/doc.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -// Package v1alpha1 contains API schema definitions for the config.gateway.envoyproxy.io -// API group. -// -// +kubebuilder:object:generate=true -// +groupName=config.gateway.envoyproxy.io -package v1alpha1 diff --git a/api/config/v1alpha1/envoyproxy_types.go b/api/config/v1alpha1/envoyproxy_types.go deleted file mode 100644 index 183c40ba406..00000000000 --- a/api/config/v1alpha1/envoyproxy_types.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - // KindEnvoyProxy is the name of the EnvoyProxy kind. - KindEnvoyProxy = "EnvoyProxy" -) - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// EnvoyProxy is the schema for the envoyproxies API. -type EnvoyProxy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // EnvoyProxySpec defines the desired state of EnvoyProxy. - Spec EnvoyProxySpec `json:"spec,omitempty"` - // EnvoyProxyStatus defines the actual state of EnvoyProxy. - Status EnvoyProxyStatus `json:"status,omitempty"` -} - -// EnvoyProxySpec defines the desired state of EnvoyProxy. -type EnvoyProxySpec struct { - // Provider defines the desired resource provider and provider-specific configuration. - // If unspecified, the "Kubernetes" resource provider is used with default configuration - // parameters. - // - // +optional - Provider *EnvoyProxyProvider `json:"provider,omitempty"` - - // Logging defines logging parameters for managed proxies. - // +kubebuilder:default={level: {default: warn}} - Logging ProxyLogging `json:"logging,omitempty"` - - // Telemetry defines telemetry parameters for managed proxies. - Telemetry ProxyTelemetry `json:"telemetry,omitempty"` - - // Bootstrap defines the Envoy Bootstrap as a YAML string. - // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap - // to learn more about the syntax. - // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration - // set by Envoy Gateway. - // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources - // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. - // Backward compatibility across minor versions is not guaranteed. - // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default - // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. - // - // +optional - Bootstrap *string `json:"bootstrap,omitempty"` -} - -type ProxyTelemetry struct { - // AccessLogs defines accesslog parameters for managed proxies. - // If unspecified, will send default format to stdout. - // +optional - AccessLog *ProxyAccessLog `json:"accessLog,omitempty"` - // Tracing defines tracing configuration for managed proxies. - // If unspecified, will not send tracing data. - // +optional - Tracing *ProxyTracing `json:"tracing,omitempty"` - - // Metrics defines metrics configuration for managed proxies. - Metrics *ProxyMetrics `json:"metrics,omitempty"` -} - -// EnvoyProxyProvider defines the desired state of a resource provider. -// +union -type EnvoyProxyProvider struct { - // Type is the type of resource provider to use. A resource provider provides - // infrastructure resources for running the data plane, e.g. Envoy proxy, and - // optional auxiliary control planes. Supported types are "Kubernetes". - // - // +unionDiscriminator - Type ProviderType `json:"type"` - // Kubernetes defines the desired state of the Kubernetes resource provider. - // Kubernetes provides infrastructure resources for running the data plane, - // e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings - // for managed Kubernetes resources are applied. - // - // +optional - Kubernetes *EnvoyProxyKubernetesProvider `json:"kubernetes,omitempty"` -} - -// EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource -// provider. -type EnvoyProxyKubernetesProvider struct { - // EnvoyDeployment defines the desired state of the Envoy deployment resource. - // If unspecified, default settings for the manged Envoy deployment resource - // are applied. - // - // +optional - EnvoyDeployment *KubernetesDeploymentSpec `json:"envoyDeployment,omitempty"` - - // EnvoyService defines the desired state of the Envoy service resource. - // If unspecified, default settings for the manged Envoy service resource - // are applied. - // - // +optional - EnvoyService *KubernetesServiceSpec `json:"envoyService,omitempty"` -} - -// ProxyLogging defines logging parameters for managed proxies. -type ProxyLogging struct { - // Level is a map of logging level per component, where the component is the key - // and the log level is the value. If unspecified, defaults to "default: warn". - // - // +kubebuilder:default={default: warn} - Level map[LogComponent]LogLevel `json:"level,omitempty"` -} - -// LogComponent defines a component that supports a configured logging level. -// +kubebuilder:validation:Enum=system;upstream;http;connection;admin;client;filter;main;router;runtime -type LogComponent string - -const ( - // LogComponentDefault defines the default logging component. - // See more details: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-l - LogComponentDefault LogComponent = "default" - - // LogComponentUpstream defines the "upstream" logging component. - LogComponentUpstream LogComponent = "upstream" - - // LogComponentHTTP defines the "http" logging component. - LogComponentHTTP LogComponent = "http" - - // LogComponentConnection defines the "connection" logging component. - LogComponentConnection LogComponent = "connection" - - // LogComponentAdmin defines the "admin" logging component. - LogComponentAdmin LogComponent = "admin" - - // LogComponentClient defines the "client" logging component. - LogComponentClient LogComponent = "client" - - // LogComponentFilter defines the "filter" logging component. - LogComponentFilter LogComponent = "filter" - - // LogComponentMain defines the "main" logging component. - LogComponentMain LogComponent = "main" - - // LogComponentRouter defines the "router" logging component. - LogComponentRouter LogComponent = "router" - - // LogComponentRuntime defines the "runtime" logging component. - LogComponentRuntime LogComponent = "runtime" -) - -// EnvoyProxyStatus defines the observed state of EnvoyProxy. This type is not implemented -// until https://github.com/envoyproxy/gateway/issues/1007 is fixed. -type EnvoyProxyStatus struct { - // INSERT ADDITIONAL STATUS FIELDS - define observed state of cluster. - // Important: Run "make" to regenerate code after modifying this file. -} - -// +kubebuilder:object:root=true - -// EnvoyProxyList contains a list of EnvoyProxy -type EnvoyProxyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []EnvoyProxy `json:"items"` -} - -func init() { - SchemeBuilder.Register(&EnvoyProxy{}, &EnvoyProxyList{}) -} diff --git a/api/config/v1alpha1/groupversion_info.go b/api/config/v1alpha1/groupversion_info.go deleted file mode 100644 index b8d005fb16e..00000000000 --- a/api/config/v1alpha1/groupversion_info.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "config.gateway.envoyproxy.io", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) diff --git a/api/config/v1alpha1/helpers.go b/api/config/v1alpha1/helpers.go deleted file mode 100644 index 2bac7e1fbad..00000000000 --- a/api/config/v1alpha1/helpers.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -import ( - appv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" -) - -// DefaultEnvoyGateway returns a new EnvoyGateway with default configuration parameters. -func DefaultEnvoyGateway() *EnvoyGateway { - return &EnvoyGateway{ - metav1.TypeMeta{ - Kind: KindEnvoyGateway, - APIVersion: GroupVersion.String(), - }, - EnvoyGatewaySpec{ - Gateway: DefaultGateway(), - Provider: DefaultEnvoyGatewayProvider(), - Logging: DefaultEnvoyGatewayLogging(), - Admin: DefaultEnvoyGatewayAdmin(), - }, - } -} - -// SetEnvoyGatewayDefaults sets default EnvoyGateway configuration parameters. -func (e *EnvoyGateway) SetEnvoyGatewayDefaults() { - if e.TypeMeta.Kind == "" { - e.TypeMeta.Kind = KindEnvoyGateway - } - if e.TypeMeta.APIVersion == "" { - e.TypeMeta.APIVersion = GroupVersion.String() - } - if e.Provider == nil { - e.Provider = DefaultEnvoyGatewayProvider() - } - if e.Gateway == nil { - e.Gateway = DefaultGateway() - } - if e.Logging == nil { - e.Logging = DefaultEnvoyGatewayLogging() - } - if e.Admin == nil { - e.Admin = DefaultEnvoyGatewayAdmin() - } -} - -// GetEnvoyGatewayAdmin returns the EnvoyGatewayAdmin of EnvoyGateway or a default EnvoyGatewayAdmin if unspecified. -func (e *EnvoyGateway) GetEnvoyGatewayAdmin() *EnvoyGatewayAdmin { - if e.Admin != nil { - if e.Admin.Address == nil { - e.Admin.Address = DefaultEnvoyGatewayAdminAddress() - } - return e.Admin - } - e.Admin = DefaultEnvoyGatewayAdmin() - - return e.Admin -} - -// DefaultGateway returns a new Gateway with default configuration parameters. -func DefaultGateway() *Gateway { - return &Gateway{ - ControllerName: GatewayControllerName, - } -} - -// DefaultEnvoyGatewayLogging returns a new EnvoyGatewayLogging with default configuration parameters. -func DefaultEnvoyGatewayLogging() *EnvoyGatewayLogging { - return &EnvoyGatewayLogging{ - Level: map[EnvoyGatewayLogComponent]LogLevel{ - LogComponentGatewayDefault: LogLevelInfo, - }, - } -} - -// DefaultEnvoyGatewayLoggingLevel returns a new EnvoyGatewayLogging with default configuration parameters. -// When v1alpha1.LogComponentGatewayDefault specified, all other logging components are ignored. -func (logging *EnvoyGatewayLogging) DefaultEnvoyGatewayLoggingLevel(level LogLevel) LogLevel { - if level != "" { - return level - } - - if logging.Level[LogComponentGatewayDefault] != "" { - return logging.Level[LogComponentGatewayDefault] - } - - return LogLevelInfo -} - -// SetEnvoyGatewayLoggingDefaults sets default EnvoyGatewayLogging configuration parameters. -func (logging *EnvoyGatewayLogging) SetEnvoyGatewayLoggingDefaults() { - if logging != nil && logging.Level != nil && logging.Level[LogComponentGatewayDefault] == "" { - logging.Level[LogComponentGatewayDefault] = LogLevelInfo - } -} - -// DefaultEnvoyGatewayProvider returns a new EnvoyGatewayProvider with default configuration parameters. -func DefaultEnvoyGatewayProvider() *EnvoyGatewayProvider { - return &EnvoyGatewayProvider{ - Type: ProviderTypeKubernetes, - } -} - -// GetEnvoyGatewayProvider returns the EnvoyGatewayProvider of EnvoyGateway or a default EnvoyGatewayProvider if unspecified. -func (e *EnvoyGateway) GetEnvoyGatewayProvider() *EnvoyGatewayProvider { - if e.Provider != nil { - return e.Provider - } - e.Provider = DefaultEnvoyGatewayProvider() - - return e.Provider -} - -// DefaultEnvoyGatewayKubeProvider returns a new EnvoyGatewayKubernetesProvider with default settings. -func DefaultEnvoyGatewayKubeProvider() *EnvoyGatewayKubernetesProvider { - return &EnvoyGatewayKubernetesProvider{ - RateLimitDeployment: DefaultKubernetesDeployment(DefaultRateLimitImage), - } -} - -// DefaultEnvoyProxyProvider returns a new EnvoyProxyProvider with default settings. -func DefaultEnvoyProxyProvider() *EnvoyProxyProvider { - return &EnvoyProxyProvider{ - Type: ProviderTypeKubernetes, - } -} - -// GetEnvoyProxyProvider returns the EnvoyProxyProvider of EnvoyProxy or a default EnvoyProxyProvider -// if unspecified. -func (e *EnvoyProxy) GetEnvoyProxyProvider() *EnvoyProxyProvider { - if e.Spec.Provider != nil { - return e.Spec.Provider - } - e.Spec.Provider = DefaultEnvoyProxyProvider() - - return e.Spec.Provider -} - -// DefaultEnvoyProxyKubeProvider returns a new EnvoyProxyKubernetesProvider with default settings. -func DefaultEnvoyProxyKubeProvider() *EnvoyProxyKubernetesProvider { - return &EnvoyProxyKubernetesProvider{ - EnvoyDeployment: DefaultKubernetesDeployment(DefaultEnvoyProxyImage), - EnvoyService: DefaultKubernetesService(), - } -} - -// DefaultKubernetesDeploymentReplicas returns the default replica settings. -func DefaultKubernetesDeploymentReplicas() *int32 { - repl := int32(DefaultDeploymentReplicas) - return &repl -} - -// DefaultKubernetesDeploymentStrategy returns the default deployment strategy settings. -func DefaultKubernetesDeploymentStrategy() *appv1.DeploymentStrategy { - return &appv1.DeploymentStrategy{ - Type: appv1.RollingUpdateDeploymentStrategyType, - } -} - -// DefaultKubernetesContainerImage returns the default envoyproxy image. -func DefaultKubernetesContainerImage(image string) *string { - return pointer.String(image) -} - -// DefaultKubernetesDeployment returns a new KubernetesDeploymentSpec with default settings. -func DefaultKubernetesDeployment(image string) *KubernetesDeploymentSpec { - return &KubernetesDeploymentSpec{ - Replicas: DefaultKubernetesDeploymentReplicas(), - Strategy: DefaultKubernetesDeploymentStrategy(), - Pod: DefaultKubernetesPod(), - Container: DefaultKubernetesContainer(image), - } -} - -// DefaultKubernetesPod returns a new KubernetesPodSpec with default settings. -func DefaultKubernetesPod() *KubernetesPodSpec { - return &KubernetesPodSpec{} -} - -// DefaultKubernetesContainer returns a new KubernetesContainerSpec with default settings. -func DefaultKubernetesContainer(image string) *KubernetesContainerSpec { - return &KubernetesContainerSpec{ - Resources: DefaultResourceRequirements(), - Image: DefaultKubernetesContainerImage(image), - } -} - -// DefaultResourceRequirements returns a new ResourceRequirements with default settings. -func DefaultResourceRequirements() *corev1.ResourceRequirements { - return &corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse(DefaultDeploymentCPUResourceRequests), - corev1.ResourceMemory: resource.MustParse(DefaultDeploymentMemoryResourceRequests), - }, - } -} - -// DefaultKubernetesService returns a new KubernetesServiceSpec with default settings. -func DefaultKubernetesService() *KubernetesServiceSpec { - return &KubernetesServiceSpec{ - Type: DefaultKubernetesServiceType(), - } -} - -// DefaultKubernetesServiceType returns a new KubernetesServiceType with default settings. -func DefaultKubernetesServiceType() *ServiceType { - return GetKubernetesServiceType(ServiceTypeLoadBalancer) -} - -// GetKubernetesServiceType returns the KubernetesServiceType pointer. -func GetKubernetesServiceType(serviceType ServiceType) *ServiceType { - return &serviceType -} - -// GetEnvoyProxyKubeProvider returns the EnvoyProxyKubernetesProvider of EnvoyProxyProvider or -// a default EnvoyProxyKubernetesProvider if unspecified. If EnvoyProxyProvider is not of -// type "Kubernetes", a nil EnvoyProxyKubernetesProvider is returned. -func (r *EnvoyProxyProvider) GetEnvoyProxyKubeProvider() *EnvoyProxyKubernetesProvider { - if r.Type != ProviderTypeKubernetes { - return nil - } - - if r.Kubernetes == nil { - r.Kubernetes = DefaultEnvoyProxyKubeProvider() - return r.Kubernetes - } - - if r.Kubernetes.EnvoyDeployment == nil { - r.Kubernetes.EnvoyDeployment = DefaultKubernetesDeployment(DefaultEnvoyProxyImage) - } - - r.Kubernetes.EnvoyDeployment.defaultKubernetesDeploymentSpec(DefaultEnvoyProxyImage) - - if r.Kubernetes.EnvoyService == nil { - r.Kubernetes.EnvoyService = DefaultKubernetesService() - } - - if r.Kubernetes.EnvoyService.Type == nil { - r.Kubernetes.EnvoyService.Type = GetKubernetesServiceType(ServiceTypeLoadBalancer) - } - - return r.Kubernetes -} - -// GetEnvoyGatewayKubeProvider returns the EnvoyGatewayKubernetesProvider of Provider or -// a default EnvoyGatewayKubernetesProvider if unspecified. If EnvoyGatewayProvider is not of -// type "Kubernetes", a nil EnvoyGatewayKubernetesProvider is returned. -func (r *EnvoyGatewayProvider) GetEnvoyGatewayKubeProvider() *EnvoyGatewayKubernetesProvider { - if r.Type != ProviderTypeKubernetes { - return nil - } - - if r.Kubernetes == nil { - r.Kubernetes = DefaultEnvoyGatewayKubeProvider() - return r.Kubernetes - } - - if r.Kubernetes.RateLimitDeployment == nil { - r.Kubernetes.RateLimitDeployment = DefaultKubernetesDeployment(DefaultRateLimitImage) - } - - r.Kubernetes.RateLimitDeployment.defaultKubernetesDeploymentSpec(DefaultRateLimitImage) - - return r.Kubernetes -} - -// defaultKubernetesDeploymentSpec fill a default KubernetesDeploymentSpec if unspecified. -func (deployment *KubernetesDeploymentSpec) defaultKubernetesDeploymentSpec(image string) { - if deployment.Replicas == nil { - deployment.Replicas = DefaultKubernetesDeploymentReplicas() - } - - if deployment.Strategy == nil { - deployment.Strategy = DefaultKubernetesDeploymentStrategy() - } - - if deployment.Pod == nil { - deployment.Pod = DefaultKubernetesPod() - } - - if deployment.Container == nil { - deployment.Container = DefaultKubernetesContainer(image) - } - - if deployment.Container.Resources == nil { - deployment.Container.Resources = DefaultResourceRequirements() - } - - if deployment.Container.Image == nil { - deployment.Container.Image = DefaultKubernetesContainerImage(image) - } -} - -// DefaultEnvoyGatewayAdmin returns a new EnvoyGatewayAdmin with default configuration parameters. -func DefaultEnvoyGatewayAdmin() *EnvoyGatewayAdmin { - return &EnvoyGatewayAdmin{ - Debug: false, - Address: DefaultEnvoyGatewayAdminAddress(), - } -} - -// DefaultEnvoyGatewayAdminAddress returns a new EnvoyGatewayAdminAddress with default configuration parameters. -func DefaultEnvoyGatewayAdminAddress() *EnvoyGatewayAdminAddress { - return &EnvoyGatewayAdminAddress{ - Port: GatewayAdminPort, - Host: GatewayAdminHost, - } -} diff --git a/api/config/v1alpha1/metric_types.go b/api/config/v1alpha1/metric_types.go deleted file mode 100644 index b1151b24132..00000000000 --- a/api/config/v1alpha1/metric_types.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -type ProxyMetrics struct { - // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. - Prometheus *PrometheusProvider `json:"prometheus,omitempty"` - // Sinks defines the metric sinks where metrics are sent to. - Sinks []MetricSink `json:"sinks,omitempty"` -} - -type MetricSinkType string - -const ( - MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" -) - -type MetricSink struct { - // Type defines the metric sink type. - // EG currently only supports OpenTelemetry. - // +kubebuilder:validation:Enum=OpenTelemetry - // +kubebuilder:default=OpenTelemetry - Type MetricSinkType `json:"type"` - // OpenTelemetry defines the configuration for OpenTelemetry sink. - // It's required if the sink type is OpenTelemetry. - OpenTelemetry *OpenTelemetrySink `json:"openTelemetry,omitempty"` -} - -type OpenTelemetrySink struct { - // Host define the service hostname. - Host string `json:"host"` - // Port defines the port the service is exposed on. - // - // +optional - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:Maximum=65535 - // +kubebuilder:default=4317 - Port int32 `json:"port,omitempty"` - - // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig -} - -type PrometheusProvider struct { -} diff --git a/api/config/v1alpha1/shared_types.go b/api/config/v1alpha1/shared_types.go deleted file mode 100644 index 782089a7356..00000000000 --- a/api/config/v1alpha1/shared_types.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -import ( - appv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" -) - -const ( - // DefaultDeploymentReplicas is the default number of deployment replicas. - DefaultDeploymentReplicas = 1 - // DefaultDeploymentCPUResourceRequests for deployment cpu resource - DefaultDeploymentCPUResourceRequests = "100m" - // DefaultDeploymentMemoryResourceRequests for deployment memory resource - DefaultDeploymentMemoryResourceRequests = "512Mi" - // DefaultEnvoyProxyImage is the default image used by envoyproxy - DefaultEnvoyProxyImage = "envoyproxy/envoy-dev:latest" - // DefaultRateLimitImage is the default image used by ratelimit. - DefaultRateLimitImage = "envoyproxy/ratelimit:master" -) - -// GroupVersionKind unambiguously identifies a Kind. -// It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind -type GroupVersionKind struct { - Group string `json:"group"` - Version string `json:"version"` - Kind string `json:"kind"` -} - -// ProviderType defines the types of providers supported by Envoy Gateway. -// -// +kubebuilder:validation:Enum=Kubernetes -type ProviderType string - -const ( - // ProviderTypeKubernetes defines the "Kubernetes" provider. - ProviderTypeKubernetes ProviderType = "Kubernetes" - - // ProviderTypeFile defines the "File" provider. This type is not implemented - // until https://github.com/envoyproxy/gateway/issues/1001 is fixed. - ProviderTypeFile ProviderType = "File" -) - -// KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. -type KubernetesDeploymentSpec struct { - // Replicas is the number of desired pods. Defaults to 1. - // - // +optional - Replicas *int32 `json:"replicas,omitempty"` - - // The deployment strategy to use to replace existing pods with new ones. - // +optional - Strategy *appv1.DeploymentStrategy `json:"strategy,omitempty"` - - // Pod defines the desired annotations and securityContext of container. - // - // +optional - Pod *KubernetesPodSpec `json:"pod,omitempty"` - - // Container defines the resources and securityContext of container. - // - // +optional - Container *KubernetesContainerSpec `json:"container,omitempty"` - - // TODO: Expose config as use cases are better understood, e.g. labels. -} - -// KubernetesPodSpec defines the desired state of the Kubernetes pod resource. -type KubernetesPodSpec struct { - // Annotations are the annotations that should be appended to the pods. - // By default, no pod annotations are appended. - // - // +optional - Annotations map[string]string `json:"annotations,omitempty"` - - // Labels are the additional labels that should be tagged to the pods. - // By default, no additional pod labels are tagged. - // - // +optional - Labels map[string]string `json:"labels,omitempty"` - - // SecurityContext holds pod-level security attributes and common container settings. - // Optional: Defaults to empty. See type description for default values of each field. - // - // +optional - SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` - - // If specified, the pod's scheduling constraints. - // +optional - Affinity *corev1.Affinity `json:"affinity,omitempty"` - - // If specified, the pod's tolerations. - // +optional - Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - - // Volumes that can be mounted by containers belonging to the pod. - // More info: https://kubernetes.io/docs/concepts/storage/volumes - // - // +optional - Volumes []corev1.Volume `json:"volumes,omitempty"` -} - -// KubernetesContainerSpec defines the desired state of the Kubernetes container resource. -type KubernetesContainerSpec struct { - // List of environment variables to set in the container. - // - // +optional - Env []corev1.EnvVar `json:"env,omitempty"` - - // Resources required by this container. - // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - // - // +optional - Resources *corev1.ResourceRequirements `json:"resources,omitempty"` - - // SecurityContext defines the security options the container should be run with. - // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. - // More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ - // - // +optional - SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` - - // Image specifies the EnvoyProxy container image to be used, instead of the default image. - // - // +optional - Image *string `json:"image,omitempty"` - - // VolumeMounts are volumes to mount into the container's filesystem. - // Cannot be updated. - // - // +optional - VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` -} - -// ServiceType string describes ingress methods for a service -// +enum -// +kubebuilder:validation:Enum=ClusterIP;LoadBalancer;NodePort -type ServiceType string - -const ( - // ServiceTypeClusterIP means a service will only be accessible inside the - // cluster, via the cluster IP. - ServiceTypeClusterIP ServiceType = "ClusterIP" - - // ServiceTypeLoadBalancer means a service will be exposed via an - // external load balancer (if the cloud provider supports it). - ServiceTypeLoadBalancer ServiceType = "LoadBalancer" - - // ServiceTypeNodePort means a service will be exposed on each Kubernetes Node - // at a static Port, common across all Nodes. - ServiceTypeNodePort ServiceType = "NodePort" -) - -// KubernetesServiceSpec defines the desired state of the Kubernetes service resource. -type KubernetesServiceSpec struct { - // Annotations that should be appended to the service. - // By default, no annotations are appended. - // - // +optional - Annotations map[string]string `json:"annotations,omitempty"` - - // Type determines how the Service is exposed. Defaults to LoadBalancer. - // Valid options are ClusterIP, LoadBalancer and NodePort. - // "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). - // "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. - // "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. - // +kubebuilder:default:="LoadBalancer" - // +optional - Type *ServiceType `json:"type,omitempty"` - - // TODO: Expose config as use cases are better understood, e.g. labels. -} - -// LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. -// This type is not implemented for EnvoyProxy until -// https://github.com/envoyproxy/gateway/issues/280 is fixed. -// +kubebuilder:validation:Enum=debug;info;error;warn -type LogLevel string - -const ( - // LogLevelDebug defines the "debug" logging level. - LogLevelDebug LogLevel = "debug" - - // LogLevelInfo defines the "Info" logging level. - LogLevelInfo LogLevel = "info" - - // LogLevelWarn defines the "Warn" logging level. - LogLevelWarn LogLevel = "warn" - - // LogLevelError defines the "Error" logging level. - LogLevelError LogLevel = "error" -) - -// XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support -// for the xds-translator -// -// +kubebuilder:validation:Enum=VirtualHost;Route;HTTPListener;Translation -type XDSTranslatorHook string - -const ( - XDSVirtualHost XDSTranslatorHook = "VirtualHost" - XDSRoute XDSTranslatorHook = "Route" - XDSHTTPListener XDSTranslatorHook = "HTTPListener" - XDSTranslation XDSTranslatorHook = "Translation" -) diff --git a/api/config/v1alpha1/validation/validate.go b/api/config/v1alpha1/validation/validate.go deleted file mode 100644 index 7f3e59d8027..00000000000 --- a/api/config/v1alpha1/validation/validate.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package validation - -import ( - "errors" - "fmt" - "reflect" - - bootstrapv3 "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/testing/protocmp" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "sigs.k8s.io/yaml" - - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" - "github.com/envoyproxy/gateway/internal/xds/bootstrap" - _ "github.com/envoyproxy/gateway/internal/xds/extensions" // register the generated types to support protojson unmarshalling -) - -// Validate validates the provided EnvoyProxy. -func ValidateEnvoyProxy(proxy *egcfgv1a1.EnvoyProxy) error { - var errs []error - if proxy == nil { - return errors.New("envoyproxy is nil") - } - if err := validateEnvoyProxySpec(&proxy.Spec); err != nil { - errs = append(errs, err) - } - - return utilerrors.NewAggregate(errs) -} - -// validateEnvoyProxySpec validates the provided EnvoyProxy spec. -func validateEnvoyProxySpec(spec *egcfgv1a1.EnvoyProxySpec) error { - var errs []error - - if spec == nil { - errs = append(errs, errors.New("spec is nil")) - } - - // validate provider - validateProviderErrs := validateProvider(spec) - if len(validateProviderErrs) != 0 { - errs = append(errs, validateProviderErrs...) - } - - // validate bootstrap - if spec != nil && spec.Bootstrap != nil { - if err := validateBootstrap(spec.Bootstrap); err != nil { - errs = append(errs, err) - } - } - return utilerrors.NewAggregate(errs) -} - -func validateProvider(spec *egcfgv1a1.EnvoyProxySpec) []error { - var errs []error - if spec != nil && spec.Provider != nil { - if spec.Provider.Type != egcfgv1a1.ProviderTypeKubernetes { - errs = append(errs, fmt.Errorf("unsupported provider type %v", spec.Provider.Type)) - } - validateServiceTypeErrs := validateServiceType(spec) - if len(validateServiceTypeErrs) != 0 { - errs = append(errs, validateServiceTypeErrs...) - } - } - return errs -} - -func validateServiceType(spec *egcfgv1a1.EnvoyProxySpec) []error { - var errs []error - if spec.Provider.Kubernetes != nil && spec.Provider.Kubernetes.EnvoyService != nil { - if serviceType := spec.Provider.Kubernetes.EnvoyService.Type; serviceType != nil { - if *serviceType != egcfgv1a1.ServiceTypeLoadBalancer && - *serviceType != egcfgv1a1.ServiceTypeClusterIP && - *serviceType != egcfgv1a1.ServiceTypeNodePort { - errs = append(errs, fmt.Errorf("unsupported envoy service type %v", serviceType)) - } - } - } - return errs -} - -func validateBootstrap(boostrapConfig *string) error { - userBootstrap := &bootstrapv3.Bootstrap{} - jsonData, err := yaml.YAMLToJSON([]byte(*boostrapConfig)) - if err != nil { - return fmt.Errorf("unable to convert user bootstrap to json: %w", err) - } - - if err := protojson.Unmarshal(jsonData, userBootstrap); err != nil { - return fmt.Errorf("unable to unmarshal user bootstrap: %w", err) - } - - // Call Validate method - if err := userBootstrap.Validate(); err != nil { - return fmt.Errorf("validation failed for user bootstrap: %w", err) - } - defaultBootstrap := &bootstrapv3.Bootstrap{} - // TODO: need validate when enable prometheus? - defaultBootstrapStr, err := bootstrap.GetRenderedBootstrapConfig(nil) - if err != nil { - return err - } - - jsonData, err = yaml.YAMLToJSON([]byte(defaultBootstrapStr)) - if err != nil { - return fmt.Errorf("unable to convert default bootstrap to json: %w", err) - } - - if err := protojson.Unmarshal(jsonData, defaultBootstrap); err != nil { - return fmt.Errorf("unable to unmarshal default bootstrap: %w", err) - } - - // Ensure dynamic resources config is same - if userBootstrap.DynamicResources == nil || - cmp.Diff(userBootstrap.DynamicResources, defaultBootstrap.DynamicResources, protocmp.Transform()) != "" { - return fmt.Errorf("dynamic_resources cannot be modified") - } - // Ensure layered runtime resources config is same - if userBootstrap.LayeredRuntime == nil || - cmp.Diff(userBootstrap.LayeredRuntime, defaultBootstrap.LayeredRuntime, protocmp.Transform()) != "" { - return fmt.Errorf("layered_runtime cannot be modified") - } - // Ensure that the xds_cluster config is same - var userXdsCluster, defaultXdsCluster *clusterv3.Cluster - for _, cluster := range userBootstrap.StaticResources.Clusters { - if cluster.Name == "xds_cluster" { - userXdsCluster = cluster - break - } - } - for _, cluster := range defaultBootstrap.StaticResources.Clusters { - if cluster.Name == "xds_cluster" { - defaultXdsCluster = cluster - break - } - } - - // nolint // Circumvents this error "Error: copylocks: call of reflect.DeepEqual copies lock value:" - if userXdsCluster == nil || !reflect.DeepEqual(*userXdsCluster.LoadAssignment, *defaultXdsCluster.LoadAssignment) { - return fmt.Errorf("xds_cluster's loadAssigntment cannot be modified") - } - - return nil -} diff --git a/api/config/v1alpha1/validation/validate_test.go b/api/config/v1alpha1/validation/validate_test.go deleted file mode 100644 index 37ee3c66ae7..00000000000 --- a/api/config/v1alpha1/validation/validate_test.go +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package validation - -import ( - // Register embed - _ "embed" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" -) - -var ( - //go:embed testdata/valid-user-bootstrap.yaml - validUserBootstrap string - //go:embed testdata/missing-admin-address-user-bootstrap.yaml - missingAdminAddressUserBootstrap string - //go:embed testdata/different-dynamic-resources-user-bootstrap.yaml - differentDynamicResourcesUserBootstrap string - //go:embed testdata/different-xds-cluster-address-bootstrap.yaml - differentXdsClusterAddressBootstrap string -) - -func TestValidateEnvoyProxy(t *testing.T) { - testCases := []struct { - name string - proxy *egcfgv1a1.EnvoyProxy - expected bool - }{ - { - name: "nil egcfgv1a1.EnvoyProxy", - proxy: nil, - expected: false, - }, - { - name: "nil provider", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: nil, - }, - }, - expected: true, - }, - { - name: "unsupported provider", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeFile, - }, - }, - }, - expected: false, - }, - { - name: "nil envoy service", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyProxyKubernetesProvider{ - EnvoyService: nil, - }, - }, - }, - }, - expected: true, - }, - { - name: "unsupported envoy service type \"\" ", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyProxyKubernetesProvider{ - EnvoyService: &egcfgv1a1.KubernetesServiceSpec{ - Type: egcfgv1a1.GetKubernetesServiceType(""), - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "unsupported envoy service type 'NodePort'", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyProxyKubernetesProvider{ - EnvoyService: &egcfgv1a1.KubernetesServiceSpec{ - Type: egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceType(corev1.ServiceTypeNodePort)), - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "valid envoy service type 'LoadBalancer'", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyProxyKubernetesProvider{ - EnvoyService: &egcfgv1a1.KubernetesServiceSpec{ - Type: egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceTypeLoadBalancer), - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "valid envoy service type 'ClusterIP'", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyProxyKubernetesProvider{ - EnvoyService: &egcfgv1a1.KubernetesServiceSpec{ - Type: egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceTypeClusterIP), - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "valid user bootstrap", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Bootstrap: &validUserBootstrap, - }, - }, - expected: true, - }, - { - name: "user bootstrap with missing admin address", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Bootstrap: &missingAdminAddressUserBootstrap, - }, - }, - expected: false, - }, - { - name: "user bootstrap with different dynamic resources", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Bootstrap: &differentDynamicResourcesUserBootstrap, - }, - }, - expected: false, - }, - { - name: "user bootstrap with different xds_cluster endpoint", - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Bootstrap: &differentXdsClusterAddressBootstrap, - }, - }, - expected: false, - }, - } - - for i := range testCases { - tc := testCases[i] - t.Run(tc.name, func(t *testing.T) { - err := ValidateEnvoyProxy(tc.proxy) - if tc.expected { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} - -func TestEnvoyGateway(t *testing.T) { - envoyGateway := egcfgv1a1.DefaultEnvoyGateway() - assert.True(t, envoyGateway.Provider != nil) - assert.True(t, envoyGateway.Gateway != nil) - assert.True(t, envoyGateway.Logging != nil) - envoyGateway.SetEnvoyGatewayDefaults() - assert.Equal(t, envoyGateway.Logging, egcfgv1a1.DefaultEnvoyGatewayLogging()) - - logging := egcfgv1a1.DefaultEnvoyGatewayLogging() - assert.True(t, logging != nil) - assert.True(t, logging.Level[egcfgv1a1.LogComponentGatewayDefault] == egcfgv1a1.LogLevelInfo) - - gatewayLogging := &egcfgv1a1.EnvoyGatewayLogging{ - Level: logging.Level, - } - gatewayLogging.SetEnvoyGatewayLoggingDefaults() - assert.True(t, gatewayLogging != nil) - assert.True(t, gatewayLogging.Level[egcfgv1a1.LogComponentGatewayDefault] == egcfgv1a1.LogLevelInfo) -} - -func TestDefaultEnvoyGatewayLoggingLevel(t *testing.T) { - type args struct { - component string - level egcfgv1a1.LogLevel - } - tests := []struct { - name string - args args - want egcfgv1a1.LogLevel - }{ - { - name: "test default info level for empty level", - args: args{component: "", level: ""}, - want: egcfgv1a1.LogLevelInfo, - }, - { - name: "test default info level for empty level", - args: args{component: string(egcfgv1a1.LogComponentGatewayDefault), level: ""}, - want: egcfgv1a1.LogLevelInfo, - }, - { - name: "test default info level for info level", - args: args{component: string(egcfgv1a1.LogComponentGatewayDefault), level: egcfgv1a1.LogLevelInfo}, - want: egcfgv1a1.LogLevelInfo, - }, - { - name: "test default error level for error level", - args: args{component: string(egcfgv1a1.LogComponentGatewayDefault), level: egcfgv1a1.LogLevelError}, - want: egcfgv1a1.LogLevelError, - }, - { - name: "test gateway-api error level for error level", - args: args{component: string(egcfgv1a1.LogComponentGatewayAPIRunner), level: egcfgv1a1.LogLevelError}, - want: egcfgv1a1.LogLevelError, - }, - { - name: "test gateway-api info level for info level", - args: args{component: string(egcfgv1a1.LogComponentGatewayAPIRunner), level: egcfgv1a1.LogLevelInfo}, - want: egcfgv1a1.LogLevelInfo, - }, - { - name: "test default gateway-api warn level for info level", - args: args{component: string(egcfgv1a1.LogComponentGatewayAPIRunner), level: ""}, - want: egcfgv1a1.LogLevelInfo, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - logging := &egcfgv1a1.EnvoyGatewayLogging{} - if got := logging.DefaultEnvoyGatewayLoggingLevel(tt.args.level); got != tt.want { - t.Errorf("defaultLevel() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestEnvoyGatewayProvider(t *testing.T) { - envoyGateway := &egcfgv1a1.EnvoyGateway{ - TypeMeta: metav1.TypeMeta{}, - EnvoyGatewaySpec: egcfgv1a1.EnvoyGatewaySpec{Provider: egcfgv1a1.DefaultEnvoyGatewayProvider()}, - } - assert.True(t, envoyGateway.Provider != nil) - - envoyGatewayProvider := envoyGateway.GetEnvoyGatewayProvider() - assert.True(t, envoyGatewayProvider.Kubernetes == nil) - assert.Equal(t, envoyGateway.Provider, envoyGatewayProvider) - - envoyGatewayProvider.Kubernetes = egcfgv1a1.DefaultEnvoyGatewayKubeProvider() - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment, egcfgv1a1.DefaultKubernetesDeployment(egcfgv1a1.DefaultRateLimitImage)) - - envoyGatewayProvider.Kubernetes = &egcfgv1a1.EnvoyGatewayKubernetesProvider{} - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment == nil) - - envoyGatewayProvider.Kubernetes = &egcfgv1a1.EnvoyGatewayKubernetesProvider{ - RateLimitDeployment: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: nil, - Pod: nil, - Container: nil, - }} - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Replicas == nil) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Pod == nil) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container == nil) - envoyGatewayKubeProvider := envoyGatewayProvider.GetEnvoyGatewayKubeProvider() - - envoyGatewayProvider.Kubernetes = &egcfgv1a1.EnvoyGatewayKubernetesProvider{ - RateLimitDeployment: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: nil, - Pod: nil, - Container: &egcfgv1a1.KubernetesContainerSpec{ - Resources: nil, - SecurityContext: nil, - Image: nil, - }, - }} - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Resources == nil) - envoyGatewayProvider.GetEnvoyGatewayKubeProvider() - - assert.True(t, envoyGatewayProvider.Kubernetes != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes, envoyGatewayKubeProvider) - - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment, egcfgv1a1.DefaultKubernetesDeployment(egcfgv1a1.DefaultRateLimitImage)) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Replicas != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Replicas, egcfgv1a1.DefaultKubernetesDeploymentReplicas()) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Pod != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Pod, egcfgv1a1.DefaultKubernetesPod()) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container, egcfgv1a1.DefaultKubernetesContainer(egcfgv1a1.DefaultRateLimitImage)) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Resources != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Resources, egcfgv1a1.DefaultResourceRequirements()) - assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Image != nil) - assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Image, egcfgv1a1.DefaultKubernetesContainerImage(egcfgv1a1.DefaultRateLimitImage)) -} - -func TestEnvoyProxyProvider(t *testing.T) { - envoyProxy := &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: egcfgv1a1.DefaultEnvoyProxyProvider(), - }, - } - assert.True(t, envoyProxy.Spec.Provider != nil) - - envoyProxyProvider := envoyProxy.GetEnvoyProxyProvider() - assert.True(t, envoyProxyProvider.Kubernetes == nil) - assert.True(t, reflect.DeepEqual(envoyProxy.Spec.Provider, envoyProxyProvider)) - - envoyProxyKubeProvider := envoyProxyProvider.GetEnvoyProxyKubeProvider() - - assert.True(t, envoyProxyProvider.Kubernetes != nil) - assert.True(t, reflect.DeepEqual(envoyProxyProvider.Kubernetes, envoyProxyKubeProvider)) - - envoyProxyProvider.GetEnvoyProxyKubeProvider() - - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment != nil) - assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment, egcfgv1a1.DefaultKubernetesDeployment(egcfgv1a1.DefaultEnvoyProxyImage)) - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Replicas != nil) - assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Replicas, egcfgv1a1.DefaultKubernetesDeploymentReplicas()) - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Pod != nil) - assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Pod, egcfgv1a1.DefaultKubernetesPod()) - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container != nil) - assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container, egcfgv1a1.DefaultKubernetesContainer(egcfgv1a1.DefaultEnvoyProxyImage)) - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Resources != nil) - assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Resources, egcfgv1a1.DefaultResourceRequirements()) - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Image != nil) - assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Image, egcfgv1a1.DefaultKubernetesContainerImage(egcfgv1a1.DefaultEnvoyProxyImage)) - - assert.True(t, envoyProxyProvider.Kubernetes.EnvoyService != nil) - assert.True(t, reflect.DeepEqual(envoyProxyProvider.Kubernetes.EnvoyService.Type, egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceTypeLoadBalancer))) -} - -func TestEnvoyGatewayAdmin(t *testing.T) { - // default envoygateway config admin should not be nil - eg := egcfgv1a1.DefaultEnvoyGateway() - assert.True(t, eg.Admin != nil) - - // get default admin config from envoygateway - // values should be set in default - egAdmin := eg.GetEnvoyGatewayAdmin() - assert.True(t, egAdmin != nil) - assert.True(t, egAdmin.Debug == false) - assert.True(t, egAdmin.Address.Port == egcfgv1a1.GatewayAdminPort) - assert.True(t, egAdmin.Address.Host == egcfgv1a1.GatewayAdminHost) - - // override the admin config - // values should be updated - eg.Admin.Debug = true - eg.Admin.Address = nil - assert.True(t, eg.Admin.Debug == true) - assert.True(t, eg.GetEnvoyGatewayAdmin().Address.Port == egcfgv1a1.GatewayAdminPort) - assert.True(t, eg.GetEnvoyGatewayAdmin().Address.Host == egcfgv1a1.GatewayAdminHost) - - // set eg defaults when admin is nil - // the admin should not be nil - eg.Admin = nil - eg.SetEnvoyGatewayDefaults() - assert.True(t, eg.Admin != nil) - assert.True(t, eg.Admin.Debug == false) - assert.True(t, eg.Admin.Address.Port == egcfgv1a1.GatewayAdminPort) - assert.True(t, eg.Admin.Address.Host == egcfgv1a1.GatewayAdminHost) -} diff --git a/api/config/v1alpha1/zz_generated.deepcopy.go b/api/config/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 02b4473cc81..00000000000 --- a/api/config/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,1260 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -// Code generated by controller-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CustomTag) DeepCopyInto(out *CustomTag) { - *out = *in - if in.Literal != nil { - in, out := &in.Literal, &out.Literal - *out = new(LiteralCustomTag) - **out = **in - } - if in.Environment != nil { - in, out := &in.Environment, &out.Environment - *out = new(EnvironmentCustomTag) - (*in).DeepCopyInto(*out) - } - if in.RequestHeader != nil { - in, out := &in.RequestHeader, &out.RequestHeader - *out = new(RequestHeaderCustomTag) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomTag. -func (in *CustomTag) DeepCopy() *CustomTag { - if in == nil { - return nil - } - out := new(CustomTag) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvironmentCustomTag) DeepCopyInto(out *EnvironmentCustomTag) { - *out = *in - if in.DefaultValue != nil { - in, out := &in.DefaultValue, &out.DefaultValue - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentCustomTag. -func (in *EnvironmentCustomTag) DeepCopy() *EnvironmentCustomTag { - if in == nil { - return nil - } - out := new(EnvironmentCustomTag) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGateway) DeepCopyInto(out *EnvoyGateway) { - *out = *in - out.TypeMeta = in.TypeMeta - in.EnvoyGatewaySpec.DeepCopyInto(&out.EnvoyGatewaySpec) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGateway. -func (in *EnvoyGateway) DeepCopy() *EnvoyGateway { - if in == nil { - return nil - } - out := new(EnvoyGateway) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyGateway) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayAdmin) DeepCopyInto(out *EnvoyGatewayAdmin) { - *out = *in - if in.Address != nil { - in, out := &in.Address, &out.Address - *out = new(EnvoyGatewayAdminAddress) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayAdmin. -func (in *EnvoyGatewayAdmin) DeepCopy() *EnvoyGatewayAdmin { - if in == nil { - return nil - } - out := new(EnvoyGatewayAdmin) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayAdminAddress) DeepCopyInto(out *EnvoyGatewayAdminAddress) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayAdminAddress. -func (in *EnvoyGatewayAdminAddress) DeepCopy() *EnvoyGatewayAdminAddress { - if in == nil { - return nil - } - out := new(EnvoyGatewayAdminAddress) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayCustomProvider) DeepCopyInto(out *EnvoyGatewayCustomProvider) { - *out = *in - in.Resource.DeepCopyInto(&out.Resource) - in.Infrastructure.DeepCopyInto(&out.Infrastructure) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayCustomProvider. -func (in *EnvoyGatewayCustomProvider) DeepCopy() *EnvoyGatewayCustomProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayCustomProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayFileResourceProvider) DeepCopyInto(out *EnvoyGatewayFileResourceProvider) { - *out = *in - if in.Paths != nil { - in, out := &in.Paths, &out.Paths - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayFileResourceProvider. -func (in *EnvoyGatewayFileResourceProvider) DeepCopy() *EnvoyGatewayFileResourceProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayFileResourceProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayHostInfrastructureProvider) DeepCopyInto(out *EnvoyGatewayHostInfrastructureProvider) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayHostInfrastructureProvider. -func (in *EnvoyGatewayHostInfrastructureProvider) DeepCopy() *EnvoyGatewayHostInfrastructureProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayHostInfrastructureProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayInfrastructureProvider) DeepCopyInto(out *EnvoyGatewayInfrastructureProvider) { - *out = *in - if in.Host != nil { - in, out := &in.Host, &out.Host - *out = new(EnvoyGatewayHostInfrastructureProvider) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayInfrastructureProvider. -func (in *EnvoyGatewayInfrastructureProvider) DeepCopy() *EnvoyGatewayInfrastructureProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayInfrastructureProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayKubernetesProvider) DeepCopyInto(out *EnvoyGatewayKubernetesProvider) { - *out = *in - if in.RateLimitDeployment != nil { - in, out := &in.RateLimitDeployment, &out.RateLimitDeployment - *out = new(KubernetesDeploymentSpec) - (*in).DeepCopyInto(*out) - } - if in.Watch != nil { - in, out := &in.Watch, &out.Watch - *out = new(KubernetesWatchMode) - (*in).DeepCopyInto(*out) - } - if in.Deploy != nil { - in, out := &in.Deploy, &out.Deploy - *out = new(KubernetesDeployMode) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayKubernetesProvider. -func (in *EnvoyGatewayKubernetesProvider) DeepCopy() *EnvoyGatewayKubernetesProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayKubernetesProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayLogging) DeepCopyInto(out *EnvoyGatewayLogging) { - *out = *in - if in.Level != nil { - in, out := &in.Level, &out.Level - *out = make(map[EnvoyGatewayLogComponent]LogLevel, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayLogging. -func (in *EnvoyGatewayLogging) DeepCopy() *EnvoyGatewayLogging { - if in == nil { - return nil - } - out := new(EnvoyGatewayLogging) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayProvider) DeepCopyInto(out *EnvoyGatewayProvider) { - *out = *in - if in.Kubernetes != nil { - in, out := &in.Kubernetes, &out.Kubernetes - *out = new(EnvoyGatewayKubernetesProvider) - (*in).DeepCopyInto(*out) - } - if in.Custom != nil { - in, out := &in.Custom, &out.Custom - *out = new(EnvoyGatewayCustomProvider) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayProvider. -func (in *EnvoyGatewayProvider) DeepCopy() *EnvoyGatewayProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewayResourceProvider) DeepCopyInto(out *EnvoyGatewayResourceProvider) { - *out = *in - if in.File != nil { - in, out := &in.File, &out.File - *out = new(EnvoyGatewayFileResourceProvider) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayResourceProvider. -func (in *EnvoyGatewayResourceProvider) DeepCopy() *EnvoyGatewayResourceProvider { - if in == nil { - return nil - } - out := new(EnvoyGatewayResourceProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyGatewaySpec) DeepCopyInto(out *EnvoyGatewaySpec) { - *out = *in - if in.Gateway != nil { - in, out := &in.Gateway, &out.Gateway - *out = new(Gateway) - **out = **in - } - if in.Provider != nil { - in, out := &in.Provider, &out.Provider - *out = new(EnvoyGatewayProvider) - (*in).DeepCopyInto(*out) - } - if in.Logging != nil { - in, out := &in.Logging, &out.Logging - *out = new(EnvoyGatewayLogging) - (*in).DeepCopyInto(*out) - } - if in.Admin != nil { - in, out := &in.Admin, &out.Admin - *out = new(EnvoyGatewayAdmin) - (*in).DeepCopyInto(*out) - } - if in.RateLimit != nil { - in, out := &in.RateLimit, &out.RateLimit - *out = new(RateLimit) - (*in).DeepCopyInto(*out) - } - if in.ExtensionManager != nil { - in, out := &in.ExtensionManager, &out.ExtensionManager - *out = new(ExtensionManager) - (*in).DeepCopyInto(*out) - } - if in.ExtensionAPIs != nil { - in, out := &in.ExtensionAPIs, &out.ExtensionAPIs - *out = new(ExtensionAPISettings) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewaySpec. -func (in *EnvoyGatewaySpec) DeepCopy() *EnvoyGatewaySpec { - if in == nil { - return nil - } - out := new(EnvoyGatewaySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyProxy) DeepCopyInto(out *EnvoyProxy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxy. -func (in *EnvoyProxy) DeepCopy() *EnvoyProxy { - if in == nil { - return nil - } - out := new(EnvoyProxy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyProxy) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyProxyKubernetesProvider) DeepCopyInto(out *EnvoyProxyKubernetesProvider) { - *out = *in - if in.EnvoyDeployment != nil { - in, out := &in.EnvoyDeployment, &out.EnvoyDeployment - *out = new(KubernetesDeploymentSpec) - (*in).DeepCopyInto(*out) - } - if in.EnvoyService != nil { - in, out := &in.EnvoyService, &out.EnvoyService - *out = new(KubernetesServiceSpec) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyKubernetesProvider. -func (in *EnvoyProxyKubernetesProvider) DeepCopy() *EnvoyProxyKubernetesProvider { - if in == nil { - return nil - } - out := new(EnvoyProxyKubernetesProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyProxyList) DeepCopyInto(out *EnvoyProxyList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]EnvoyProxy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyList. -func (in *EnvoyProxyList) DeepCopy() *EnvoyProxyList { - if in == nil { - return nil - } - out := new(EnvoyProxyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyProxyList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyProxyProvider) DeepCopyInto(out *EnvoyProxyProvider) { - *out = *in - if in.Kubernetes != nil { - in, out := &in.Kubernetes, &out.Kubernetes - *out = new(EnvoyProxyKubernetesProvider) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyProvider. -func (in *EnvoyProxyProvider) DeepCopy() *EnvoyProxyProvider { - if in == nil { - return nil - } - out := new(EnvoyProxyProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyProxySpec) DeepCopyInto(out *EnvoyProxySpec) { - *out = *in - if in.Provider != nil { - in, out := &in.Provider, &out.Provider - *out = new(EnvoyProxyProvider) - (*in).DeepCopyInto(*out) - } - in.Logging.DeepCopyInto(&out.Logging) - in.Telemetry.DeepCopyInto(&out.Telemetry) - if in.Bootstrap != nil { - in, out := &in.Bootstrap, &out.Bootstrap - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxySpec. -func (in *EnvoyProxySpec) DeepCopy() *EnvoyProxySpec { - if in == nil { - return nil - } - out := new(EnvoyProxySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyProxyStatus) DeepCopyInto(out *EnvoyProxyStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyStatus. -func (in *EnvoyProxyStatus) DeepCopy() *EnvoyProxyStatus { - if in == nil { - return nil - } - out := new(EnvoyProxyStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtensionAPISettings) DeepCopyInto(out *ExtensionAPISettings) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionAPISettings. -func (in *ExtensionAPISettings) DeepCopy() *ExtensionAPISettings { - if in == nil { - return nil - } - out := new(ExtensionAPISettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtensionHooks) DeepCopyInto(out *ExtensionHooks) { - *out = *in - if in.XDSTranslator != nil { - in, out := &in.XDSTranslator, &out.XDSTranslator - *out = new(XDSTranslatorHooks) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionHooks. -func (in *ExtensionHooks) DeepCopy() *ExtensionHooks { - if in == nil { - return nil - } - out := new(ExtensionHooks) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtensionManager) DeepCopyInto(out *ExtensionManager) { - *out = *in - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = make([]GroupVersionKind, len(*in)) - copy(*out, *in) - } - if in.Hooks != nil { - in, out := &in.Hooks, &out.Hooks - *out = new(ExtensionHooks) - (*in).DeepCopyInto(*out) - } - if in.Service != nil { - in, out := &in.Service, &out.Service - *out = new(ExtensionService) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionManager. -func (in *ExtensionManager) DeepCopy() *ExtensionManager { - if in == nil { - return nil - } - out := new(ExtensionManager) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtensionService) DeepCopyInto(out *ExtensionService) { - *out = *in - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(ExtensionTLS) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionService. -func (in *ExtensionService) DeepCopy() *ExtensionService { - if in == nil { - return nil - } - out := new(ExtensionService) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtensionTLS) DeepCopyInto(out *ExtensionTLS) { - *out = *in - in.CertificateRef.DeepCopyInto(&out.CertificateRef) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionTLS. -func (in *ExtensionTLS) DeepCopy() *ExtensionTLS { - if in == nil { - return nil - } - out := new(ExtensionTLS) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FileEnvoyProxyAccessLog) DeepCopyInto(out *FileEnvoyProxyAccessLog) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileEnvoyProxyAccessLog. -func (in *FileEnvoyProxyAccessLog) DeepCopy() *FileEnvoyProxyAccessLog { - if in == nil { - return nil - } - out := new(FileEnvoyProxyAccessLog) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Gateway) DeepCopyInto(out *Gateway) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Gateway. -func (in *Gateway) DeepCopy() *Gateway { - if in == nil { - return nil - } - out := new(Gateway) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GroupVersionKind) DeepCopyInto(out *GroupVersionKind) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupVersionKind. -func (in *GroupVersionKind) DeepCopy() *GroupVersionKind { - if in == nil { - return nil - } - out := new(GroupVersionKind) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesContainerSpec) DeepCopyInto(out *KubernetesContainerSpec) { - *out = *in - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]corev1.EnvVar, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = new(corev1.ResourceRequirements) - (*in).DeepCopyInto(*out) - } - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(corev1.SecurityContext) - (*in).DeepCopyInto(*out) - } - if in.Image != nil { - in, out := &in.Image, &out.Image - *out = new(string) - **out = **in - } - if in.VolumeMounts != nil { - in, out := &in.VolumeMounts, &out.VolumeMounts - *out = make([]corev1.VolumeMount, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesContainerSpec. -func (in *KubernetesContainerSpec) DeepCopy() *KubernetesContainerSpec { - if in == nil { - return nil - } - out := new(KubernetesContainerSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesDeployMode) DeepCopyInto(out *KubernetesDeployMode) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesDeployMode. -func (in *KubernetesDeployMode) DeepCopy() *KubernetesDeployMode { - if in == nil { - return nil - } - out := new(KubernetesDeployMode) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesDeploymentSpec) DeepCopyInto(out *KubernetesDeploymentSpec) { - *out = *in - if in.Replicas != nil { - in, out := &in.Replicas, &out.Replicas - *out = new(int32) - **out = **in - } - if in.Strategy != nil { - in, out := &in.Strategy, &out.Strategy - *out = new(v1.DeploymentStrategy) - (*in).DeepCopyInto(*out) - } - if in.Pod != nil { - in, out := &in.Pod, &out.Pod - *out = new(KubernetesPodSpec) - (*in).DeepCopyInto(*out) - } - if in.Container != nil { - in, out := &in.Container, &out.Container - *out = new(KubernetesContainerSpec) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesDeploymentSpec. -func (in *KubernetesDeploymentSpec) DeepCopy() *KubernetesDeploymentSpec { - if in == nil { - return nil - } - out := new(KubernetesDeploymentSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesPodSpec) DeepCopyInto(out *KubernetesPodSpec) { - *out = *in - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(corev1.PodSecurityContext) - (*in).DeepCopyInto(*out) - } - if in.Affinity != nil { - in, out := &in.Affinity, &out.Affinity - *out = new(corev1.Affinity) - (*in).DeepCopyInto(*out) - } - if in.Tolerations != nil { - in, out := &in.Tolerations, &out.Tolerations - *out = make([]corev1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Volumes != nil { - in, out := &in.Volumes, &out.Volumes - *out = make([]corev1.Volume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesPodSpec. -func (in *KubernetesPodSpec) DeepCopy() *KubernetesPodSpec { - if in == nil { - return nil - } - out := new(KubernetesPodSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesServiceSpec) DeepCopyInto(out *KubernetesServiceSpec) { - *out = *in - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Type != nil { - in, out := &in.Type, &out.Type - *out = new(ServiceType) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesServiceSpec. -func (in *KubernetesServiceSpec) DeepCopy() *KubernetesServiceSpec { - if in == nil { - return nil - } - out := new(KubernetesServiceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesWatchMode) DeepCopyInto(out *KubernetesWatchMode) { - *out = *in - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesWatchMode. -func (in *KubernetesWatchMode) DeepCopy() *KubernetesWatchMode { - if in == nil { - return nil - } - out := new(KubernetesWatchMode) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LiteralCustomTag) DeepCopyInto(out *LiteralCustomTag) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LiteralCustomTag. -func (in *LiteralCustomTag) DeepCopy() *LiteralCustomTag { - if in == nil { - return nil - } - out := new(LiteralCustomTag) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetricSink) DeepCopyInto(out *MetricSink) { - *out = *in - if in.OpenTelemetry != nil { - in, out := &in.OpenTelemetry, &out.OpenTelemetry - *out = new(OpenTelemetrySink) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricSink. -func (in *MetricSink) DeepCopy() *MetricSink { - if in == nil { - return nil - } - out := new(MetricSink) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OpenTelemetryEnvoyProxyAccessLog) DeepCopyInto(out *OpenTelemetryEnvoyProxyAccessLog) { - *out = *in - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryEnvoyProxyAccessLog. -func (in *OpenTelemetryEnvoyProxyAccessLog) DeepCopy() *OpenTelemetryEnvoyProxyAccessLog { - if in == nil { - return nil - } - out := new(OpenTelemetryEnvoyProxyAccessLog) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OpenTelemetrySink) DeepCopyInto(out *OpenTelemetrySink) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetrySink. -func (in *OpenTelemetrySink) DeepCopy() *OpenTelemetrySink { - if in == nil { - return nil - } - out := new(OpenTelemetrySink) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusProvider) DeepCopyInto(out *PrometheusProvider) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusProvider. -func (in *PrometheusProvider) DeepCopy() *PrometheusProvider { - if in == nil { - return nil - } - out := new(PrometheusProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyAccessLog) DeepCopyInto(out *ProxyAccessLog) { - *out = *in - if in.Settings != nil { - in, out := &in.Settings, &out.Settings - *out = make([]ProxyAccessLogSetting, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLog. -func (in *ProxyAccessLog) DeepCopy() *ProxyAccessLog { - if in == nil { - return nil - } - out := new(ProxyAccessLog) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyAccessLogFormat) DeepCopyInto(out *ProxyAccessLogFormat) { - *out = *in - if in.Text != nil { - in, out := &in.Text, &out.Text - *out = new(string) - **out = **in - } - if in.JSON != nil { - in, out := &in.JSON, &out.JSON - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogFormat. -func (in *ProxyAccessLogFormat) DeepCopy() *ProxyAccessLogFormat { - if in == nil { - return nil - } - out := new(ProxyAccessLogFormat) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyAccessLogSetting) DeepCopyInto(out *ProxyAccessLogSetting) { - *out = *in - in.Format.DeepCopyInto(&out.Format) - if in.Sinks != nil { - in, out := &in.Sinks, &out.Sinks - *out = make([]ProxyAccessLogSink, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogSetting. -func (in *ProxyAccessLogSetting) DeepCopy() *ProxyAccessLogSetting { - if in == nil { - return nil - } - out := new(ProxyAccessLogSetting) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyAccessLogSink) DeepCopyInto(out *ProxyAccessLogSink) { - *out = *in - if in.File != nil { - in, out := &in.File, &out.File - *out = new(FileEnvoyProxyAccessLog) - **out = **in - } - if in.OpenTelemetry != nil { - in, out := &in.OpenTelemetry, &out.OpenTelemetry - *out = new(OpenTelemetryEnvoyProxyAccessLog) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogSink. -func (in *ProxyAccessLogSink) DeepCopy() *ProxyAccessLogSink { - if in == nil { - return nil - } - out := new(ProxyAccessLogSink) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyLogging) DeepCopyInto(out *ProxyLogging) { - *out = *in - if in.Level != nil { - in, out := &in.Level, &out.Level - *out = make(map[LogComponent]LogLevel, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyLogging. -func (in *ProxyLogging) DeepCopy() *ProxyLogging { - if in == nil { - return nil - } - out := new(ProxyLogging) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyMetrics) DeepCopyInto(out *ProxyMetrics) { - *out = *in - if in.Prometheus != nil { - in, out := &in.Prometheus, &out.Prometheus - *out = new(PrometheusProvider) - **out = **in - } - if in.Sinks != nil { - in, out := &in.Sinks, &out.Sinks - *out = make([]MetricSink, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyMetrics. -func (in *ProxyMetrics) DeepCopy() *ProxyMetrics { - if in == nil { - return nil - } - out := new(ProxyMetrics) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyTelemetry) DeepCopyInto(out *ProxyTelemetry) { - *out = *in - if in.AccessLog != nil { - in, out := &in.AccessLog, &out.AccessLog - *out = new(ProxyAccessLog) - (*in).DeepCopyInto(*out) - } - if in.Tracing != nil { - in, out := &in.Tracing, &out.Tracing - *out = new(ProxyTracing) - (*in).DeepCopyInto(*out) - } - if in.Metrics != nil { - in, out := &in.Metrics, &out.Metrics - *out = new(ProxyMetrics) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyTelemetry. -func (in *ProxyTelemetry) DeepCopy() *ProxyTelemetry { - if in == nil { - return nil - } - out := new(ProxyTelemetry) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyTracing) DeepCopyInto(out *ProxyTracing) { - *out = *in - if in.SamplingRate != nil { - in, out := &in.SamplingRate, &out.SamplingRate - *out = new(uint32) - **out = **in - } - if in.CustomTags != nil { - in, out := &in.CustomTags, &out.CustomTags - *out = make(map[string]CustomTag, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() - } - } - out.Provider = in.Provider -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyTracing. -func (in *ProxyTracing) DeepCopy() *ProxyTracing { - if in == nil { - return nil - } - out := new(ProxyTracing) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimit) DeepCopyInto(out *RateLimit) { - *out = *in - in.Backend.DeepCopyInto(&out.Backend) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimit. -func (in *RateLimit) DeepCopy() *RateLimit { - if in == nil { - return nil - } - out := new(RateLimit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitDatabaseBackend) DeepCopyInto(out *RateLimitDatabaseBackend) { - *out = *in - if in.Redis != nil { - in, out := &in.Redis, &out.Redis - *out = new(RateLimitRedisSettings) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitDatabaseBackend. -func (in *RateLimitDatabaseBackend) DeepCopy() *RateLimitDatabaseBackend { - if in == nil { - return nil - } - out := new(RateLimitDatabaseBackend) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitRedisSettings) DeepCopyInto(out *RateLimitRedisSettings) { - *out = *in - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(RedisTLSSettings) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRedisSettings. -func (in *RateLimitRedisSettings) DeepCopy() *RateLimitRedisSettings { - if in == nil { - return nil - } - out := new(RateLimitRedisSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RedisTLSSettings) DeepCopyInto(out *RedisTLSSettings) { - *out = *in - if in.CertificateRef != nil { - in, out := &in.CertificateRef, &out.CertificateRef - *out = new(v1beta1.SecretObjectReference) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisTLSSettings. -func (in *RedisTLSSettings) DeepCopy() *RedisTLSSettings { - if in == nil { - return nil - } - out := new(RedisTLSSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RequestHeaderCustomTag) DeepCopyInto(out *RequestHeaderCustomTag) { - *out = *in - if in.DefaultValue != nil { - in, out := &in.DefaultValue, &out.DefaultValue - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestHeaderCustomTag. -func (in *RequestHeaderCustomTag) DeepCopy() *RequestHeaderCustomTag { - if in == nil { - return nil - } - out := new(RequestHeaderCustomTag) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TracingProvider) DeepCopyInto(out *TracingProvider) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TracingProvider. -func (in *TracingProvider) DeepCopy() *TracingProvider { - if in == nil { - return nil - } - out := new(TracingProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *XDSTranslatorHooks) DeepCopyInto(out *XDSTranslatorHooks) { - *out = *in - if in.Pre != nil { - in, out := &in.Pre, &out.Pre - *out = make([]XDSTranslatorHook, len(*in)) - copy(*out, *in) - } - if in.Post != nil { - in, out := &in.Post, &out.Post - *out = make([]XDSTranslatorHook, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XDSTranslatorHooks. -func (in *XDSTranslatorHooks) DeepCopy() *XDSTranslatorHooks { - if in == nil { - return nil - } - out := new(XDSTranslatorHooks) - in.DeepCopyInto(out) - return out -} diff --git a/api/config/v1alpha1/accesslogging_types.go b/api/v1alpha1/accesslogging_types.go similarity index 82% rename from api/config/v1alpha1/accesslogging_types.go rename to api/v1alpha1/accesslogging_types.go index 4d9f16ab97a..483a224fab4 100644 --- a/api/config/v1alpha1/accesslogging_types.go +++ b/api/v1alpha1/accesslogging_types.go @@ -33,14 +33,17 @@ const ( ) // ProxyAccessLogFormat defines the format of accesslog. +// By default accesslogs are written to standard output. // +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'Text' ? has(self.text) : !has(self.text)",message="If AccessLogFormat type is Text, text field needs to be set." +// +kubebuilder:validation:XValidation:rule="self.type == 'JSON' ? has(self.json) : !has(self.json)",message="If AccessLogFormat type is JSON, json field needs to be set." type ProxyAccessLogFormat struct { // Type defines the type of accesslog format. // +kubebuilder:validation:Enum=Text;JSON // +unionDiscriminator Type ProxyAccessLogFormatType `json:"type,omitempty"` // Text defines the text accesslog format, following Envoy accesslog formatting, - // empty value results in proxy's default access log format. // It's required when the format type is "Text". // Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. // The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. @@ -65,9 +68,15 @@ const ( ProxyAccessLogSinkTypeOpenTelemetry ProxyAccessLogSinkType = "OpenTelemetry" ) +// ProxyAccessLogSink defines the sink of accesslog. +// +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'File' ? has(self.file) : !has(self.file)",message="If AccessLogSink type is File, file field needs to be set." +// +kubebuilder:validation:XValidation:rule="self.type == 'OpenTelemetry' ? has(self.openTelemetry) : !has(self.openTelemetry)",message="If AccessLogSink type is OpenTelemetry, openTelemetry field needs to be set." type ProxyAccessLogSink struct { // Type defines the type of accesslog sink. // +kubebuilder:validation:Enum=File;OpenTelemetry + // +unionDiscriminator Type ProxyAccessLogSinkType `json:"type,omitempty"` // File defines the file accesslog sink. // +optional @@ -79,7 +88,7 @@ type ProxyAccessLogSink struct { type FileEnvoyProxyAccessLog struct { // Path defines the file path used to expose envoy access log(e.g. /dev/stdout). - // Empty value disables accesslog. + // +kubebuilder:validation:MinLength=1 Path string `json:"path,omitempty"` } diff --git a/api/v1alpha1/authenticationfilter_types.go b/api/v1alpha1/authenticationfilter_types.go deleted file mode 100644 index 7907ddef2ea..00000000000 --- a/api/v1alpha1/authenticationfilter_types.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - // KindAuthenticationFilter is the name of the AuthenticationFilter kind. - KindAuthenticationFilter = "AuthenticationFilter" -) - -// +kubebuilder:object:root=true -// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` - -type AuthenticationFilter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired state of the AuthenticationFilter type. - Spec AuthenticationFilterSpec `json:"spec"` - - // Note: The status sub-resource has been excluded but may be added in the future. -} - -// ClaimToHeader defines a configuration to convert JWT claims into HTTP headers -type ClaimToHeader struct { - - // Header defines the name of the HTTP request header that the JWT Claim will be saved into. - Header string `json:"header"` - - // Claim is the JWT Claim that should be saved into the header : it can be a nested claim of type - // (eg. "claim.nested.key", "sub"). The nested claim name must use dot "." - // to separate the JSON name path. - Claim string `json:"claim"` -} - -// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. -// +union -type AuthenticationFilterSpec struct { - // Type defines the type of authentication provider to use. Supported provider types - // are "JWT". - // - // +unionDiscriminator - Type AuthenticationFilterType `json:"type"` - - // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple - // jwtProviders are specified, the JWT is considered valid if any of the providers - // successfully validate the JWT. For additional details, see - // https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. - // - // +kubebuilder:validation:MaxItems=4 - // +optional - JwtProviders []JwtAuthenticationFilterProvider `json:"jwtProviders,omitempty"` -} - -// AuthenticationFilterType is a type of authentication provider. -// +kubebuilder:validation:Enum=JWT -type AuthenticationFilterType string - -const ( - // JwtAuthenticationFilterProviderType is a provider that uses JSON Web Token (JWT) - // for authenticating requests.. - JwtAuthenticationFilterProviderType AuthenticationFilterType = "JWT" -) - -// JwtAuthenticationFilterProvider defines the JSON Web Token (JWT) authentication provider type -// and how JWTs should be verified: -type JwtAuthenticationFilterProvider struct { - // Name defines a unique name for the JWT provider. A name can have a variety of forms, - // including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Name string `json:"name"` - - // Issuer is the principal that issued the JWT and takes the form of a URL or email address. - // For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for - // URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, - // the JWT issuer is not checked. - // - // +kubebuilder:validation:MaxLength=253 - // +optional - Issuer string `json:"issuer,omitempty"` - - // Audiences is a list of JWT audiences allowed access. For additional details, see - // https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences - // are not checked. - // - // +kubebuilder:validation:MaxItems=8 - // +optional - Audiences []string `json:"audiences,omitempty"` - - // RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote - // HTTP/HTTPS endpoint. - RemoteJWKS RemoteJWKS `json:"remoteJWKS"` - - // ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers - // For examples, following config: - // The claim must be of type; string, int, double, bool. Array type claims are not supported - // - ClaimToHeaders []ClaimToHeader `json:"claimToHeaders,omitempty"` - // TODO: Add TBD JWT fields based on defined use cases. -} - -// RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote -// HTTP/HTTPS endpoint. -type RemoteJWKS struct { - // URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to - // validate the server certificate. - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - URI string `json:"uri"` - - // TODO: Add TBD remote JWKS fields based on defined use cases. -} - -//+kubebuilder:object:root=true - -// AuthenticationFilterList contains a list of AuthenticationFilter. -type AuthenticationFilterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []AuthenticationFilter `json:"items"` -} - -func init() { - SchemeBuilder.Register(&AuthenticationFilter{}, &AuthenticationFilterList{}) -} diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go new file mode 100644 index 00000000000..33830ef86ae --- /dev/null +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -0,0 +1,91 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +const ( + // KindBackendTrafficPolicy is the name of the BackendTrafficPolicy kind. + KindBackendTrafficPolicy = "BackendTrafficPolicy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=btp +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].reason` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +// +// BackendTrafficPolicy allows the user to configure the behavior of the connection +// between the downstream client and Envoy Proxy listener. +type BackendTrafficPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec defines the desired state of BackendTrafficPolicy. + Spec BackendTrafficPolicySpec `json:"spec"` + + // status defines the current status of BackendTrafficPolicy. + Status BackendTrafficPolicyStatus `json:"status,omitempty"` +} + +// spec defines the desired state of BackendTrafficPolicy. +type BackendTrafficPolicySpec struct { + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" + // +kubebuilder:validation:XValidation:rule="self.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute']", message="this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" + // +kubebuilder:validation:XValidation:rule="!has(self.sectionName)",message="this policy does not yet support the sectionName field" + // + // targetRef is the name of the resource this policy + // is being attached to. + // This Policy and the TargetRef MUST be in the same namespace + // for this Policy to have effect and be applied to the Gateway. + TargetRef gwapiv1a2.PolicyTargetReferenceWithSectionName `json:"targetRef"` + + // RateLimit allows the user to limit the number of incoming requests + // to a predefined value based on attributes within the traffic flow. + // +optional + RateLimit *RateLimitSpec `json:"rateLimit,omitempty"` + + // LoadBalancer policy to apply when routing traffic from the gateway to + // the backend endpoints + // +optional + LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` + + // ProxyProtocol enables the Proxy Protocol when communicating with the backend. + // +optional + ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty"` + + // TcpKeepalive settings associated with the upstream client connection. + // Disabled by default. + // + // +optional + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty"` +} + +// BackendTrafficPolicyStatus defines the state of BackendTrafficPolicy +type BackendTrafficPolicyStatus struct { + // Conditions describe the current conditions of the BackendTrafficPolicy. + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// BackendTrafficPolicyList contains a list of BackendTrafficPolicy resources. +type BackendTrafficPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BackendTrafficPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&BackendTrafficPolicy{}, &BackendTrafficPolicyList{}) +} diff --git a/api/v1alpha1/basic_auth_types.go b/api/v1alpha1/basic_auth_types.go new file mode 100644 index 00000000000..97fa66d5e76 --- /dev/null +++ b/api/v1alpha1/basic_auth_types.go @@ -0,0 +1,27 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + +const BasicAuthUsersSecretKey = ".htpasswd" + +// BasicAuth defines the configuration for the HTTP Basic Authentication. +type BasicAuth struct { + // The Kubernetes secret which contains the username-password pairs in + // htpasswd format, used to verify user credentials in the "Authorization" + // header. + // + // This is an Opaque secret. The username-password pairs should be stored in + // the key ".htpasswd". As the key name indicates, the value needs to be the + // htpasswd format, for example: "user1:{SHA}hashed_user1_password". + // Right now, only SHA hash algorithm is supported. + // Reference to https://httpd.apache.org/docs/2.4/programs/htpasswd.html + // for more details. + // + // Note: The secret must be in the same namespace as the SecurityPolicy. + Users gwapiv1b1.SecretObjectReference `json:"users"` +} diff --git a/api/v1alpha1/clienttrafficpolicy_types.go b/api/v1alpha1/clienttrafficpolicy_types.go new file mode 100644 index 00000000000..946022a12d9 --- /dev/null +++ b/api/v1alpha1/clienttrafficpolicy_types.go @@ -0,0 +1,101 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +const ( + // KindClientTrafficPolicy is the name of the ClientTrafficPolicy kind. + KindClientTrafficPolicy = "ClientTrafficPolicy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=ctp +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].reason` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` + +// ClientTrafficPolicy allows the user to configure the behavior of the connection +// between the downstream client and Envoy Proxy listener. +type ClientTrafficPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of ClientTrafficPolicy. + Spec ClientTrafficPolicySpec `json:"spec"` + + // Status defines the current status of ClientTrafficPolicy. + Status ClientTrafficPolicyStatus `json:"status,omitempty"` +} + +// ClientTrafficPolicySpec defines the desired state of ClientTrafficPolicy. +type ClientTrafficPolicySpec struct { + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" + // +kubebuilder:validation:XValidation:rule="self.kind == 'Gateway'", message="this policy can only have a targetRef.kind of Gateway" + // +kubebuilder:validation:XValidation:rule="!has(self.sectionName)",message="this policy does not yet support the sectionName field" + // + // TargetRef is the name of the Gateway resource this policy + // is being attached to. + // This Policy and the TargetRef MUST be in the same namespace + // for this Policy to have effect and be applied to the Gateway. + // TargetRef + TargetRef gwapiv1a2.PolicyTargetReferenceWithSectionName `json:"targetRef"` + // TcpKeepalive settings associated with the downstream client connection. + // If defined, sets SO_KEEPALIVE on the listener socket to enable TCP Keepalives. + // Disabled by default. + // + // +optional + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty"` + // EnableProxyProtocol interprets the ProxyProtocol header and adds the + // Client Address into the X-Forwarded-For header. + // Note Proxy Protocol must be present when this field is set, else the connection + // is closed. + // + // +optional + EnableProxyProtocol *bool `json:"enableProxyProtocol,omitempty"` +} + +// ClientTrafficPolicyStatus defines the state of ClientTrafficPolicy +type ClientTrafficPolicyStatus struct { + // Conditions describe the current conditions of the ClientTrafficPolicy. + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +const ( + // PolicyConditionOverridden indicates whether the policy has + // completely attached to all the sections within the target or not. + // + // Possible reasons for this condition to be True are: + // + // * "Overridden" + // + PolicyConditionOverridden gwapiv1a2.PolicyConditionType = "Overridden" + + // PolicyReasonOverridden is used with the "Overridden" condition when the policy + // has been overridden by another policy targeting a section within the same target. + PolicyReasonOverridden gwapiv1a2.PolicyConditionReason = "Overridden" +) + +//+kubebuilder:object:root=true + +// ClientTrafficPolicyList contains a list of ClientTrafficPolicy resources. +type ClientTrafficPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClientTrafficPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ClientTrafficPolicy{}, &ClientTrafficPolicyList{}) +} diff --git a/api/v1alpha1/cors_types.go b/api/v1alpha1/cors_types.go new file mode 100644 index 00000000000..2831a372d6d --- /dev/null +++ b/api/v1alpha1/cors_types.go @@ -0,0 +1,27 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// CORS defines the configuration for Cross-Origin Resource Sharing (CORS). +type CORS struct { + // AllowOrigins defines the origins that are allowed to make requests. + // +kubebuilder:validation:MinItems=1 + AllowOrigins []StringMatch `json:"allowOrigins,omitempty" yaml:"allowOrigins"` + // AllowMethods defines the methods that are allowed to make requests. + // +kubebuilder:validation:MinItems=1 + AllowMethods []string `json:"allowMethods,omitempty" yaml:"allowMethods"` + // AllowHeaders defines the headers that are allowed to be sent with requests. + AllowHeaders []string `json:"allowHeaders,omitempty" yaml:"allowHeaders,omitempty"` + // ExposeHeaders defines the headers that can be exposed in the responses. + ExposeHeaders []string `json:"exposeHeaders,omitempty" yaml:"exposeHeaders,omitempty"` + // MaxAge defines how long the results of a preflight request can be cached. + MaxAge *metav1.Duration `json:"maxAge,omitempty" yaml:"maxAge,omitempty"` + // AllowCredentials indicates whether a request can include user credentials + // like cookies, authentication headers, or TLS client certificates. + AllowCredentials *bool `json:"allowCredentials,omitempty" yaml:"allowCredentials,omitempty"` +} diff --git a/api/v1alpha1/doc.go b/api/v1alpha1/doc.go index ba39955fc08..4e705afd0e9 100644 --- a/api/v1alpha1/doc.go +++ b/api/v1alpha1/doc.go @@ -3,7 +3,8 @@ // The full text of the Apache license is available in the LICENSE file at // the root of the repo. -// Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io API group. +// Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io +// API group. // // +kubebuilder:object:generate=true // +groupName=gateway.envoyproxy.io diff --git a/api/v1alpha1/envoygateway_helpers.go b/api/v1alpha1/envoygateway_helpers.go new file mode 100644 index 00000000000..25866ed1203 --- /dev/null +++ b/api/v1alpha1/envoygateway_helpers.go @@ -0,0 +1,229 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// DefaultEnvoyGateway returns a new EnvoyGateway with default configuration parameters. +func DefaultEnvoyGateway() *EnvoyGateway { + return &EnvoyGateway{ + metav1.TypeMeta{ + Kind: KindEnvoyGateway, + APIVersion: GroupVersion.String(), + }, + EnvoyGatewaySpec{ + Gateway: DefaultGateway(), + Provider: DefaultEnvoyGatewayProvider(), + Logging: DefaultEnvoyGatewayLogging(), + Admin: DefaultEnvoyGatewayAdmin(), + Telemetry: DefaultEnvoyGatewayTelemetry(), + }, + } +} + +// SetEnvoyGatewayDefaults sets default EnvoyGateway configuration parameters. +func (e *EnvoyGateway) SetEnvoyGatewayDefaults() { + if e.TypeMeta.Kind == "" { + e.TypeMeta.Kind = KindEnvoyGateway + } + if e.TypeMeta.APIVersion == "" { + e.TypeMeta.APIVersion = GroupVersion.String() + } + if e.Provider == nil { + e.Provider = DefaultEnvoyGatewayProvider() + } + if e.Gateway == nil { + e.Gateway = DefaultGateway() + } + if e.Logging == nil { + e.Logging = DefaultEnvoyGatewayLogging() + } + if e.Admin == nil { + e.Admin = DefaultEnvoyGatewayAdmin() + } + if e.Telemetry == nil { + e.Telemetry = DefaultEnvoyGatewayTelemetry() + } +} + +// GetEnvoyGatewayAdmin returns the EnvoyGatewayAdmin of EnvoyGateway or a default EnvoyGatewayAdmin if unspecified. +func (e *EnvoyGateway) GetEnvoyGatewayAdmin() *EnvoyGatewayAdmin { + if e.Admin != nil { + if e.Admin.Address == nil { + e.Admin.Address = DefaultEnvoyGatewayAdminAddress() + } + return e.Admin + } + e.Admin = DefaultEnvoyGatewayAdmin() + + return e.Admin +} + +// GetEnvoyGatewayAdminAddress returns the EnvoyGateway Admin Address. +func (e *EnvoyGateway) GetEnvoyGatewayAdminAddress() string { + address := e.GetEnvoyGatewayAdmin().Address + if address != nil { + return fmt.Sprintf("%s:%d", address.Host, address.Port) + } + + return "" +} + +// NamespaceMode returns if uses namespace mode. +func (e *EnvoyGateway) NamespaceMode() bool { + return e.Provider != nil && + e.Provider.Kubernetes != nil && + e.Provider.Kubernetes.Watch != nil && + e.Provider.Kubernetes.Watch.Type == KubernetesWatchModeTypeNamespaces && + len(e.Provider.Kubernetes.Watch.Namespaces) > 0 +} + +// DefaultGateway returns a new Gateway with default configuration parameters. +func DefaultGateway() *Gateway { + return &Gateway{ + ControllerName: GatewayControllerName, + } +} + +// DefaultEnvoyGatewayLogging returns a new EnvoyGatewayLogging with default configuration parameters. +func DefaultEnvoyGatewayLogging() *EnvoyGatewayLogging { + return &EnvoyGatewayLogging{ + Level: map[EnvoyGatewayLogComponent]LogLevel{ + LogComponentGatewayDefault: LogLevelInfo, + }, + } +} + +// GetEnvoyGatewayTelemetry returns the EnvoyGatewayTelemetry of EnvoyGateway or a default EnvoyGatewayTelemetry if unspecified. +func (e *EnvoyGateway) GetEnvoyGatewayTelemetry() *EnvoyGatewayTelemetry { + if e.Telemetry != nil { + if e.Telemetry.Metrics.Prometheus == nil { + e.Telemetry.Metrics.Prometheus = DefaultEnvoyGatewayPrometheus() + } + if e.Telemetry.Metrics == nil { + e.Telemetry.Metrics = DefaultEnvoyGatewayMetrics() + } + return e.Telemetry + } + e.Telemetry = DefaultEnvoyGatewayTelemetry() + + return e.Telemetry +} + +// DisablePrometheus returns if disable prometheus. +func (e *EnvoyGateway) DisablePrometheus() bool { + return e.GetEnvoyGatewayTelemetry().Metrics.Prometheus.Disable +} + +// DefaultEnvoyGatewayTelemetry returns a new EnvoyGatewayTelemetry with default configuration parameters. +func DefaultEnvoyGatewayTelemetry() *EnvoyGatewayTelemetry { + return &EnvoyGatewayTelemetry{ + Metrics: DefaultEnvoyGatewayMetrics(), + } +} + +// DefaultEnvoyGatewayMetrics returns a new EnvoyGatewayMetrics with default configuration parameters. +func DefaultEnvoyGatewayMetrics() *EnvoyGatewayMetrics { + return &EnvoyGatewayMetrics{ + Prometheus: DefaultEnvoyGatewayPrometheus(), + } +} + +// DefaultEnvoyGatewayPrometheus returns a new EnvoyGatewayMetrics with default configuration parameters. +func DefaultEnvoyGatewayPrometheus() *EnvoyGatewayPrometheusProvider { + return &EnvoyGatewayPrometheusProvider{ + // Enable prometheus pull by default. + Disable: false, + } +} + +// DefaultEnvoyGatewayProvider returns a new EnvoyGatewayProvider with default configuration parameters. +func DefaultEnvoyGatewayProvider() *EnvoyGatewayProvider { + return &EnvoyGatewayProvider{ + Type: ProviderTypeKubernetes, + } +} + +// GetEnvoyGatewayProvider returns the EnvoyGatewayProvider of EnvoyGateway or a default EnvoyGatewayProvider if unspecified. +func (e *EnvoyGateway) GetEnvoyGatewayProvider() *EnvoyGatewayProvider { + if e.Provider != nil { + return e.Provider + } + e.Provider = DefaultEnvoyGatewayProvider() + + return e.Provider +} + +// DefaultEnvoyGatewayKubeProvider returns a new EnvoyGatewayKubernetesProvider with default settings. +func DefaultEnvoyGatewayKubeProvider() *EnvoyGatewayKubernetesProvider { + return &EnvoyGatewayKubernetesProvider{ + RateLimitDeployment: DefaultKubernetesDeployment(DefaultRateLimitImage), + } +} + +// DefaultEnvoyGatewayAdmin returns a new EnvoyGatewayAdmin with default configuration parameters. +func DefaultEnvoyGatewayAdmin() *EnvoyGatewayAdmin { + return &EnvoyGatewayAdmin{ + Address: DefaultEnvoyGatewayAdminAddress(), + EnableDumpConfig: false, + EnablePprof: false, + } +} + +// DefaultEnvoyGatewayAdminAddress returns a new EnvoyGatewayAdminAddress with default configuration parameters. +func DefaultEnvoyGatewayAdminAddress() *EnvoyGatewayAdminAddress { + return &EnvoyGatewayAdminAddress{ + Port: GatewayAdminPort, + Host: GatewayAdminHost, + } +} + +// GetEnvoyGatewayKubeProvider returns the EnvoyGatewayKubernetesProvider of Provider or +// a default EnvoyGatewayKubernetesProvider if unspecified. If EnvoyGatewayProvider is not of +// type "Kubernetes", a nil EnvoyGatewayKubernetesProvider is returned. +func (r *EnvoyGatewayProvider) GetEnvoyGatewayKubeProvider() *EnvoyGatewayKubernetesProvider { + if r.Type != ProviderTypeKubernetes { + return nil + } + + if r.Kubernetes == nil { + r.Kubernetes = DefaultEnvoyGatewayKubeProvider() + return r.Kubernetes + } + + if r.Kubernetes.RateLimitDeployment == nil { + r.Kubernetes.RateLimitDeployment = DefaultKubernetesDeployment(DefaultRateLimitImage) + } + + r.Kubernetes.RateLimitDeployment.defaultKubernetesDeploymentSpec(DefaultRateLimitImage) + + return r.Kubernetes +} + +// DefaultEnvoyGatewayLoggingLevel returns a new EnvoyGatewayLogging with default configuration parameters. +// When v1alpha1.LogComponentGatewayDefault specified, all other logging components are ignored. +func (logging *EnvoyGatewayLogging) DefaultEnvoyGatewayLoggingLevel(level LogLevel) LogLevel { + if level != "" { + return level + } + + if logging.Level[LogComponentGatewayDefault] != "" { + return logging.Level[LogComponentGatewayDefault] + } + + return LogLevelInfo +} + +// SetEnvoyGatewayLoggingDefaults sets default EnvoyGatewayLogging configuration parameters. +func (logging *EnvoyGatewayLogging) SetEnvoyGatewayLoggingDefaults() { + if logging != nil && logging.Level != nil && logging.Level[LogComponentGatewayDefault] == "" { + logging.Level[LogComponentGatewayDefault] = LogLevelInfo + } +} diff --git a/api/v1alpha1/envoygateway_metrics_types.go b/api/v1alpha1/envoygateway_metrics_types.go new file mode 100644 index 00000000000..0b827a06afa --- /dev/null +++ b/api/v1alpha1/envoygateway_metrics_types.go @@ -0,0 +1,48 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +// EnvoyGatewayMetrics defines control plane push/pull metrics configurations. +type EnvoyGatewayMetrics struct { + // Sinks defines the metric sinks where metrics are sent to. + Sinks []EnvoyGatewayMetricSink `json:"sinks,omitempty"` + // Prometheus defines the configuration for prometheus endpoint. + Prometheus *EnvoyGatewayPrometheusProvider `json:"prometheus,omitempty"` +} + +// EnvoyGatewayMetricSink defines control plane +// metric sinks where metrics are sent to. +type EnvoyGatewayMetricSink struct { + // Type defines the metric sink type. + // EG control plane currently supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + OpenTelemetry *EnvoyGatewayOpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type EnvoyGatewayOpenTelemetrySink struct { + // Host define the sink service hostname. + Host string `json:"host"` + // Protocol define the sink service protocol. + // +kubebuilder:validation:Enum=grpc;http + Protocol string `json:"protocol"` + // Port defines the port the sink service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +// EnvoyGatewayPrometheusProvider will expose prometheus endpoint in pull mode. +type EnvoyGatewayPrometheusProvider struct { + // Disable defines if disables the prometheus metrics in pull mode. + // + Disable bool `json:"disable,omitempty"` +} diff --git a/api/config/v1alpha1/envoygateway_types.go b/api/v1alpha1/envoygateway_types.go similarity index 82% rename from api/config/v1alpha1/envoygateway_types.go rename to api/v1alpha1/envoygateway_types.go index 3f18ad814af..d479d3f7f9e 100644 --- a/api/config/v1alpha1/envoygateway_types.go +++ b/api/v1alpha1/envoygateway_types.go @@ -7,7 +7,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) const ( @@ -19,6 +19,10 @@ const ( GatewayAdminPort = 19000 // GatewayAdminHost is the host of envoy gateway admin server. GatewayAdminHost = "127.0.0.1" + // GatewayMetricsPort is the port which envoy gateway metrics server is listening on. + GatewayMetricsPort = 19001 + // GatewayMetricsHost is the host of envoy gateway metrics server. + GatewayMetricsHost = "0.0.0.0" ) // +kubebuilder:object:root=true @@ -51,6 +55,7 @@ type EnvoyGatewaySpec struct { // +optional // +kubebuilder:default={default: info} Logging *EnvoyGatewayLogging `json:"logging,omitempty"` + // Admin defines the desired admin related abilities. // If unspecified, the Admin is used with default configuration // parameters. @@ -58,6 +63,12 @@ type EnvoyGatewaySpec struct { // +optional Admin *EnvoyGatewayAdmin `json:"admin,omitempty"` + // Telemetry defines the desired control plane telemetry related abilities. + // If unspecified, the telemetry is used with default configuration. + // + // +optional + Telemetry *EnvoyGatewayTelemetry `json:"telemetry,omitempty"` + // RateLimit defines the configuration associated with the Rate Limit service // deployed by Envoy Gateway required to implement the Global Rate limiting // functionality. The specific rate limit service used here is the reference @@ -79,6 +90,13 @@ type EnvoyGatewaySpec struct { ExtensionAPIs *ExtensionAPISettings `json:"extensionApis,omitempty"` } +// EnvoyGatewayTelemetry defines telemetry configurations for envoy gateway control plane. +// Control plane will focus on metrics observability telemetry and tracing telemetry later. +type EnvoyGatewayTelemetry struct { + // Metrics defines metrics configuration for envoy gateway. + Metrics *EnvoyGatewayMetrics `json:"metrics,omitempty"` +} + // EnvoyGatewayLogging defines logging for Envoy Gateway. type EnvoyGatewayLogging struct { // Level is the logging level. If unspecified, defaults to "info". @@ -122,7 +140,7 @@ type Gateway struct { // ControllerName defines the name of the Gateway API controller. If unspecified, // defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following // for additional details: - // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass + // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1.GatewayClass // // +optional ControllerName string `json:"controllerName,omitempty"` @@ -160,7 +178,7 @@ type EnvoyGatewayProvider struct { // EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. type EnvoyGatewayKubernetesProvider struct { // RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. - // If unspecified, default settings for the manged Envoy ratelimit deployment resource + // If unspecified, default settings for the managed Envoy ratelimit deployment resource // are applied. // // +optional @@ -174,18 +192,41 @@ type EnvoyGatewayKubernetesProvider struct { // +optional Deploy *KubernetesDeployMode `json:"deploy,omitempty"` // OverwriteControlPlaneCerts updates the secrets containing the control plane certs, when set. - OverwriteControlPlaneCerts bool `json:"overwrite_control_plane_certs,omitempty"` + // +optional + OverwriteControlPlaneCerts *bool `json:"overwriteControlPlaneCerts,omitempty"` } +const ( + // KubernetesWatchModeTypeNamespaces indicates that the namespace watch mode is used. + KubernetesWatchModeTypeNamespaces = "Namespaces" + + // KubernetesWatchModeTypeNamespaceSelectors indicates that namespaceSelectors watch + // mode is used. + KubernetesWatchModeTypeNamespaceSelectors = "NamespaceSelectors" +) + +// KubernetesWatchModeType defines the type of KubernetesWatchMode +type KubernetesWatchModeType string + // KubernetesWatchMode holds the configuration for which input resources to watch and reconcile. type KubernetesWatchMode struct { + // Type indicates what watch mode to use. KubernetesWatchModeTypeNamespaces and + // KubernetesWatchModeTypeNamespaceSelectors are currently supported + // By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources + // from all namespaces. + Type KubernetesWatchModeType + // Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped // resources such as Gateway, HTTPRoute and Service. // Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as - // GatewayClass that it is linked to. - // By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources - // from all namespaces. + // GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set Namespaces []string + + // NamespaceSelectors holds a list of labels that namespaces have to have in order to be watched. + // Note this doesn't set the informer to watch the namespaces with the given labels. Informer still + // watches all namespaces. But the events for objects whois namespce have no given labels + // will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set + NamespaceSelectors []string `json:"namespaces,omitempty"` } // KubernetesDeployMode holds configuration for how to deploy managed resources such as the Envoy Proxy @@ -273,6 +314,19 @@ type RateLimit struct { // database backend used by the rate limit service to store // state associated with global ratelimiting. Backend RateLimitDatabaseBackend `json:"backend"` + + // Timeout specifies the timeout period for the proxy to access the ratelimit server + // If not set, timeout is 20ms. + // +optional + // +kubebuilder:validation:Format=duration + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // FailClosed is a switch used to control the flow of traffic + // when the response from the ratelimit server cannot be obtained. + // If FailClosed is false, let the traffic pass, + // otherwise, don't let the traffic pass and return 500. + // If not set, FailClosed is False. + FailClosed bool `json:"failClosed"` } // RateLimitDatabaseBackend defines the configuration associated with @@ -305,7 +359,7 @@ type RedisTLSSettings struct { // CertificateRef defines the client certificate reference for TLS connections. // Currently only a Kubernetes Secret of type TLS is supported. // +optional - CertificateRef *gwapiv1b1.SecretObjectReference `json:"certificateRef,omitempty"` + CertificateRef *gwapiv1.SecretObjectReference `json:"certificateRef,omitempty"` } // RateLimitRedisSettings defines the configuration for connecting to redis database. @@ -379,7 +433,7 @@ type ExtensionTLS struct { // CertificateRef can only reference a Kubernetes Secret at this time. // // +kubebuilder:validation:Required - CertificateRef gwapiv1b1.SecretObjectReference `json:"certificateRef"` + CertificateRef gwapiv1.SecretObjectReference `json:"certificateRef"` } // EnvoyGatewayAdmin defines the Envoy Gateway Admin configuration. @@ -389,11 +443,14 @@ type EnvoyGatewayAdmin struct { // // +optional Address *EnvoyGatewayAdminAddress `json:"address,omitempty"` - - // Debug defines if enable the /debug endpoint of Envoy Gateway. + // EnableDumpConfig defines if enable dump config in Envoy Gateway logs. + // + // +optional + EnableDumpConfig bool `json:"enableDumpConfig,omitempty"` + // EnablePprof defines if enable pprof in Envoy Gateway Admin Server. // // +optional - Debug bool `json:"debug,omitempty"` + EnablePprof bool `json:"enablePprof,omitempty"` } // EnvoyGatewayAdminAddress defines the Envoy Gateway Admin Address configuration. diff --git a/api/v1alpha1/envoypatchpolicy_types.go b/api/v1alpha1/envoypatchpolicy_types.go index 012ac42a989..30c7f8652c0 100644 --- a/api/v1alpha1/envoypatchpolicy_types.go +++ b/api/v1alpha1/envoypatchpolicy_types.go @@ -17,6 +17,7 @@ const ( ) // +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=epp // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Programmed")].reason` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` diff --git a/api/v1alpha1/envoyproxy_helpers.go b/api/v1alpha1/envoyproxy_helpers.go new file mode 100644 index 00000000000..cca9121f3f7 --- /dev/null +++ b/api/v1alpha1/envoyproxy_helpers.go @@ -0,0 +1,122 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + "fmt" + "sort" + "strings" + + autoscalingv2 "k8s.io/api/autoscaling/v2" + v1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" +) + +// DefaultEnvoyProxyProvider returns a new EnvoyProxyProvider with default settings. +func DefaultEnvoyProxyProvider() *EnvoyProxyProvider { + return &EnvoyProxyProvider{ + Type: ProviderTypeKubernetes, + } +} + +// GetEnvoyProxyProvider returns the EnvoyProxyProvider of EnvoyProxy or a default EnvoyProxyProvider +// if unspecified. +func (e *EnvoyProxy) GetEnvoyProxyProvider() *EnvoyProxyProvider { + if e.Spec.Provider != nil { + return e.Spec.Provider + } + e.Spec.Provider = DefaultEnvoyProxyProvider() + + return e.Spec.Provider +} + +// DefaultEnvoyProxyKubeProvider returns a new EnvoyProxyKubernetesProvider with default settings. +func DefaultEnvoyProxyKubeProvider() *EnvoyProxyKubernetesProvider { + return &EnvoyProxyKubernetesProvider{ + EnvoyDeployment: DefaultKubernetesDeployment(DefaultEnvoyProxyImage), + EnvoyService: DefaultKubernetesService(), + } +} + +func DefaultEnvoyProxyHpaMetrics() []autoscalingv2.MetricSpec { + return []autoscalingv2.MetricSpec{ + { + Resource: &autoscalingv2.ResourceMetricSource{ + Name: v1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: ptr.To[int32](80), + }, + }, + Type: autoscalingv2.ResourceMetricSourceType, + }, + } +} + +// GetEnvoyProxyKubeProvider returns the EnvoyProxyKubernetesProvider of EnvoyProxyProvider or +// a default EnvoyProxyKubernetesProvider if unspecified. If EnvoyProxyProvider is not of +// type "Kubernetes", a nil EnvoyProxyKubernetesProvider is returned. +func (r *EnvoyProxyProvider) GetEnvoyProxyKubeProvider() *EnvoyProxyKubernetesProvider { + if r.Type != ProviderTypeKubernetes { + return nil + } + + if r.Kubernetes == nil { + r.Kubernetes = DefaultEnvoyProxyKubeProvider() + return r.Kubernetes + } + + if r.Kubernetes.EnvoyDeployment == nil { + r.Kubernetes.EnvoyDeployment = DefaultKubernetesDeployment(DefaultEnvoyProxyImage) + } + + r.Kubernetes.EnvoyDeployment.defaultKubernetesDeploymentSpec(DefaultEnvoyProxyImage) + + if r.Kubernetes.EnvoyService == nil { + r.Kubernetes.EnvoyService = DefaultKubernetesService() + } + + if r.Kubernetes.EnvoyService.Type == nil { + r.Kubernetes.EnvoyService.Type = GetKubernetesServiceType(ServiceTypeLoadBalancer) + } + + if r.Kubernetes.EnvoyHpa != nil { + r.Kubernetes.EnvoyHpa.setDefault() + } + + return r.Kubernetes +} + +// DefaultEnvoyProxyLoggingLevel returns envoy proxy v1alpha1.LogComponentGatewayDefault log level. +// If unspecified, defaults to "warn". When specified, all other logging components are ignored. +func (logging *ProxyLogging) DefaultEnvoyProxyLoggingLevel() LogLevel { + if logging.Level[LogComponentDefault] != "" { + return logging.Level[LogComponentDefault] + } + + return LogLevelWarn +} + +// GetEnvoyProxyComponentLevel returns envoy proxy component log level args. +// xref: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-component-log-level +func (logging *ProxyLogging) GetEnvoyProxyComponentLevel() string { + var args []string + + for component, level := range logging.Level { + if component == LogComponentDefault { + // Skip default component + continue + } + + if level != "" { + args = append(args, fmt.Sprintf("%s:%s", component, level)) + } + } + + sort.Strings(args) + + return strings.Join(args, ",") +} diff --git a/api/v1alpha1/envoyproxy_metric_types.go b/api/v1alpha1/envoyproxy_metric_types.go new file mode 100644 index 00000000000..71fb42ca62b --- /dev/null +++ b/api/v1alpha1/envoyproxy_metric_types.go @@ -0,0 +1,67 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +type MetricSinkType string + +const ( + MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" +) + +type ProxyMetrics struct { + // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. + Prometheus *ProxyPrometheusProvider `json:"prometheus,omitempty"` + // Sinks defines the metric sinks where metrics are sent to. + Sinks []ProxyMetricSink `json:"sinks,omitempty"` + // Matches defines configuration for selecting specific metrics instead of generating all metrics stats + // that are enabled by default. This helps reduce CPU and memory overhead in Envoy, but eliminating some stats + // may after critical functionality. Here are the stats that we strongly recommend not disabling: + // `cluster_manager.warming_clusters`, `cluster..membership_total`,`cluster..membership_healthy`, + // `cluster..membership_degraded`,reference https://github.com/envoyproxy/envoy/issues/9856, + // https://github.com/envoyproxy/envoy/issues/14610 + // + Matches []StringMatch `json:"matches,omitempty"` + + // EnableVirtualHostStats enables envoy stat metrics for virtual hosts. + EnableVirtualHostStats bool `json:"enableVirtualHostStats,omitempty"` +} + +// ProxyMetricSink defines the sink of metrics. +// Default metrics sink is OpenTelemetry. +// +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'OpenTelemetry' ? has(self.openTelemetry) : !has(self.openTelemetry)",message="If MetricSink type is OpenTelemetry, openTelemetry field needs to be set." +type ProxyMetricSink struct { + // Type defines the metric sink type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + // +unionDiscriminator + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + // +optional + OpenTelemetry *ProxyOpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type ProxyOpenTelemetrySink struct { + // Host define the service hostname. + Host string `json:"host"` + // Port defines the port the service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + + // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig +} + +type ProxyPrometheusProvider struct { + // Disable the Prometheus endpoint. + Disable bool `json:"disable,omitempty"` +} diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go new file mode 100644 index 00000000000..acf34417c76 --- /dev/null +++ b/api/v1alpha1/envoyproxy_types.go @@ -0,0 +1,231 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // KindEnvoyProxy is the name of the EnvoyProxy kind. + KindEnvoyProxy = "EnvoyProxy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=eproxy +// +kubebuilder:subresource:status + +// EnvoyProxy is the schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // EnvoyProxySpec defines the desired state of EnvoyProxy. + Spec EnvoyProxySpec `json:"spec,omitempty"` + // EnvoyProxyStatus defines the actual state of EnvoyProxy. + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of EnvoyProxy. +type EnvoyProxySpec struct { + // Provider defines the desired resource provider and provider-specific configuration. + // If unspecified, the "Kubernetes" resource provider is used with default configuration + // parameters. + // + // +optional + Provider *EnvoyProxyProvider `json:"provider,omitempty"` + + // Logging defines logging parameters for managed proxies. + // +kubebuilder:default={level: {default: warn}} + Logging ProxyLogging `json:"logging,omitempty"` + + // Telemetry defines telemetry parameters for managed proxies. + // + // +optional + Telemetry *ProxyTelemetry `json:"telemetry,omitempty"` + + // Bootstrap defines the Envoy Bootstrap as a YAML string. + // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + // to learn more about the syntax. + // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration + // set by Envoy Gateway. + // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources + // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. + // Backward compatibility across minor versions is not guaranteed. + // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default + // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. + // + // +optional + Bootstrap *ProxyBootstrap `json:"bootstrap,omitempty"` + + // Concurrency defines the number of worker threads to run. If unset, it defaults to + // the number of cpuset threads on the platform. + // + // +optional + Concurrency *int32 `json:"concurrency,omitempty"` + + // MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure. + // Setting this field to true would merge all Gateway Listeners under the parent Gateway Class. + // This means that the port, protocol and hostname tuple must be unique for every listener. + // If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. + // + // +optional + MergeGateways *bool `json:"mergeGateways,omitempty"` +} + +type ProxyTelemetry struct { + // AccessLogs defines accesslog parameters for managed proxies. + // If unspecified, will send default format to stdout. + // +optional + AccessLog *ProxyAccessLog `json:"accessLog,omitempty"` + // Tracing defines tracing configuration for managed proxies. + // If unspecified, will not send tracing data. + // +optional + Tracing *ProxyTracing `json:"tracing,omitempty"` + + // Metrics defines metrics configuration for managed proxies. + Metrics *ProxyMetrics `json:"metrics,omitempty"` +} + +// EnvoyProxyProvider defines the desired state of a resource provider. +// +union +type EnvoyProxyProvider struct { + // Type is the type of resource provider to use. A resource provider provides + // infrastructure resources for running the data plane, e.g. Envoy proxy, and + // optional auxiliary control planes. Supported types are "Kubernetes". + // + // +unionDiscriminator + Type ProviderType `json:"type"` + // Kubernetes defines the desired state of the Kubernetes resource provider. + // Kubernetes provides infrastructure resources for running the data plane, + // e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings + // for managed Kubernetes resources are applied. + // + // +optional + Kubernetes *EnvoyProxyKubernetesProvider `json:"kubernetes,omitempty"` +} + +// EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource +// provider. +type EnvoyProxyKubernetesProvider struct { + // EnvoyDeployment defines the desired state of the Envoy deployment resource. + // If unspecified, default settings for the managed Envoy deployment resource + // are applied. + // + // +optional + EnvoyDeployment *KubernetesDeploymentSpec `json:"envoyDeployment,omitempty"` + + // EnvoyService defines the desired state of the Envoy service resource. + // If unspecified, default settings for the managed Envoy service resource + // are applied. + // + // +optional + // +kubebuilder:validation:XValidation:message="allocateLoadBalancerNodePorts can only be set for LoadBalancer type",rule="!has(self.allocateLoadBalancerNodePorts) || self.type == 'LoadBalancer'" + // +kubebuilder:validation:XValidation:message="loadBalancerIP can only be set for LoadBalancer type",rule="!has(self.loadBalancerIP) || self.type == 'LoadBalancer'" + // +kubebuilder:validation:XValidation:message="loadBalancerIP must be a valid IPv4 address",rule="!has(self.loadBalancerIP) || self.loadBalancerIP.matches(r\"^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4})\")" + EnvoyService *KubernetesServiceSpec `json:"envoyService,omitempty"` + + // EnvoyHpa defines the Horizontal Pod Autoscaler settings for Envoy Proxy Deployment. + // Once the HPA is being set, Replicas field from EnvoyDeployment will be ignored. + // + // +optional + // +kubebuilder:validation:XValidation:message="minReplicas must be greater than 0",rule="!has(self.minReplicas) || self.minReplicas > 0" + // +kubebuilder:validation:XValidation:message="maxReplicas must be greater than 0",rule="!has(self.maxReplicas) || self.maxReplicas > 0" + // +kubebuilder:validation:XValidation:message="maxReplicas cannot be less than or equal to minReplicas",rule="!has(self.minReplicas) || self.maxReplicas > self.minReplicas" + EnvoyHpa *KubernetesHorizontalPodAutoscalerSpec `json:"envoyHpa,omitempty"` +} + +// ProxyLogging defines logging parameters for managed proxies. +type ProxyLogging struct { + // Level is a map of logging level per component, where the component is the key + // and the log level is the value. If unspecified, defaults to "default: warn". + // + // +kubebuilder:default={default: warn} + Level map[ProxyLogComponent]LogLevel `json:"level,omitempty"` +} + +// ProxyLogComponent defines a component that supports a configured logging level. +// +kubebuilder:validation:Enum=system;upstream;http;connection;admin;client;filter;main;router;runtime +type ProxyLogComponent string + +const ( + // LogComponentDefault defines the default logging component. + // See more details: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-l + LogComponentDefault ProxyLogComponent = "default" + + // LogComponentUpstream defines the "upstream" logging component. + LogComponentUpstream ProxyLogComponent = "upstream" + + // LogComponentHTTP defines the "http" logging component. + LogComponentHTTP ProxyLogComponent = "http" + + // LogComponentConnection defines the "connection" logging component. + LogComponentConnection ProxyLogComponent = "connection" + + // LogComponentAdmin defines the "admin" logging component. + LogComponentAdmin ProxyLogComponent = "admin" + + // LogComponentClient defines the "client" logging component. + LogComponentClient ProxyLogComponent = "client" + + // LogComponentFilter defines the "filter" logging component. + LogComponentFilter ProxyLogComponent = "filter" + + // LogComponentMain defines the "main" logging component. + LogComponentMain ProxyLogComponent = "main" + + // LogComponentRouter defines the "router" logging component. + LogComponentRouter ProxyLogComponent = "router" + + // LogComponentRuntime defines the "runtime" logging component. + LogComponentRuntime ProxyLogComponent = "runtime" +) + +// ProxyBootstrap defines Envoy Bootstrap configuration. +type ProxyBootstrap struct { + // Type is the type of the bootstrap configuration, it should be either Replace or Merge. + // If unspecified, it defaults to Replace. + // +optional + // +kubebuilder:default=Replace + Type *BootstrapType `json:"type"` + + // Value is a YAML string of the bootstrap. + Value string `json:"value"` +} + +// BootstrapType defines the types of bootstrap supported by Envoy Gateway. +// +kubebuilder:validation:Enum=Merge;Replace +type BootstrapType string + +const ( + // Merge merges the provided bootstrap with the default one. The provided bootstrap can add or override a value + // within a map, or add a new value to a list. + // Please note that the provided bootstrap can't override a value within a list. + BootstrapTypeMerge BootstrapType = "Merge" + + // Replace replaces the default bootstrap with the provided one. + BootstrapTypeReplace BootstrapType = "Replace" +) + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. This type is not implemented +// until https://github.com/envoyproxy/gateway/issues/1007 is fixed. +type EnvoyProxyStatus struct { + // INSERT ADDITIONAL STATUS FIELDS - define observed state of cluster. + // Important: Run "make" to regenerate code after modifying this file. +} + +// +kubebuilder:object:root=true + +// EnvoyProxyList contains a list of EnvoyProxy +type EnvoyProxyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []EnvoyProxy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&EnvoyProxy{}, &EnvoyProxyList{}) +} diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index 1b13b602e87..be4c68a538d 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -10,9 +10,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/scheme" ) +const GroupName = "gateway.envoyproxy.io" + var ( + // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "gateway.envoyproxy.io", Version: "v1alpha1"} + GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/v1alpha1/jwt_types.go b/api/v1alpha1/jwt_types.go new file mode 100644 index 00000000000..6700d28b854 --- /dev/null +++ b/api/v1alpha1/jwt_types.go @@ -0,0 +1,97 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +// JWT defines the configuration for JSON Web Token (JWT) authentication. +type JWT struct { + + // Providers defines the JSON Web Token (JWT) authentication provider type. + // When multiple JWT providers are specified, the JWT is considered valid if + // any of the providers successfully validate the JWT. For additional details, + // see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. + // + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=4 + Providers []JWTProvider `json:"providers"` +} + +// JWTProvider defines how a JSON Web Token (JWT) can be verified. +type JWTProvider struct { + // Name defines a unique name for the JWT provider. A name can have a variety of forms, + // including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // Issuer is the principal that issued the JWT and takes the form of a URL or email address. + // For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for + // URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, + // the JWT issuer is not checked. + // + // +kubebuilder:validation:MaxLength=253 + // +optional + Issuer string `json:"issuer,omitempty"` + + // Audiences is a list of JWT audiences allowed access. For additional details, see + // https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences + // are not checked. + // + // +kubebuilder:validation:MaxItems=8 + // +optional + Audiences []string `json:"audiences,omitempty"` + + // RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote + // HTTP/HTTPS endpoint. + RemoteJWKS RemoteJWKS `json:"remoteJWKS"` + + // ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers + // For examples, following config: + // The claim must be of type; string, int, double, bool. Array type claims are not supported + // + ClaimToHeaders []ClaimToHeader `json:"claimToHeaders,omitempty"` + + // ExtractFrom defines different ways to extract the JWT token from HTTP request. + // If empty, it defaults to extract JWT token from the Authorization HTTP request header using Bearer schema + // or access_token from query parameters. + // + // +optional + ExtractFrom *JWTExtractor `json:"extractFrom,omitempty"` +} + +// RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote +// HTTP/HTTPS endpoint. +type RemoteJWKS struct { + // URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to + // validate the server certificate. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + URI string `json:"uri"` + + // TODO: Add TBD remote JWKS fields based on defined use cases. +} + +// ClaimToHeader defines a configuration to convert JWT claims into HTTP headers +type ClaimToHeader struct { + + // Header defines the name of the HTTP request header that the JWT Claim will be saved into. + Header string `json:"header"` + + // Claim is the JWT Claim that should be saved into the header : it can be a nested claim of type + // (eg. "claim.nested.key", "sub"). The nested claim name must use dot "." + // to separate the JSON name path. + Claim string `json:"claim"` +} + +// JWTExtractor defines a custom JWT token extraction from HTTP request. +type JWTExtractor struct { + // Cookies represents a list of cookie names to extract the JWT token from. + // If specified, Envoy will extract the JWT token from the listed cookies and validate each of them. + // If any cookie is found to be an invalid JWT, a 401 error will be returned. + // + Cookies []string `json:"cookies,omitempty"` +} diff --git a/api/v1alpha1/keepalive_types.go b/api/v1alpha1/keepalive_types.go new file mode 100644 index 00000000000..ca5731bd471 --- /dev/null +++ b/api/v1alpha1/keepalive_types.go @@ -0,0 +1,32 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// TCPKeepalive define the TCP Keepalive configuration. +type TCPKeepalive struct { + // The total number of unacknowledged probes to send before deciding + // the connection is dead. + // Defaults to 9. + // + // +optional + Probes *uint32 `json:"probes,omitempty"` + // The duration a connection needs to be idle before keep-alive + // probes start being sent. + // The duration format is + // Defaults to `7200s`. + // + // +optional + IdleTime *gwapiv1.Duration `json:"idleTime,omitempty"` + // The duration between keep-alive probes. + // Defaults to `75s`. + // + // +optional + Interval *gwapiv1.Duration `json:"interval,omitempty"` +} diff --git a/api/v1alpha1/kubernetes_helpers.go b/api/v1alpha1/kubernetes_helpers.go new file mode 100644 index 00000000000..30068173642 --- /dev/null +++ b/api/v1alpha1/kubernetes_helpers.go @@ -0,0 +1,114 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + appv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/utils/ptr" +) + +// DefaultKubernetesDeploymentReplicas returns the default replica settings. +func DefaultKubernetesDeploymentReplicas() *int32 { + repl := int32(DefaultDeploymentReplicas) + return &repl +} + +// DefaultKubernetesDeploymentStrategy returns the default deployment strategy settings. +func DefaultKubernetesDeploymentStrategy() *appv1.DeploymentStrategy { + return &appv1.DeploymentStrategy{ + Type: appv1.RollingUpdateDeploymentStrategyType, + } +} + +// DefaultKubernetesContainerImage returns the default envoyproxy image. +func DefaultKubernetesContainerImage(image string) *string { + return ptr.To(image) +} + +// DefaultKubernetesDeployment returns a new KubernetesDeploymentSpec with default settings. +func DefaultKubernetesDeployment(image string) *KubernetesDeploymentSpec { + return &KubernetesDeploymentSpec{ + Replicas: DefaultKubernetesDeploymentReplicas(), + Strategy: DefaultKubernetesDeploymentStrategy(), + Pod: DefaultKubernetesPod(), + Container: DefaultKubernetesContainer(image), + } +} + +// DefaultKubernetesPod returns a new KubernetesPodSpec with default settings. +func DefaultKubernetesPod() *KubernetesPodSpec { + return &KubernetesPodSpec{} +} + +// DefaultKubernetesContainer returns a new KubernetesContainerSpec with default settings. +func DefaultKubernetesContainer(image string) *KubernetesContainerSpec { + return &KubernetesContainerSpec{ + Resources: DefaultResourceRequirements(), + Image: DefaultKubernetesContainerImage(image), + } +} + +// DefaultResourceRequirements returns a new ResourceRequirements with default settings. +func DefaultResourceRequirements() *corev1.ResourceRequirements { + return &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(DefaultDeploymentCPUResourceRequests), + corev1.ResourceMemory: resource.MustParse(DefaultDeploymentMemoryResourceRequests), + }, + } +} + +// DefaultKubernetesService returns a new KubernetesServiceSpec with default settings. +func DefaultKubernetesService() *KubernetesServiceSpec { + return &KubernetesServiceSpec{ + Type: DefaultKubernetesServiceType(), + } +} + +// DefaultKubernetesServiceType returns a new KubernetesServiceType with default settings. +func DefaultKubernetesServiceType() *ServiceType { + return GetKubernetesServiceType(ServiceTypeLoadBalancer) +} + +// GetKubernetesServiceType returns the KubernetesServiceType pointer. +func GetKubernetesServiceType(serviceType ServiceType) *ServiceType { + return &serviceType +} + +// defaultKubernetesDeploymentSpec fill a default KubernetesDeploymentSpec if unspecified. +func (deployment *KubernetesDeploymentSpec) defaultKubernetesDeploymentSpec(image string) { + if deployment.Replicas == nil { + deployment.Replicas = DefaultKubernetesDeploymentReplicas() + } + + if deployment.Strategy == nil { + deployment.Strategy = DefaultKubernetesDeploymentStrategy() + } + + if deployment.Pod == nil { + deployment.Pod = DefaultKubernetesPod() + } + + if deployment.Container == nil { + deployment.Container = DefaultKubernetesContainer(image) + } + + if deployment.Container.Resources == nil { + deployment.Container.Resources = DefaultResourceRequirements() + } + + if deployment.Container.Image == nil { + deployment.Container.Image = DefaultKubernetesContainerImage(image) + } +} + +func (hpa *KubernetesHorizontalPodAutoscalerSpec) setDefault() { + if len(hpa.Metrics) == 0 { + hpa.Metrics = DefaultEnvoyProxyHpaMetrics() + } +} diff --git a/api/v1alpha1/loadbalancer_types.go b/api/v1alpha1/loadbalancer_types.go new file mode 100644 index 00000000000..3bd4d9c4e54 --- /dev/null +++ b/api/v1alpha1/loadbalancer_types.go @@ -0,0 +1,78 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// LoadBalancer defines the load balancer policy to be applied. +// +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'ConsistentHash' ? has(self.consistentHash) : !has(self.consistentHash)",message="If LoadBalancer type is consistentHash, consistentHash field needs to be set." +// +kubebuilder:validation:XValidation:rule="self.type in ['Random', 'ConsistentHash'] ? !has(self.slowStart) : true ",message="Currently SlowStart is only supported for RoundRobin and LeastRequest load balancers." +type LoadBalancer struct { + // Type decides the type of Load Balancer policy. + // Valid LoadBalancerType values are + // "ConsistentHash", + // "LeastRequest", + // "Random", + // "RoundRobin", + // + // +unionDiscriminator + Type LoadBalancerType `json:"type"` + // ConsistentHash defines the configuration when the load balancer type is + // set to ConsistentHash + // + // +optional + ConsistentHash *ConsistentHash `json:"consistentHash,omitempty"` + + // SlowStart defines the configuration related to the slow start load balancer policy. + // If set, during slow start window, traffic sent to the newly added hosts will gradually increase. + // Currently this is only supported for RoundRobin and LeastRequest load balancers + // + // +optional + SlowStart *SlowStart `json:"slowStart,omitempty"` +} + +// LoadBalancerType specifies the types of LoadBalancer. +// +kubebuilder:validation:Enum=ConsistentHash;LeastRequest;Random;RoundRobin +type LoadBalancerType string + +const ( + // ConsistentHashLoadBalancerType load balancer policy. + ConsistentHashLoadBalancerType LoadBalancerType = "ConsistentHash" + // LeastRequestLoadBalancerType load balancer policy. + LeastRequestLoadBalancerType LoadBalancerType = "LeastRequest" + // RandomLoadBalancerType load balancer policy. + RandomLoadBalancerType LoadBalancerType = "Random" + // RoundRobinLoadBalancerType load balancer policy. + RoundRobinLoadBalancerType LoadBalancerType = "RoundRobin" +) + +// ConsistentHash defines the configuration related to the consistent hash +// load balancer policy +type ConsistentHash struct { + Type ConsistentHashType `json:"type"` +} + +// ConsistentHashType defines the type of input to hash on. +// +kubebuilder:validation:Enum=SourceIP +type ConsistentHashType string + +const ( + // SourceIPConsistentHashType hashes based on the source IP address. + SourceIPConsistentHashType ConsistentHashType = "SourceIP" +) + +// SlowStart defines the configuration related to the slow start load balancer policy. +type SlowStart struct { + // Window defines the duration of the warm up period for newly added host. + // During slow start window, traffic sent to the newly added hosts will gradually increase. + // Currently only supports linear growth of traffic. For additional details, + // see https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster-slowstartconfig + // +kubebuilder:validation:Required + Window *metav1.Duration `json:"window"` + // TODO: Add support for non-linear traffic increases based on user usage. +} diff --git a/api/v1alpha1/oidc_types.go b/api/v1alpha1/oidc_types.go new file mode 100644 index 00000000000..f690cfd7c56 --- /dev/null +++ b/api/v1alpha1/oidc_types.go @@ -0,0 +1,74 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const OIDCClientSecretKey = "client-secret" + +// OIDC defines the configuration for the OpenID Connect (OIDC) authentication. +type OIDC struct { + // The OIDC Provider configuration. + Provider OIDCProvider `json:"provider"` + + // The client ID to be used in the OIDC + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // + // +kubebuilder:validation:MinLength=1 + ClientID string `json:"clientID"` + + // The Kubernetes secret which contains the OIDC client secret to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // + // This is an Opaque secret. The client secret should be stored in the key + // "client-secret". + // +kubebuilder:validation:Required + ClientSecret gwapiv1b1.SecretObjectReference `json:"clientSecret"` + + // The OIDC scopes to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // The "openid" scope is always added to the list of scopes if not already + // specified. + // +optional + Scopes []string `json:"scopes,omitempty"` +} + +// OIDCProvider defines the OIDC Provider configuration. +// To make the EG OIDC config easy to use, some of the low-level ouath2 filter +// configuration knobs are hidden from the user, and default values will be provided +// when translating to XDS. For example: +// +// * redirect_uri: uses a default redirect URI "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" +// +// * signout_path: uses a default signout path "/signout" +// +// * redirect_path_matcher: uses a default redirect path matcher "/oauth2/callback" +// +// If we get requests to expose these knobs, we can always do so later. +type OIDCProvider struct { + // The OIDC Provider's [issuer identifier](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery). + // Issuer MUST be a URI RFC 3986 [RFC3986] with a scheme component that MUST + // be https, a host component, and optionally, port and path components and + // no query or fragment components. + // +kubebuilder:validation:MinLength=1 + Issuer string `json:"issuer"` + + // TODO zhaohuabing validate the issuer + + // The OIDC Provider's [authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint). + // If not provided, EG will try to discover it from the provider's [Well-Known Configuration Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). + // + // +optional + AuthorizationEndpoint *string `json:"authorizationEndpoint,omitempty"` + + // The OIDC Provider's [token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint). + // If not provided, EG will try to discover it from the provider's [Well-Known Configuration Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). + // + // +optional + TokenEndpoint *string `json:"tokenEndpoint,omitempty"` +} diff --git a/api/v1alpha1/proxyprotocol_types.go b/api/v1alpha1/proxyprotocol_types.go new file mode 100644 index 00000000000..16d6a900844 --- /dev/null +++ b/api/v1alpha1/proxyprotocol_types.go @@ -0,0 +1,27 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +// ProxyProtocol defines the configuration related to the proxy protocol +// when communicating with the backend. +type ProxyProtocol struct { + // Version of ProxyProtol + // Valid ProxyProtocolVersion values are + // "V1" + // "V2" + Version ProxyProtocolVersion `json:"version"` +} + +// ProxyProtocolVersion defines the version of the Proxy Protocol to use. +// +kubebuilder:validation:Enum=V1;V2 +type ProxyProtocolVersion string + +const ( + // ProxyProtocolVersionV1 is the PROXY protocol version 1 (human readable format). + ProxyProtocolVersionV1 ProxyProtocolVersion = "V1" + // ProxyProtocolVersionV2 is the PROXY protocol version 2 (binary format). + ProxyProtocolVersionV2 ProxyProtocolVersion = "V2" +) diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go new file mode 100644 index 00000000000..e013579efdf --- /dev/null +++ b/api/v1alpha1/ratelimit_types.go @@ -0,0 +1,164 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +// RateLimitSpec defines the desired state of RateLimitSpec. +// +union +type RateLimitSpec struct { + // Type decides the scope for the RateLimits. + // Valid RateLimitType values are "Global". + // + // +unionDiscriminator + Type RateLimitType `json:"type"` + // Global defines global rate limit configuration. + // + // +optional + Global *GlobalRateLimit `json:"global,omitempty"` +} + +// RateLimitType specifies the types of RateLimiting. +// +kubebuilder:validation:Enum=Global +type RateLimitType string + +const ( + // GlobalRateLimitType allows the rate limits to be applied across all Envoy proxy instances. + GlobalRateLimitType RateLimitType = "Global" +) + +// GlobalRateLimit defines global rate limit configuration. +type GlobalRateLimit struct { + // Rules are a list of RateLimit selectors and limits. + // Each rule and its associated limit is applied + // in a mutually exclusive way i.e. if multiple + // rules get selected, each of their associated + // limits get applied, so a single traffic request + // might increase the rate limit counters for multiple + // rules if selected. + // + // +kubebuilder:validation:MaxItems=16 + Rules []RateLimitRule `json:"rules"` +} + +// RateLimitRule defines the semantics for matching attributes +// from the incoming requests, and setting limits for them. +type RateLimitRule struct { + // ClientSelectors holds the list of select conditions to select + // specific clients using attributes from the traffic flow. + // All individual select conditions must hold True for this rule + // and its limit to be applied. + // If this field is empty, it is equivalent to True, and + // the limit is applied. + // + // +optional + // +kubebuilder:validation:MaxItems=8 + ClientSelectors []RateLimitSelectCondition `json:"clientSelectors,omitempty"` + // Limit holds the rate limit values. + // This limit is applied for traffic flows when the selectors + // compute to True, causing the request to be counted towards the limit. + // The limit is enforced and the request is ratelimited, i.e. a response with + // 429 HTTP status code is sent back to the client when + // the selected requests have reached the limit. + Limit RateLimitValue `json:"limit"` +} + +// RateLimitSelectCondition specifies the attributes within the traffic flow that can +// be used to select a subset of clients to be ratelimited. +// All the individual conditions must hold True for the overall condition to hold True. +type RateLimitSelectCondition struct { + // Headers is a list of request headers to match. Multiple header values are ANDed together, + // meaning, a request MUST match all the specified headers. + // + // +listType=map + // +listMapKey=name + // +optional + // +kubebuilder:validation:MaxItems=16 + Headers []HeaderMatch `json:"headers,omitempty"` + + // SourceCIDR is the client IP Address range to match on. + // + // +optional + SourceCIDR *SourceMatch `json:"sourceCIDR,omitempty"` +} + +type SourceMatchType string + +const ( + // SourceMatchExact All IP Addresses within the specified Source IP CIDR are treated as a single client selector + // and share the same rate limit bucket. + SourceMatchExact SourceMatchType = "Exact" + // SourceMatchDistinct Each IP Address within the specified Source IP CIDR is treated as a distinct client selector + // and uses a separate rate limit bucket/counter. + SourceMatchDistinct SourceMatchType = "Distinct" +) + +type SourceMatch struct { + // +optional + // +kubebuilder:default=Exact + Type *SourceMatchType `json:"type,omitempty"` + + // Value is the IP CIDR that represents the range of Source IP Addresses of the client. + // These could also be the intermediate addresses through which the request has flown through and is part of the `X-Forwarded-For` header. + // For example, `192.168.0.1/32`, `192.168.0.0/24`, `001:db8::/64`. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Value string `json:"value"` +} + +// HeaderMatch defines the match attributes within the HTTP Headers of the request. +type HeaderMatch struct { // TODO: zhaohuabing this type could be replaced with a general purpose StringMatch type. + // Type specifies how to match against the value of the header. + // + // +optional + // +kubebuilder:default=Exact + Type *HeaderMatchType `json:"type,omitempty"` + + // Name of the HTTP header. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Name string `json:"name"` + + // Value within the HTTP header. Due to the + // case-insensitivity of header names, "foo" and "Foo" are considered equivalent. + // Do not set this field when Type="Distinct", implying matching on any/all unique + // values within the header. + // + // +optional + // +kubebuilder:validation:MaxLength=1024 + Value *string `json:"value,omitempty"` +} + +// HeaderMatchType specifies the semantics of how HTTP header values should be compared. +// Valid HeaderMatchType values are "Exact", "RegularExpression", and "Distinct". +// +// +kubebuilder:validation:Enum=Exact;RegularExpression;Distinct +type HeaderMatchType string + +// HeaderMatchType constants. +const ( + // HeaderMatchExact matches the exact value of the Value field against the value of + // the specified HTTP Header. + HeaderMatchExact HeaderMatchType = "Exact" + // HeaderMatchRegularExpression matches a regular expression against the value of the + // specified HTTP Header. The regex string must adhere to the syntax documented in + // https://github.com/google/re2/wiki/Syntax. + HeaderMatchRegularExpression HeaderMatchType = "RegularExpression" + // HeaderMatchDistinct matches any and all possible unique values encountered in the + // specified HTTP Header. Note that each unique value will receive its own rate limit + // bucket. + HeaderMatchDistinct HeaderMatchType = "Distinct" +) + +// RateLimitValue defines the limits for rate limiting. +type RateLimitValue struct { + Requests uint `json:"requests"` + Unit RateLimitUnit `json:"unit"` +} + +// RateLimitUnit specifies the intervals for setting rate limits. +// Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". +// +// +kubebuilder:validation:Enum=Second;Minute;Hour;Day +type RateLimitUnit string diff --git a/api/v1alpha1/ratelimitfilter_types.go b/api/v1alpha1/ratelimitfilter_types.go deleted file mode 100644 index 320d934c9da..00000000000 --- a/api/v1alpha1/ratelimitfilter_types.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - // KindRateLimitFilter is the name of the RateLimitFilter kind. - KindRateLimitFilter = "RateLimitFilter" -) - -// +kubebuilder:object:root=true -// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` - -// RateLimitFilter allows the user to limit the number of incoming requests -// to a predefined value based on attributes within the traffic flow. -type RateLimitFilter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired state of RateLimitFilter. - Spec RateLimitFilterSpec `json:"spec"` -} - -// RateLimitFilterSpec defines the desired state of RateLimitFilter. -// +union -type RateLimitFilterSpec struct { - // Type decides the scope for the RateLimits. - // Valid RateLimitType values are "Global". - // - // +unionDiscriminator - Type RateLimitType `json:"type"` - // Global defines global rate limit configuration. - // - // +optional - Global *GlobalRateLimit `json:"global,omitempty"` -} - -// RateLimitType specifies the types of RateLimiting. -// +kubebuilder:validation:Enum=Global -type RateLimitType string - -const ( - // GlobalRateLimitType allows the rate limits to be applied across all Envoy proxy instances. - GlobalRateLimitType RateLimitType = "Global" -) - -// GlobalRateLimit defines global rate limit configuration. -type GlobalRateLimit struct { - // Rules are a list of RateLimit selectors and limits. - // Each rule and its associated limit is applied - // in a mutually exclusive way i.e. if multiple - // rules get selected, each of their associated - // limits get applied, so a single traffic request - // might increase the rate limit counters for multiple - // rules if selected. - // - // +kubebuilder:validation:MaxItems=16 - Rules []RateLimitRule `json:"rules"` -} - -// RateLimitRule defines the semantics for matching attributes -// from the incoming requests, and setting limits for them. -type RateLimitRule struct { - // ClientSelectors holds the list of select conditions to select - // specific clients using attributes from the traffic flow. - // All individual select conditions must hold True for this rule - // and its limit to be applied. - // If this field is empty, it is equivalent to True, and - // the limit is applied. - // - // +optional - // +kubebuilder:validation:MaxItems=8 - ClientSelectors []RateLimitSelectCondition `json:"clientSelectors,omitempty"` - // Limit holds the rate limit values. - // This limit is applied for traffic flows when the selectors - // compute to True, causing the request to be counted towards the limit. - // The limit is enforced and the request is ratelimited, i.e. a response with - // 429 HTTP status code is sent back to the client when - // the selected requests have reached the limit. - Limit RateLimitValue `json:"limit"` -} - -// RateLimitSelectCondition specifies the attributes within the traffic flow that can -// be used to select a subset of clients to be ratelimited. -// All the individual conditions must hold True for the overall condition to hold True. -type RateLimitSelectCondition struct { - // Headers is a list of request headers to match. Multiple header values are ANDed together, - // meaning, a request MUST match all the specified headers. - // - // +listType=map - // +listMapKey=name - // +optional - // +kubebuilder:validation:MaxItems=16 - Headers []HeaderMatch `json:"headers,omitempty"` - - // Deprecated: Use SourceCIDR instead. - // +optional - SourceIP *string `json:"sourceIP,omitempty"` - - // SourceCIDR is the client IP Address range to match on. - // - // +optional - SourceCIDR *SourceMatch `json:"sourceCIDR,omitempty"` -} - -type SourceMatchType string - -const ( - // SourceMatchExact All IP Addresses within the specified Source IP CIDR are treated as a single client selector - // and share the same rate limit bucket. - SourceMatchExact SourceMatchType = "Exact" - // SourceMatchDistinct Each IP Address within the specified Source IP CIDR is treated as a distinct client selector - // and uses a separate rate limit bucket/counter. - SourceMatchDistinct SourceMatchType = "Distinct" -) - -type SourceMatch struct { - // +optional - // +kubebuilder:default=Exact - Type *SourceMatchType `json:"type,omitempty"` - - // Value is the IP CIDR that represents the range of Source IP Addresses of the client. - // These could also be the intermediate addresses through which the request has flown through and is part of the `X-Forwarded-For` header. - // For example, `192.168.0.1/32`, `192.168.0.0/24`, `001:db8::/64`. - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=256 - Value string `json:"value"` -} - -// HeaderMatch defines the match attributes within the HTTP Headers of the request. -type HeaderMatch struct { - // Type specifies how to match against the value of the header. - // - // +optional - // +kubebuilder:default=Exact - Type *HeaderMatchType `json:"type,omitempty"` - - // Name of the HTTP header. - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=256 - Name string `json:"name"` - - // Value within the HTTP header. Due to the - // case-insensitivity of header names, "foo" and "Foo" are considered equivalent. - // Do not set this field when Type="Distinct", implying matching on any/all unique - // values within the header. - // - // +optional - // +kubebuilder:validation:MaxLength=1024 - Value *string `json:"value,omitempty"` -} - -// HeaderMatchType specifies the semantics of how HTTP header values should be compared. -// Valid HeaderMatchType values are "Exact", "RegularExpression", and "Distinct". -// -// +kubebuilder:validation:Enum=Exact;RegularExpression;Distinct -type HeaderMatchType string - -// HeaderMatchType constants. -const ( - // HeaderMatchExact matches the exact value of the Value field against the value of - // the specified HTTP Header. - HeaderMatchExact HeaderMatchType = "Exact" - // HeaderMatchRegularExpression matches a regular expression against the value of the - // specified HTTP Header. The regex string must adhere to the syntax documented in - // https://github.com/google/re2/wiki/Syntax. - HeaderMatchRegularExpression HeaderMatchType = "RegularExpression" - // HeaderMatchDistinct matches any and all possible unique values encountered in the - // specified HTTP Header. Note that each unique value will receive its own rate limit - // bucket. - HeaderMatchDistinct HeaderMatchType = "Distinct" -) - -// RateLimitValue defines the limits for rate limiting. -type RateLimitValue struct { - Requests uint `json:"requests"` - Unit RateLimitUnit `json:"unit"` -} - -// RateLimitUnit specifies the intervals for setting rate limits. -// Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". -// -// +kubebuilder:validation:Enum=Second;Minute;Hour;Day -type RateLimitUnit string - -//+kubebuilder:object:root=true - -// RateLimitFilterList contains a list of RateLimitFilter resources. -type RateLimitFilterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []RateLimitFilter `json:"items"` -} - -func init() { - SchemeBuilder.Register(&RateLimitFilter{}, &RateLimitFilterList{}) -} diff --git a/api/v1alpha1/securitypolicy_types.go b/api/v1alpha1/securitypolicy_types.go new file mode 100644 index 00000000000..ab6bc313088 --- /dev/null +++ b/api/v1alpha1/securitypolicy_types.go @@ -0,0 +1,93 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +const ( + // KindSecurityPolicy is the name of the SecurityPolicy kind. + KindSecurityPolicy = "SecurityPolicy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=sp +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].reason` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` + +// SecurityPolicy allows the user to configure various security settings for a +// Gateway. +type SecurityPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of SecurityPolicy. + Spec SecurityPolicySpec `json:"spec"` + + // Status defines the current status of SecurityPolicy. + Status SecurityPolicyStatus `json:"status,omitempty"` +} + +// SecurityPolicySpec defines the desired state of SecurityPolicy. +type SecurityPolicySpec struct { + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" + // +kubebuilder:validation:XValidation:rule="self.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute']", message="this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute" + // +kubebuilder:validation:XValidation:rule="!has(self.sectionName)",message="this policy does not yet support the sectionName field" + // + // TargetRef is the name of the Gateway resource this policy + // is being attached to. + // This Policy and the TargetRef MUST be in the same namespace + // for this Policy to have effect and be applied to the Gateway. + // TargetRef + TargetRef gwapiv1a2.PolicyTargetReferenceWithSectionName `json:"targetRef"` + + // CORS defines the configuration for Cross-Origin Resource Sharing (CORS). + // + // +optional + CORS *CORS `json:"cors,omitempty"` + + // BasicAuth defines the configuration for the HTTP Basic Authentication. + // + // +optional + BasicAuth *BasicAuth `json:"basicAuth,omitempty"` + + // JWT defines the configuration for JSON Web Token (JWT) authentication. + // + // +optional + JWT *JWT `json:"jwt,omitempty"` + + // OIDC defines the configuration for the OpenID Connect (OIDC) authentication. + // + // +optional + OIDC *OIDC `json:"oidc,omitempty"` +} + +// SecurityPolicyStatus defines the state of SecurityPolicy +type SecurityPolicyStatus struct { + // Conditions describe the current conditions of the SecurityPolicy. + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +//+kubebuilder:object:root=true + +// SecurityPolicyList contains a list of SecurityPolicy resources. +type SecurityPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SecurityPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SecurityPolicy{}, &SecurityPolicyList{}) +} diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go new file mode 100644 index 00000000000..7fa593ef186 --- /dev/null +++ b/api/v1alpha1/shared_types.go @@ -0,0 +1,313 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + appv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" +) + +const ( + // DefaultDeploymentReplicas is the default number of deployment replicas. + DefaultDeploymentReplicas = 1 + // DefaultDeploymentCPUResourceRequests for deployment cpu resource + DefaultDeploymentCPUResourceRequests = "100m" + // DefaultDeploymentMemoryResourceRequests for deployment memory resource + DefaultDeploymentMemoryResourceRequests = "512Mi" + // DefaultEnvoyProxyImage is the default image used by envoyproxy + DefaultEnvoyProxyImage = "envoyproxy/envoy-dev:latest" + // DefaultRateLimitImage is the default image used by ratelimit. + DefaultRateLimitImage = "envoyproxy/ratelimit:master" + // HTTPProtocol is the common-used http protocol. + HTTPProtocol = "http" + // GRPCProtocol is the common-used grpc protocol. + GRPCProtocol = "grpc" +) + +// GroupVersionKind unambiguously identifies a Kind. +// It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind +type GroupVersionKind struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` +} + +// ProviderType defines the types of providers supported by Envoy Gateway. +// +// +kubebuilder:validation:Enum=Kubernetes +type ProviderType string + +const ( + // ProviderTypeKubernetes defines the "Kubernetes" provider. + ProviderTypeKubernetes ProviderType = "Kubernetes" + + // ProviderTypeFile defines the "File" provider. This type is not implemented + // until https://github.com/envoyproxy/gateway/issues/1001 is fixed. + ProviderTypeFile ProviderType = "File" +) + +// KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. +type KubernetesDeploymentSpec struct { + // Replicas is the number of desired pods. Defaults to 1. + // + // +optional + Replicas *int32 `json:"replicas,omitempty"` + + // The deployment strategy to use to replace existing pods with new ones. + // +optional + Strategy *appv1.DeploymentStrategy `json:"strategy,omitempty"` + + // Pod defines the desired specification of pod. + // + // +optional + Pod *KubernetesPodSpec `json:"pod,omitempty"` + + // Container defines the desired specification of main container. + // + // +optional + Container *KubernetesContainerSpec `json:"container,omitempty"` + + // List of initialization containers belonging to the pod. + // More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + // + // +optional + InitContainers []corev1.Container `json:"initContainers,omitempty"` + + // TODO: Expose config as use cases are better understood, e.g. labels. +} + +// KubernetesPodSpec defines the desired state of the Kubernetes pod resource. +type KubernetesPodSpec struct { + // Annotations are the annotations that should be appended to the pods. + // By default, no pod annotations are appended. + // + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + + // Labels are the additional labels that should be tagged to the pods. + // By default, no additional pod labels are tagged. + // + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // SecurityContext holds pod-level security attributes and common container settings. + // Optional: Defaults to empty. See type description for default values of each field. + // + // +optional + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + + // If specified, the pod's scheduling constraints. + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` + + // If specified, the pod's tolerations. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + + // Volumes that can be mounted by containers belonging to the pod. + // More info: https://kubernetes.io/docs/concepts/storage/volumes + // + // +optional + Volumes []corev1.Volume `json:"volumes,omitempty"` + + // HostNetwork, If this is set to true, the pod will use host's network namespace. + // +optional + HostNetwork bool `json:"hostNetwork,omitempty"` +} + +// KubernetesContainerSpec defines the desired state of the Kubernetes container resource. +type KubernetesContainerSpec struct { + // List of environment variables to set in the container. + // + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources required by this container. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // + // +optional + Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + + // SecurityContext defines the security options the container should be run with. + // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + // More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + // + // +optional + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + + // Image specifies the EnvoyProxy container image to be used, instead of the default image. + // + // +optional + Image *string `json:"image,omitempty"` + + // VolumeMounts are volumes to mount into the container's filesystem. + // Cannot be updated. + // + // +optional + VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` +} + +// ServiceType string describes ingress methods for a service +// +enum +// +kubebuilder:validation:Enum=ClusterIP;LoadBalancer;NodePort +type ServiceType string + +const ( + // ServiceTypeClusterIP means a service will only be accessible inside the + // cluster, via the cluster IP. + ServiceTypeClusterIP ServiceType = "ClusterIP" + + // ServiceTypeLoadBalancer means a service will be exposed via an + // external load balancer (if the cloud provider supports it). + ServiceTypeLoadBalancer ServiceType = "LoadBalancer" + + // ServiceTypeNodePort means a service will be exposed on each Kubernetes Node + // at a static Port, common across all Nodes. + ServiceTypeNodePort ServiceType = "NodePort" +) + +// KubernetesServiceSpec defines the desired state of the Kubernetes service resource. +type KubernetesServiceSpec struct { + // Annotations that should be appended to the service. + // By default, no annotations are appended. + // + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + + // Type determines how the Service is exposed. Defaults to LoadBalancer. + // Valid options are ClusterIP, LoadBalancer and NodePort. + // "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). + // "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. + // "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. + // +kubebuilder:default:="LoadBalancer" + // +optional + Type *ServiceType `json:"type,omitempty"` + + // LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider + // implementation if more than one are available or is otherwise expected to be specified + // +optional + LoadBalancerClass *string `json:"loadBalancerClass,omitempty"` + + // AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for + // services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster + // load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a + // value), those requests will be respected, regardless of this field. This field may only be set for + // services with type LoadBalancer and will be cleared if the type is changed to any other type. + // +optional + AllocateLoadBalancerNodePorts *bool `json:"allocateLoadBalancerNodePorts,omitempty"` + + // LoadBalancerIP defines the IP Address of the underlying load balancer service. This field + // may be ignored if the load balancer provider does not support this feature. + // This field has been deprecated in Kubernetes, but it is still used for setting the IP Address in some cloud + // providers such as GCP. + // +optional + LoadBalancerIP *string `json:"loadBalancerIP,omitempty"` + + // TODO: Expose config as use cases are better understood, e.g. labels. +} + +// LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. +// +kubebuilder:validation:Enum=debug;info;error;warn +type LogLevel string + +const ( + // LogLevelDebug defines the "debug" logging level. + LogLevelDebug LogLevel = "debug" + + // LogLevelInfo defines the "Info" logging level. + LogLevelInfo LogLevel = "info" + + // LogLevelWarn defines the "Warn" logging level. + LogLevelWarn LogLevel = "warn" + + // LogLevelError defines the "Error" logging level. + LogLevelError LogLevel = "error" +) + +// XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support +// for the xds-translator +// +// +kubebuilder:validation:Enum=VirtualHost;Route;HTTPListener;Translation +type XDSTranslatorHook string + +const ( + XDSVirtualHost XDSTranslatorHook = "VirtualHost" + XDSRoute XDSTranslatorHook = "Route" + XDSHTTPListener XDSTranslatorHook = "HTTPListener" + XDSTranslation XDSTranslatorHook = "Translation" +) + +// StringMatch defines how to match any strings. +// This is a general purpose match condition that can be used by other EG APIs +// that need to match against a string. +type StringMatch struct { + // Type specifies how to match against a string. + // + // +optional + // +kubebuilder:default=Exact + Type *StringMatchType `json:"type,omitempty"` + + // Value specifies the string value that the match must have. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=1024 + Value string `json:"value"` +} + +// StringMatchType specifies the semantics of how a string value should be compared. +// Valid MatchType values are "Exact", "Prefix", "Suffix", "RegularExpression". +// +// +kubebuilder:validation:Enum=Exact;Prefix;Suffix;RegularExpression +type StringMatchType string + +const ( + // StringMatchExact :the input string must match exactly the match value. + StringMatchExact StringMatchType = "Exact" + + // StringMatchPrefix :the input string must start with the match value. + StringMatchPrefix StringMatchType = "Prefix" + + // StringMatchSuffix :the input string must end with the match value. + StringMatchSuffix StringMatchType = "Suffix" + + // StringMatchRegularExpression :The input string must match the regular expression + // specified in the match value. + // The regex string must adhere to the syntax documented in + // https://github.com/google/re2/wiki/Syntax. + StringMatchRegularExpression StringMatchType = "RegularExpression" +) + +// KubernetesHorizontalPodAutoscalerSpec defines Kubernetes Horizontal Pod Autoscaler settings of Envoy Proxy Deployment. +// See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec. +type KubernetesHorizontalPodAutoscalerSpec struct { + // minReplicas is the lower limit for the number of replicas to which the autoscaler + // can scale down. It defaults to 1 replica. + // + // +optional + MinReplicas *int32 `json:"minReplicas,omitempty"` + + // maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. + // It cannot be less that minReplicas. + // + MaxReplicas *int32 `json:"maxReplicas"` + + // metrics contains the specifications for which to use to calculate the + // desired replica count (the maximum replica count across all metrics will + // be used). + // If left empty, it defaults to being based on CPU utilization with average on 80% usage. + // + // +optional + Metrics []autoscalingv2.MetricSpec `json:"metrics,omitempty"` + + // behavior configures the scaling behavior of the target + // in both Up and Down directions (scaleUp and scaleDown fields respectively). + // If not set, the default HPAScalingRules for scale up and scale down are used. + // See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. + // + // +optional + Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"` +} diff --git a/api/config/v1alpha1/tracing_types.go b/api/v1alpha1/tracing_types.go similarity index 100% rename from api/config/v1alpha1/tracing_types.go rename to api/v1alpha1/tracing_types.go diff --git a/api/v1alpha1/validation/authenticationfilter.go b/api/v1alpha1/validation/authenticationfilter.go deleted file mode 100644 index 684d473f12e..00000000000 --- a/api/v1alpha1/validation/authenticationfilter.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package validation - -import ( - "errors" - "fmt" - "net/mail" - "net/url" - - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/validation" - - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" -) - -// ValidateAuthenticationFilter validates the provided filter. The only supported -// ValidateAuthenticationFilter type is "JWT". -func ValidateAuthenticationFilter(filter *egv1a1.AuthenticationFilter) error { - var errs []error - if filter == nil { - return errors.New("filter is nil") - } - if err := validateAuthenticationFilterSpec(&filter.Spec); err != nil { - errs = append(errs, errors.New("filter is nil")) - } - - return utilerrors.NewAggregate(errs) -} - -// validateAuthenticationFilterSpec validates the provided spec. The only supported -// ValidateAuthenticationFilter type is "JWT". -func validateAuthenticationFilterSpec(spec *egv1a1.AuthenticationFilterSpec) error { - var errs []error - - switch { - case spec == nil: - errs = append(errs, errors.New("spec is nil")) - case spec.Type != egv1a1.JwtAuthenticationFilterProviderType: - errs = append(errs, fmt.Errorf("unsupported authenticationfilter type: %v", spec.Type)) - case len(spec.JwtProviders) == 0: - errs = append(errs, fmt.Errorf("at least one provider must be specified for type %v", spec.Type)) - } - - // Return early if any errors exist. - if len(errs) != 0 { - return utilerrors.NewAggregate(errs) - } - - if err := ValidateJwtProviders(spec.JwtProviders); err != nil { - errs = append(errs, err) - } - - return utilerrors.NewAggregate(errs) -} - -// ValidateJwtProviders validates the provided JWT authentication filter providers. -func ValidateJwtProviders(providers []egv1a1.JwtAuthenticationFilterProvider) error { - var errs []error - - var names []string - for _, provider := range providers { - switch { - case len(provider.Name) == 0: - errs = append(errs, errors.New("jwt provider cannot be an empty string")) - case len(provider.Issuer) != 0: - // Issuer can take the format of a URL or an email address. - if _, err := url.ParseRequestURI(provider.Issuer); err != nil { - _, err := mail.ParseAddress(provider.Issuer) - if err != nil { - errs = append(errs, fmt.Errorf("invalid issuer; must be a URL or email address: %v", err)) - } - } - case len(provider.RemoteJWKS.URI) == 0: - errs = append(errs, fmt.Errorf("uri must be set for remote JWKS provider: %s", provider.Name)) - } - if _, err := url.ParseRequestURI(provider.RemoteJWKS.URI); err != nil { - errs = append(errs, fmt.Errorf("invalid remote JWKS URI: %v", err)) - } - - if len(errs) == 0 { - if strErrs := validation.IsQualifiedName(provider.Name); len(strErrs) != 0 { - for _, strErr := range strErrs { - errs = append(errs, errors.New(strErr)) - } - } - // Ensure uniqueness among provider names. - if names == nil { - names = append(names, provider.Name) - } else { - for _, name := range names { - if name == provider.Name { - errs = append(errs, fmt.Errorf("provider name %s must be unique", provider.Name)) - } else { - names = append(names, provider.Name) - } - } - } - } - - for _, claimToHeader := range provider.ClaimToHeaders { - switch { - case len(claimToHeader.Header) == 0: - errs = append(errs, fmt.Errorf("header must be set for claimToHeader provider: %s", claimToHeader.Header)) - case len(claimToHeader.Claim) == 0: - errs = append(errs, fmt.Errorf("claim must be set for claimToHeader provider: %s", claimToHeader.Claim)) - } - } - } - - return utilerrors.NewAggregate(errs) -} diff --git a/api/v1alpha1/validation/authenticationfilter_test.go b/api/v1alpha1/validation/authenticationfilter_test.go deleted file mode 100644 index 2ce3f272d0d..00000000000 --- a/api/v1alpha1/validation/authenticationfilter_test.go +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package validation - -import ( - "testing" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" -) - -func TestValidateAuthenticationFilter(t *testing.T) { - testCases := []struct { - name string - filter *egv1a1.AuthenticationFilter - expected bool - }{ - { - name: "nil authentication filter", - filter: nil, - expected: false, - }, - { - name: "valid authentication filter with url", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "valid authentication filter with email", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "test@test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "valid authentication filter with jwtClaimToHeader", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "test@test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - ClaimToHeaders: []egv1a1.ClaimToHeader{ - { - Header: "test", - Claim: "test", - }, - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "unqualified authentication provider name", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "unqualified_...", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "unspecified provider name", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "non unique provider names", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "unique", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - { - Name: "non-unique", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - { - Name: "non-unique", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "invalid issuer uri", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "http://invalid url.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "http://www.test.local", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "inivalid issuer email", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "test@!123...", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "invalid remote jwks uri", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "http://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "invalid/local", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "unspecified remote jwks uri", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "", - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "unspecified jwtClaimToHeader headerName", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "test@test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - ClaimToHeaders: []egv1a1.ClaimToHeader{ - { - Header: "", - Claim: "test", - }, - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "unspecified jwtClaimToHeader claimName", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "test@test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - ClaimToHeaders: []egv1a1.ClaimToHeader{ - { - Header: "test", - Claim: "", - }, - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "unspecified issuer", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "unspecified audiences", - filter: &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "https://www.test.local", - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - expected: true, - }, - } - - for i := range testCases { - tc := testCases[i] - t.Run(tc.name, func(t *testing.T) { - err := ValidateAuthenticationFilter(tc.filter) - if tc.expected { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} diff --git a/api/v1alpha1/validation/envoygateway_validate.go b/api/v1alpha1/validation/envoygateway_validate.go new file mode 100644 index 00000000000..657cb9ee73d --- /dev/null +++ b/api/v1alpha1/validation/envoygateway_validate.go @@ -0,0 +1,97 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package validation + +import ( + "errors" + "fmt" + "net/url" + + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/envoyproxy/gateway/api/v1alpha1" +) + +// Validate validates the provided EnvoyGateway. +func ValidateEnvoyGateway(eg *v1alpha1.EnvoyGateway) error { + switch { + case eg == nil: + return errors.New("envoy gateway config is unspecified") + case eg.Gateway == nil: + return errors.New("gateway is unspecified") + case len(eg.Gateway.ControllerName) == 0: + return errors.New("gateway controllerName is unspecified") + case eg.Provider == nil: + return errors.New("provider is unspecified") + case eg.Provider.Type != v1alpha1.ProviderTypeKubernetes: + return fmt.Errorf("unsupported provider %v", eg.Provider.Type) + case eg.Logging != nil && len(eg.Logging.Level) != 0: + level := eg.Logging.Level + for component, logLevel := range level { + switch component { + case v1alpha1.LogComponentGatewayDefault, + v1alpha1.LogComponentProviderRunner, + v1alpha1.LogComponentGatewayAPIRunner, + v1alpha1.LogComponentXdsTranslatorRunner, + v1alpha1.LogComponentXdsServerRunner, + v1alpha1.LogComponentInfrastructureRunner, + v1alpha1.LogComponentGlobalRateLimitRunner: + switch logLevel { + case v1alpha1.LogLevelDebug, v1alpha1.LogLevelError, v1alpha1.LogLevelWarn, v1alpha1.LogLevelInfo: + default: + return errors.New("envoy gateway logging level invalid. valid options: info/debug/warn/error") + } + default: + return errors.New("envoy gateway logging components invalid. valid options: system/provider/gateway-api/xds-translator/xds-server/infrastructure") + } + } + case eg.RateLimit != nil: + if eg.RateLimit.Backend.Type != v1alpha1.RedisBackendType { + return fmt.Errorf("unsupported ratelimit backend %v", eg.RateLimit.Backend.Type) + } + if eg.RateLimit.Backend.Redis == nil || eg.RateLimit.Backend.Redis.URL == "" { + return fmt.Errorf("empty ratelimit redis settings") + } + if _, err := url.Parse(eg.RateLimit.Backend.Redis.URL); err != nil { + return fmt.Errorf("unknown ratelimit redis url format: %w", err) + } + case eg.ExtensionManager != nil: + if eg.ExtensionManager.Hooks == nil || eg.ExtensionManager.Hooks.XDSTranslator == nil { + return fmt.Errorf("registered extension has no hooks specified") + } + + if len(eg.ExtensionManager.Hooks.XDSTranslator.Pre) == 0 && len(eg.ExtensionManager.Hooks.XDSTranslator.Post) == 0 { + return fmt.Errorf("registered extension has no hooks specified") + } + + if eg.ExtensionManager.Service == nil { + return fmt.Errorf("extension service config is empty") + } + + if eg.ExtensionManager.Service.TLS != nil { + certificateRefKind := eg.ExtensionManager.Service.TLS.CertificateRef.Kind + + if certificateRefKind == nil { + return fmt.Errorf("certificateRef empty in extension service server TLS settings") + } + + if *certificateRefKind != gwapiv1.Kind("Secret") { + return fmt.Errorf("unsupported extension server TLS certificateRef %v", certificateRefKind) + } + } + case eg.Telemetry != nil: + if eg.Telemetry.Metrics != nil { + for _, sink := range eg.Telemetry.Metrics.Sinks { + if sink.Type == v1alpha1.MetricSinkTypeOpenTelemetry { + if sink.OpenTelemetry == nil { + return fmt.Errorf("OpenTelemetry is required when sink Type is OpenTelemetry") + } + } + } + } + } + return nil +} diff --git a/api/v1alpha1/validation/envoygateway_validate_test.go b/api/v1alpha1/validation/envoygateway_validate_test.go new file mode 100644 index 00000000000..1728cb8a058 --- /dev/null +++ b/api/v1alpha1/validation/envoygateway_validate_test.go @@ -0,0 +1,700 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package validation + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/envoyproxy/gateway/api/v1alpha1" +) + +var ( + TLSSecretKind = v1.Kind("Secret") + TLSUnrecognizedKind = v1.Kind("Unrecognized") +) + +func TestValidateEnvoyGateway(t *testing.T) { + eg := v1alpha1.DefaultEnvoyGateway() + + testCases := []struct { + name string + eg *v1alpha1.EnvoyGateway + expect bool + }{ + { + name: "default", + eg: eg, + expect: true, + }, + { + name: "unspecified envoy gateway", + eg: nil, + expect: false, + }, + { + name: "unspecified gateway", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + }, + }, + expect: false, + }, + { + name: "unspecified provider", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + }, + }, + expect: false, + }, + { + name: "empty gateway controllerName", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: &v1alpha1.Gateway{ControllerName: ""}, + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + }, + }, + expect: false, + }, + { + name: "unsupported provider", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: &v1alpha1.EnvoyGatewayProvider{Type: v1alpha1.ProviderTypeFile}, + }, + }, + expect: false, + }, + { + name: "empty ratelimit", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + RateLimit: &v1alpha1.RateLimit{}, + }, + }, + expect: false, + }, + { + name: "empty ratelimit redis setting", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + RateLimit: &v1alpha1.RateLimit{ + Backend: v1alpha1.RateLimitDatabaseBackend{ + Type: v1alpha1.RedisBackendType, + Redis: &v1alpha1.RateLimitRedisSettings{}, + }, + }, + }, + }, + expect: false, + }, + { + name: "unknown ratelimit redis url format", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + RateLimit: &v1alpha1.RateLimit{ + Backend: v1alpha1.RateLimitDatabaseBackend{ + Type: v1alpha1.RedisBackendType, + Redis: &v1alpha1.RateLimitRedisSettings{ + URL: ":foo", + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "happy ratelimit redis settings", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + RateLimit: &v1alpha1.RateLimit{ + Backend: v1alpha1.RateLimitDatabaseBackend{ + Type: v1alpha1.RedisBackendType, + Redis: &v1alpha1.RateLimitRedisSettings{ + URL: "localhost:6376", + }, + }, + }, + }, + }, + expect: true, + }, + { + name: "happy extension settings", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + ExtensionManager: &v1alpha1.ExtensionManager{ + Resources: []v1alpha1.GroupVersionKind{ + { + Group: "foo.example.io", + Version: "v1alpha1", + Kind: "Foo", + }, + }, + Hooks: &v1alpha1.ExtensionHooks{ + XDSTranslator: &v1alpha1.XDSTranslatorHooks{ + Pre: []v1alpha1.XDSTranslatorHook{}, + Post: []v1alpha1.XDSTranslatorHook{ + v1alpha1.XDSHTTPListener, + v1alpha1.XDSTranslation, + v1alpha1.XDSRoute, + v1alpha1.XDSVirtualHost, + }, + }, + }, + Service: &v1alpha1.ExtensionService{ + Host: "foo.extension", + Port: 80, + }, + }, + }, + }, + expect: true, + }, + { + name: "happy extension settings tls", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + ExtensionManager: &v1alpha1.ExtensionManager{ + Resources: []v1alpha1.GroupVersionKind{ + { + Group: "foo.example.io", + Version: "v1alpha1", + Kind: "Foo", + }, + }, + Hooks: &v1alpha1.ExtensionHooks{ + XDSTranslator: &v1alpha1.XDSTranslatorHooks{ + Pre: []v1alpha1.XDSTranslatorHook{}, + Post: []v1alpha1.XDSTranslatorHook{ + v1alpha1.XDSHTTPListener, + v1alpha1.XDSTranslation, + v1alpha1.XDSRoute, + v1alpha1.XDSVirtualHost, + }, + }, + }, + Service: &v1alpha1.ExtensionService{ + Host: "foo.extension", + Port: 443, + TLS: &v1alpha1.ExtensionTLS{ + CertificateRef: v1.SecretObjectReference{ + Kind: &TLSSecretKind, + Name: v1.ObjectName("certificate"), + }, + }, + }, + }, + }, + }, + expect: true, + }, + { + name: "happy extension settings no resources", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + ExtensionManager: &v1alpha1.ExtensionManager{ + Hooks: &v1alpha1.ExtensionHooks{ + XDSTranslator: &v1alpha1.XDSTranslatorHooks{ + Pre: []v1alpha1.XDSTranslatorHook{}, + Post: []v1alpha1.XDSTranslatorHook{ + v1alpha1.XDSHTTPListener, + v1alpha1.XDSTranslation, + v1alpha1.XDSRoute, + v1alpha1.XDSVirtualHost, + }, + }, + }, + Service: &v1alpha1.ExtensionService{ + Host: "foo.extension", + Port: 443, + TLS: &v1alpha1.ExtensionTLS{ + CertificateRef: v1.SecretObjectReference{ + Kind: &TLSSecretKind, + Name: v1.ObjectName("certificate"), + }, + }, + }, + }, + }, + }, + expect: true, + }, + { + name: "unknown TLS certificateRef in extension settings", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + ExtensionManager: &v1alpha1.ExtensionManager{ + Resources: []v1alpha1.GroupVersionKind{ + { + Group: "foo.example.io", + Version: "v1alpha1", + Kind: "Foo", + }, + }, + Hooks: &v1alpha1.ExtensionHooks{ + XDSTranslator: &v1alpha1.XDSTranslatorHooks{ + Pre: []v1alpha1.XDSTranslatorHook{}, + Post: []v1alpha1.XDSTranslatorHook{ + v1alpha1.XDSHTTPListener, + v1alpha1.XDSTranslation, + v1alpha1.XDSRoute, + v1alpha1.XDSVirtualHost, + }, + }, + }, + Service: &v1alpha1.ExtensionService{ + Host: "foo.extension", + Port: 8080, + TLS: &v1alpha1.ExtensionTLS{ + CertificateRef: v1.SecretObjectReference{ + Kind: &TLSUnrecognizedKind, + Name: v1.ObjectName("certificate"), + }, + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "empty service in extension settings", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + ExtensionManager: &v1alpha1.ExtensionManager{ + Resources: []v1alpha1.GroupVersionKind{ + { + Group: "foo.example.io", + Version: "v1alpha1", + Kind: "Foo", + }, + }, + Hooks: &v1alpha1.ExtensionHooks{ + XDSTranslator: &v1alpha1.XDSTranslatorHooks{ + Pre: []v1alpha1.XDSTranslatorHook{}, + Post: []v1alpha1.XDSTranslatorHook{ + v1alpha1.XDSHTTPListener, + v1alpha1.XDSTranslation, + v1alpha1.XDSRoute, + v1alpha1.XDSVirtualHost, + }, + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "empty hooks in extension settings", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + ExtensionManager: &v1alpha1.ExtensionManager{ + Resources: []v1alpha1.GroupVersionKind{ + { + Group: "foo.example.io", + Version: "v1alpha1", + Kind: "Foo", + }, + }, + Service: &v1alpha1.ExtensionService{ + Host: "foo.extension", + Port: 8080, + }, + }, + }, + }, + expect: false, + }, + { + name: "valid gateway logging level info", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Logging: &v1alpha1.EnvoyGatewayLogging{ + Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ + v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelInfo, + }, + }, + }, + }, + expect: true, + }, + { + name: "valid gateway logging level warn", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Logging: &v1alpha1.EnvoyGatewayLogging{ + Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ + v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelWarn, + }, + }, + }, + }, + expect: true, + }, + { + name: "valid gateway logging level error", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Logging: &v1alpha1.EnvoyGatewayLogging{ + Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ + v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelError, + }, + }, + }, + }, + expect: true, + }, + { + name: "valid gateway logging level debug", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Logging: &v1alpha1.EnvoyGatewayLogging{ + Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ + v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelDebug, + v1alpha1.LogComponentProviderRunner: v1alpha1.LogLevelDebug, + }, + }, + }, + }, + expect: true, + }, + { + name: "invalid gateway logging level", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Logging: &v1alpha1.EnvoyGatewayLogging{ + Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ + v1alpha1.LogComponentGatewayDefault: "inffo", + }, + }, + }, + }, + expect: false, + }, { + name: "valid gateway metrics sink", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Telemetry: &v1alpha1.EnvoyGatewayTelemetry{ + Metrics: &v1alpha1.EnvoyGatewayMetrics{ + Sinks: []v1alpha1.EnvoyGatewayMetricSink{ + { + Type: v1alpha1.MetricSinkTypeOpenTelemetry, + OpenTelemetry: &v1alpha1.EnvoyGatewayOpenTelemetrySink{ + Host: "x.x.x.x", + Port: 4317, + Protocol: "grpc", + }, + }, + }, + }, + }, + }, + }, + expect: true, + }, { + name: "invalid gateway metrics sink", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Telemetry: &v1alpha1.EnvoyGatewayTelemetry{ + Metrics: &v1alpha1.EnvoyGatewayMetrics{ + Sinks: []v1alpha1.EnvoyGatewayMetricSink{ + { + Type: v1alpha1.MetricSinkTypeOpenTelemetry, + }, + }, + }, + }, + }, + }, + expect: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := ValidateEnvoyGateway(tc.eg) + if !tc.expect { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestEnvoyGateway(t *testing.T) { + envoyGateway := v1alpha1.DefaultEnvoyGateway() + assert.True(t, envoyGateway.Provider != nil) + assert.True(t, envoyGateway.Gateway != nil) + assert.True(t, envoyGateway.Logging != nil) + envoyGateway.SetEnvoyGatewayDefaults() + assert.Equal(t, envoyGateway.Logging, v1alpha1.DefaultEnvoyGatewayLogging()) + + logging := v1alpha1.DefaultEnvoyGatewayLogging() + assert.True(t, logging != nil) + assert.True(t, logging.Level[v1alpha1.LogComponentGatewayDefault] == v1alpha1.LogLevelInfo) + + gatewayLogging := &v1alpha1.EnvoyGatewayLogging{ + Level: logging.Level, + } + gatewayLogging.SetEnvoyGatewayLoggingDefaults() + assert.True(t, gatewayLogging != nil) + assert.True(t, gatewayLogging.Level[v1alpha1.LogComponentGatewayDefault] == v1alpha1.LogLevelInfo) +} + +func TestDefaultEnvoyGatewayLoggingLevel(t *testing.T) { + type args struct { + component string + level v1alpha1.LogLevel + } + tests := []struct { + name string + args args + want v1alpha1.LogLevel + }{ + { + name: "test default info level for empty level", + args: args{component: "", level: ""}, + want: v1alpha1.LogLevelInfo, + }, + { + name: "test default info level for empty level", + args: args{component: string(v1alpha1.LogComponentGatewayDefault), level: ""}, + want: v1alpha1.LogLevelInfo, + }, + { + name: "test default info level for info level", + args: args{component: string(v1alpha1.LogComponentGatewayDefault), level: v1alpha1.LogLevelInfo}, + want: v1alpha1.LogLevelInfo, + }, + { + name: "test default error level for error level", + args: args{component: string(v1alpha1.LogComponentGatewayDefault), level: v1alpha1.LogLevelError}, + want: v1alpha1.LogLevelError, + }, + { + name: "test gateway-api error level for error level", + args: args{component: string(v1alpha1.LogComponentGatewayAPIRunner), level: v1alpha1.LogLevelError}, + want: v1alpha1.LogLevelError, + }, + { + name: "test gateway-api info level for info level", + args: args{component: string(v1alpha1.LogComponentGatewayAPIRunner), level: v1alpha1.LogLevelInfo}, + want: v1alpha1.LogLevelInfo, + }, + { + name: "test default gateway-api warn level for info level", + args: args{component: string(v1alpha1.LogComponentGatewayAPIRunner), level: ""}, + want: v1alpha1.LogLevelInfo, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logging := &v1alpha1.EnvoyGatewayLogging{} + if got := logging.DefaultEnvoyGatewayLoggingLevel(tt.args.level); got != tt.want { + t.Errorf("defaultLevel() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEnvoyGatewayProvider(t *testing.T) { + envoyGateway := &v1alpha1.EnvoyGateway{ + TypeMeta: metav1.TypeMeta{}, + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{Provider: v1alpha1.DefaultEnvoyGatewayProvider()}, + } + assert.True(t, envoyGateway.Provider != nil) + + envoyGatewayProvider := envoyGateway.GetEnvoyGatewayProvider() + assert.True(t, envoyGatewayProvider.Kubernetes == nil) + assert.Equal(t, envoyGateway.Provider, envoyGatewayProvider) + + envoyGatewayProvider.Kubernetes = v1alpha1.DefaultEnvoyGatewayKubeProvider() + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment, v1alpha1.DefaultKubernetesDeployment(v1alpha1.DefaultRateLimitImage)) + + envoyGatewayProvider.Kubernetes = &v1alpha1.EnvoyGatewayKubernetesProvider{} + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment == nil) + + envoyGatewayProvider.Kubernetes = &v1alpha1.EnvoyGatewayKubernetesProvider{ + RateLimitDeployment: &v1alpha1.KubernetesDeploymentSpec{ + Replicas: nil, + Pod: nil, + Container: nil, + }} + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Replicas == nil) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Pod == nil) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container == nil) + envoyGatewayKubeProvider := envoyGatewayProvider.GetEnvoyGatewayKubeProvider() + + envoyGatewayProvider.Kubernetes = &v1alpha1.EnvoyGatewayKubernetesProvider{ + RateLimitDeployment: &v1alpha1.KubernetesDeploymentSpec{ + Replicas: nil, + Pod: nil, + Container: &v1alpha1.KubernetesContainerSpec{ + Resources: nil, + SecurityContext: nil, + Image: nil, + }, + }} + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Resources == nil) + envoyGatewayProvider.GetEnvoyGatewayKubeProvider() + + assert.True(t, envoyGatewayProvider.Kubernetes != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes, envoyGatewayKubeProvider) + + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment, v1alpha1.DefaultKubernetesDeployment(v1alpha1.DefaultRateLimitImage)) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Replicas != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Replicas, v1alpha1.DefaultKubernetesDeploymentReplicas()) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Pod != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Pod, v1alpha1.DefaultKubernetesPod()) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container, v1alpha1.DefaultKubernetesContainer(v1alpha1.DefaultRateLimitImage)) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Resources != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Resources, v1alpha1.DefaultResourceRequirements()) + assert.True(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Image != nil) + assert.Equal(t, envoyGatewayProvider.Kubernetes.RateLimitDeployment.Container.Image, v1alpha1.DefaultKubernetesContainerImage(v1alpha1.DefaultRateLimitImage)) +} + +func TestEnvoyGatewayAdmin(t *testing.T) { + // default envoygateway config admin should not be nil + eg := v1alpha1.DefaultEnvoyGateway() + assert.True(t, eg.Admin != nil) + + // get default admin config from envoygateway + // values should be set in default + egAdmin := eg.GetEnvoyGatewayAdmin() + assert.True(t, egAdmin != nil) + assert.True(t, egAdmin.Address.Port == v1alpha1.GatewayAdminPort) + assert.True(t, egAdmin.Address.Host == v1alpha1.GatewayAdminHost) + assert.True(t, egAdmin.EnableDumpConfig == false) + assert.True(t, egAdmin.EnablePprof == false) + + // override the admin config + // values should be updated + eg.Admin = &v1alpha1.EnvoyGatewayAdmin{ + Address: &v1alpha1.EnvoyGatewayAdminAddress{ + Host: "0.0.0.0", + Port: 19010, + }, + EnableDumpConfig: true, + EnablePprof: true, + } + + assert.True(t, eg.GetEnvoyGatewayAdmin().Address.Port == 19010) + assert.True(t, eg.GetEnvoyGatewayAdmin().Address.Host == "0.0.0.0") + assert.True(t, eg.GetEnvoyGatewayAdmin().EnableDumpConfig == true) + assert.True(t, eg.GetEnvoyGatewayAdmin().EnablePprof == true) + + // set eg defaults when admin is nil + // the admin should not be nil + eg.Admin = nil + eg.SetEnvoyGatewayDefaults() + assert.True(t, eg.Admin != nil) + assert.True(t, eg.Admin.Address.Port == v1alpha1.GatewayAdminPort) + assert.True(t, eg.Admin.Address.Host == v1alpha1.GatewayAdminHost) + assert.True(t, eg.Admin.EnableDumpConfig == false) + assert.True(t, eg.Admin.EnablePprof == false) +} + +func TestEnvoyGatewayTelemetry(t *testing.T) { + // default envoygateway config telemetry should not be nil + eg := v1alpha1.DefaultEnvoyGateway() + assert.True(t, eg.Telemetry != nil) + + // get default telemetry config from envoygateway + // values should be set in default + egTelemetry := eg.GetEnvoyGatewayTelemetry() + assert.True(t, egTelemetry != nil) + assert.True(t, egTelemetry.Metrics != nil) + assert.True(t, egTelemetry.Metrics.Prometheus.Disable == false) + assert.True(t, egTelemetry.Metrics.Sinks == nil) + + // override the telemetry config + // values should be updated + eg.Telemetry.Metrics = &v1alpha1.EnvoyGatewayMetrics{ + Prometheus: &v1alpha1.EnvoyGatewayPrometheusProvider{ + Disable: true, + }, + Sinks: []v1alpha1.EnvoyGatewayMetricSink{ + { + Type: v1alpha1.MetricSinkTypeOpenTelemetry, + OpenTelemetry: &v1alpha1.EnvoyGatewayOpenTelemetrySink{ + Host: "otel-collector.monitoring.svc.cluster.local", + Protocol: "grpc", + Port: 4317, + }, + }, { + Type: v1alpha1.MetricSinkTypeOpenTelemetry, + OpenTelemetry: &v1alpha1.EnvoyGatewayOpenTelemetrySink{ + Host: "otel-collector.monitoring.svc.cluster.local", + Protocol: "http", + Port: 4318, + }, + }, + }, + } + + assert.True(t, eg.GetEnvoyGatewayTelemetry().Metrics.Prometheus.Disable == true) + assert.True(t, len(eg.GetEnvoyGatewayTelemetry().Metrics.Sinks) == 2) + assert.True(t, eg.GetEnvoyGatewayTelemetry().Metrics.Sinks[0].Type == v1alpha1.MetricSinkTypeOpenTelemetry) + + // set eg defaults when telemetry is nil + // the telemetry should not be nil + eg.Telemetry = nil + eg.SetEnvoyGatewayDefaults() + assert.True(t, eg.Telemetry != nil) + assert.True(t, eg.Telemetry.Metrics != nil) + assert.True(t, eg.Telemetry.Metrics.Prometheus.Disable == false) + assert.True(t, eg.Telemetry.Metrics.Sinks == nil) +} diff --git a/api/v1alpha1/validation/envoyproxy_validate.go b/api/v1alpha1/validation/envoyproxy_validate.go new file mode 100644 index 00000000000..6c608f0e0f7 --- /dev/null +++ b/api/v1alpha1/validation/envoyproxy_validate.go @@ -0,0 +1,244 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package validation + +import ( + "errors" + "fmt" + "net" + "reflect" + + bootstrapv3 "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/testing/protocmp" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "sigs.k8s.io/yaml" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/xds/bootstrap" + _ "github.com/envoyproxy/gateway/internal/xds/extensions" // register the generated types to support protojson unmarshalling +) + +// ValidateEnvoyProxy validates the provided EnvoyProxy. +func ValidateEnvoyProxy(proxy *egv1a1.EnvoyProxy) error { + var errs []error + if proxy == nil { + return errors.New("envoyproxy is nil") + } + if err := validateEnvoyProxySpec(&proxy.Spec); err != nil { + errs = append(errs, err) + } + + return utilerrors.NewAggregate(errs) +} + +// validateEnvoyProxySpec validates the provided EnvoyProxy spec. +func validateEnvoyProxySpec(spec *egv1a1.EnvoyProxySpec) error { + var errs []error + + if spec == nil { + errs = append(errs, errors.New("spec is nil")) + } + + // validate provider + validateProviderErrs := validateProvider(spec) + if len(validateProviderErrs) != 0 { + errs = append(errs, validateProviderErrs...) + } + + // validate bootstrap + if spec != nil && spec.Bootstrap != nil { + if err := validateBootstrap(spec.Bootstrap); err != nil { + errs = append(errs, err) + } + } + + validateProxyTelemetryErrs := validateProxyTelemetry(spec) + if len(validateProxyTelemetryErrs) != 0 { + errs = append(errs, validateProxyTelemetryErrs...) + } + + return utilerrors.NewAggregate(errs) +} + +// TODO: remove this function if CEL validation became stable +func validateProvider(spec *egv1a1.EnvoyProxySpec) []error { + var errs []error + if spec != nil && spec.Provider != nil { + if spec.Provider.Type != egv1a1.ProviderTypeKubernetes { + errs = append(errs, fmt.Errorf("unsupported provider type %v", spec.Provider.Type)) + } + validateServiceErrs := validateService(spec) + if len(validateServiceErrs) != 0 { + errs = append(errs, validateServiceErrs...) + } + } + return errs +} + +// TODO: remove this function if CEL validation became stable +func validateService(spec *egv1a1.EnvoyProxySpec) []error { + var errs []error + if spec.Provider.Kubernetes != nil && spec.Provider.Kubernetes.EnvoyService != nil { + if serviceType := spec.Provider.Kubernetes.EnvoyService.Type; serviceType != nil { + if *serviceType != egv1a1.ServiceTypeLoadBalancer && + *serviceType != egv1a1.ServiceTypeClusterIP && + *serviceType != egv1a1.ServiceTypeNodePort { + errs = append(errs, fmt.Errorf("unsupported envoy service type %v", serviceType)) + } + } + if serviceType, serviceAllocateLoadBalancerNodePorts := + spec.Provider.Kubernetes.EnvoyService.Type, spec.Provider.Kubernetes.EnvoyService.AllocateLoadBalancerNodePorts; serviceType != nil && serviceAllocateLoadBalancerNodePorts != nil { + if *serviceType != egv1a1.ServiceTypeLoadBalancer { + errs = append(errs, fmt.Errorf("allocateLoadBalancerNodePorts can only be set for %v type", egv1a1.ServiceTypeLoadBalancer)) + } + } + if serviceType, serviceLoadBalancerIP := spec.Provider.Kubernetes.EnvoyService.Type, spec.Provider.Kubernetes.EnvoyService.LoadBalancerIP; serviceType != nil && serviceLoadBalancerIP != nil { + if *serviceType != egv1a1.ServiceTypeLoadBalancer { + errs = append(errs, fmt.Errorf("loadBalancerIP can only be set for %v type", egv1a1.ServiceTypeLoadBalancer)) + } + + if ip := net.ParseIP(*serviceLoadBalancerIP); ip == nil || ip.To4() == nil { + errs = append(errs, fmt.Errorf("loadBalancerIP:%s is an invalid IPv4 address", *serviceLoadBalancerIP)) + } + } + } + return errs +} + +func validateBootstrap(boostrapConfig *egv1a1.ProxyBootstrap) error { + defaultBootstrap := &bootstrapv3.Bootstrap{} + // TODO: need validate when enable prometheus? + defaultBootstrapStr, err := bootstrap.GetRenderedBootstrapConfig(nil) + if err != nil { + return err + } + + userBootstrapStr, err := bootstrap.ApplyBootstrapConfig(boostrapConfig, defaultBootstrapStr) + if err != nil { + return err + } + + jsonData, err := yaml.YAMLToJSON([]byte(userBootstrapStr)) + if err != nil { + return fmt.Errorf("unable to convert user bootstrap to json: %w", err) + } + + userBootstrap := &bootstrapv3.Bootstrap{} + if err := protojson.Unmarshal(jsonData, userBootstrap); err != nil { + return fmt.Errorf("unable to unmarshal user bootstrap: %w", err) + } + + // Call Validate method + if err := userBootstrap.Validate(); err != nil { + return fmt.Errorf("validation failed for user bootstrap: %w", err) + } + + jsonData, err = yaml.YAMLToJSON([]byte(defaultBootstrapStr)) + if err != nil { + return fmt.Errorf("unable to convert default bootstrap to json: %w", err) + } + + if err := protojson.Unmarshal(jsonData, defaultBootstrap); err != nil { + return fmt.Errorf("unable to unmarshal default bootstrap: %w", err) + } + + // Ensure dynamic resources config is same + if userBootstrap.DynamicResources == nil || + cmp.Diff(userBootstrap.DynamicResources, defaultBootstrap.DynamicResources, protocmp.Transform()) != "" { + return fmt.Errorf("dynamic_resources cannot be modified") + } + + // Ensure that the xds_cluster config is same + var userXdsCluster, defaultXdsCluster *clusterv3.Cluster + for _, cluster := range userBootstrap.StaticResources.Clusters { + if cluster.Name == "xds_cluster" { + userXdsCluster = cluster + break + } + } + for _, cluster := range defaultBootstrap.StaticResources.Clusters { + if cluster.Name == "xds_cluster" { + defaultXdsCluster = cluster + break + } + } + + // nolint // Circumvents this error "Error: copylocks: call of reflect.DeepEqual copies lock value:" + if userXdsCluster == nil || !reflect.DeepEqual(*userXdsCluster.LoadAssignment, *defaultXdsCluster.LoadAssignment) { + return fmt.Errorf("xds_cluster's loadAssigntment cannot be modified") + } + + return nil +} + +func validateProxyTelemetry(spec *egv1a1.EnvoyProxySpec) []error { + var errs []error + + if spec != nil && + spec.Telemetry != nil && + spec.Telemetry.AccessLog != nil { + accessLogErrs := validateProxyAccessLog(spec.Telemetry.AccessLog) + if len(accessLogErrs) > 0 { + errs = append(errs, accessLogErrs...) + } + } + + if spec != nil && spec.Telemetry != nil && spec.Telemetry.Metrics != nil { + for _, sink := range spec.Telemetry.Metrics.Sinks { + if sink.Type == egv1a1.MetricSinkTypeOpenTelemetry { + if sink.OpenTelemetry == nil { + err := fmt.Errorf("opentelemetry is required if the sink type is OpenTelemetry") + errs = append(errs, err) + } + } + } + } + + return errs +} + +func validateProxyAccessLog(accessLog *egv1a1.ProxyAccessLog) []error { + if accessLog.Disable { + return nil + } + + var errs []error + + for _, setting := range accessLog.Settings { + switch setting.Format.Type { + case egv1a1.ProxyAccessLogFormatTypeText: + if setting.Format.Text == nil { + err := fmt.Errorf("unable to configure access log when using Text format but \"text\" field being empty") + errs = append(errs, err) + } + case egv1a1.ProxyAccessLogFormatTypeJSON: + if setting.Format.JSON == nil { + err := fmt.Errorf("unable to configure access log when using JSON format but \"json\" field being empty") + errs = append(errs, err) + } + } + + for _, sink := range setting.Sinks { + switch sink.Type { + case egv1a1.ProxyAccessLogSinkTypeFile: + if sink.File == nil { + err := fmt.Errorf("unable to configure access log when using File sink type but \"file\" field being empty") + errs = append(errs, err) + } + case egv1a1.ProxyAccessLogSinkTypeOpenTelemetry: + if sink.OpenTelemetry == nil { + err := fmt.Errorf("unable to configure access log when using OpenTelemetry sink type but \"openTelemetry\" field being empty") + errs = append(errs, err) + } + } + } + } + + return errs +} diff --git a/api/v1alpha1/validation/envoyproxy_validate_test.go b/api/v1alpha1/validation/envoyproxy_validate_test.go new file mode 100644 index 00000000000..0a9fcde07b0 --- /dev/null +++ b/api/v1alpha1/validation/envoyproxy_validate_test.go @@ -0,0 +1,589 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package validation + +import ( + // Register embed + _ "embed" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +var ( + //go:embed testdata/valid-user-bootstrap.yaml + validUserBootstrap string + //go:embed testdata/merge-user-bootstrap.yaml + mergeUserBootstrap string + //go:embed testdata/missing-admin-address-user-bootstrap.yaml + missingAdminAddressUserBootstrap string + //go:embed testdata/different-dynamic-resources-user-bootstrap.yaml + differentDynamicResourcesUserBootstrap string + //go:embed testdata/different-xds-cluster-address-bootstrap.yaml + differentXdsClusterAddressBootstrap string +) + +func TestValidateEnvoyProxy(t *testing.T) { + testCases := []struct { + name string + proxy *egv1a1.EnvoyProxy + expected bool + }{ + { + name: "nil egv1a1.EnvoyProxy", + proxy: nil, + expected: false, + }, + { + name: "nil provider", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: nil, + }, + }, + expected: true, + }, + { + name: "unsupported provider", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeFile, + }, + }, + }, + expected: false, + }, + { + name: "nil envoy service", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: nil, + }, + }, + }, + }, + expected: true, + }, + { + name: "unsupported envoy service type \"\" ", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(""), + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "valid envoy service type 'NodePort'", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceType(corev1.ServiceTypeNodePort)), + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "valid envoy service type 'LoadBalancer'", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "valid envoy service type 'ClusterIP'", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeClusterIP), + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "envoy service type 'LoadBalancer' with allocateLoadBalancerNodePorts", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + AllocateLoadBalancerNodePorts: ptr.To(false), + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "non envoy service type 'LoadBalancer' with allocateLoadBalancerNodePorts", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeClusterIP), + AllocateLoadBalancerNodePorts: ptr.To(false), + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "envoy service type 'LoadBalancer' with valid loadBalancerIP", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("10.11.12.13"), + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "envoy service type 'LoadBalancer' with invalid loadBalancerIP", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("invalid-ip"), + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "envoy service type 'LoadBalancer' with ipv6 loadBalancerIP", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("2001:db8::68"), + }, + }, + }, + }, + }, + expected: false, + }, + + { + name: "valid user bootstrap replace type", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Bootstrap: &egv1a1.ProxyBootstrap{ + Value: validUserBootstrap, + }, + }, + }, + expected: true, + }, + { + name: "valid user bootstrap merge type", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Bootstrap: &egv1a1.ProxyBootstrap{ + Type: ptr.To(egv1a1.BootstrapTypeMerge), + Value: mergeUserBootstrap, + }, + }, + }, + expected: true, + }, + { + name: "user bootstrap with missing admin address", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Bootstrap: &egv1a1.ProxyBootstrap{ + Value: missingAdminAddressUserBootstrap, + }, + }, + }, + expected: false, + }, + { + name: "user bootstrap with different dynamic resources", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Bootstrap: &egv1a1.ProxyBootstrap{ + Value: differentDynamicResourcesUserBootstrap, + }, + }, + }, + expected: false, + }, + { + name: "user bootstrap with different xds_cluster endpoint", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Bootstrap: &egv1a1.ProxyBootstrap{ + Value: differentXdsClusterAddressBootstrap, + }, + }, + }, + expected: false, + }, + { + name: "should invalid when accesslog enabled using Text format, but `text` field being empty", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeText, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "should invalid when accesslog enabled using File sink, but `file` field being empty", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeText, + Text: ptr.To("[%START_TIME%]"), + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeFile, + }, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, { + name: "should invalid when metrics type is OpenTelemetry, but `OpenTelemetry` field being empty", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + Metrics: &egv1a1.ProxyMetrics{ + Sinks: []egv1a1.ProxyMetricSink{ + { + Type: egv1a1.MetricSinkTypeOpenTelemetry, + }, + }, + }, + }, + }, + }, + expected: false, + }, { + name: "should valid when metrics type is OpenTelemetry and `OpenTelemetry` field being not empty", + proxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + Metrics: &egv1a1.ProxyMetrics{ + Sinks: []egv1a1.ProxyMetricSink{ + { + Type: egv1a1.MetricSinkTypeOpenTelemetry, + OpenTelemetry: &egv1a1.ProxyOpenTelemetrySink{ + Host: "0.0.0.0", + Port: 3217, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + } + + for i := range testCases { + tc := testCases[i] + t.Run(tc.name, func(t *testing.T) { + err := ValidateEnvoyProxy(tc.proxy) + if tc.expected { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +func TestEnvoyProxyProvider(t *testing.T) { + envoyProxy := &egv1a1.EnvoyProxy{ + Spec: egv1a1.EnvoyProxySpec{ + Provider: egv1a1.DefaultEnvoyProxyProvider(), + }, + } + assert.True(t, envoyProxy.Spec.Provider != nil) + + envoyProxyProvider := envoyProxy.GetEnvoyProxyProvider() + assert.True(t, envoyProxyProvider.Kubernetes == nil) + assert.True(t, reflect.DeepEqual(envoyProxy.Spec.Provider, envoyProxyProvider)) + + envoyProxyKubeProvider := envoyProxyProvider.GetEnvoyProxyKubeProvider() + + assert.True(t, envoyProxyProvider.Kubernetes != nil) + assert.True(t, reflect.DeepEqual(envoyProxyProvider.Kubernetes, envoyProxyKubeProvider)) + + envoyProxyProvider.GetEnvoyProxyKubeProvider() + + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment != nil) + assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment, egv1a1.DefaultKubernetesDeployment(egv1a1.DefaultEnvoyProxyImage)) + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Replicas != nil) + assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Replicas, egv1a1.DefaultKubernetesDeploymentReplicas()) + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Pod != nil) + assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Pod, egv1a1.DefaultKubernetesPod()) + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container != nil) + assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container, egv1a1.DefaultKubernetesContainer(egv1a1.DefaultEnvoyProxyImage)) + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Resources != nil) + assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Resources, egv1a1.DefaultResourceRequirements()) + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Image != nil) + assert.Equal(t, envoyProxyProvider.Kubernetes.EnvoyDeployment.Container.Image, egv1a1.DefaultKubernetesContainerImage(egv1a1.DefaultEnvoyProxyImage)) + + assert.True(t, envoyProxyProvider.Kubernetes.EnvoyService != nil) + assert.True(t, reflect.DeepEqual(envoyProxyProvider.Kubernetes.EnvoyService.Type, egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer))) +} + +func TestGetEnvoyProxyDefaultComponentLevel(t *testing.T) { + cases := []struct { + logging egv1a1.ProxyLogging + component egv1a1.ProxyLogComponent + expected egv1a1.LogLevel + }{ + { + logging: egv1a1.ProxyLogging{ + Level: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelInfo, + }, + }, + expected: egv1a1.LogLevelInfo, + }, + { + logging: egv1a1.ProxyLogging{ + Level: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelInfo, + }, + }, + expected: egv1a1.LogLevelInfo, + }, + } + + for _, tc := range cases { + t.Run("", func(t *testing.T) { + got := tc.logging.DefaultEnvoyProxyLoggingLevel() + require.Equal(t, tc.expected, got) + }) + } +} + +func TestGetEnvoyProxyComponentLevelArgs(t *testing.T) { + cases := []struct { + logging egv1a1.ProxyLogging + expected string + }{ + { + logging: egv1a1.ProxyLogging{}, + expected: "", + }, + { + logging: egv1a1.ProxyLogging{ + Level: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelInfo, + }, + }, + expected: "", + }, + { + logging: egv1a1.ProxyLogging{ + Level: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelInfo, + egv1a1.LogComponentAdmin: egv1a1.LogLevelWarn, + }, + }, + expected: "admin:warn", + }, + { + logging: egv1a1.ProxyLogging{ + Level: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelInfo, + egv1a1.LogComponentAdmin: egv1a1.LogLevelWarn, + egv1a1.LogComponentFilter: egv1a1.LogLevelDebug, + }, + }, + expected: "admin:warn,filter:debug", + }, + { + logging: egv1a1.ProxyLogging{ + Level: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelInfo, + egv1a1.LogComponentAdmin: egv1a1.LogLevelWarn, + egv1a1.LogComponentFilter: egv1a1.LogLevelDebug, + egv1a1.LogComponentClient: "", + }, + }, + expected: "admin:warn,filter:debug", + }, + } + + for _, tc := range cases { + t.Run("", func(t *testing.T) { + got := tc.logging.GetEnvoyProxyComponentLevel() + require.Equal(t, tc.expected, got) + }) + } +} diff --git a/api/v1alpha1/validation/securitypolicy_validate.go b/api/v1alpha1/validation/securitypolicy_validate.go new file mode 100644 index 00000000000..66bee6a8eb5 --- /dev/null +++ b/api/v1alpha1/validation/securitypolicy_validate.go @@ -0,0 +1,117 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package validation + +import ( + "errors" + "fmt" + "net/mail" + "net/url" + + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/validation" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +// ValidateSecurityPolicy validates the provided SecurityPolicy. +func ValidateSecurityPolicy(policy *egv1a1.SecurityPolicy) error { + var errs []error + if policy == nil { + return errors.New("policy is nil") + } + if err := validateSecurityPolicySpec(&policy.Spec); err != nil { + errs = append(errs, errors.New("policy is nil")) + } + + return utilerrors.NewAggregate(errs) +} + +// validateSecurityPolicySpec validates the provided spec. +func validateSecurityPolicySpec(spec *egv1a1.SecurityPolicySpec) error { + var errs []error + + sum := 0 + switch { + case spec == nil: + errs = append(errs, errors.New("spec is nil")) + case spec.CORS != nil: + sum++ + case spec.JWT != nil: + sum++ + } + if sum == 0 { + errs = append(errs, errors.New("no security policy is specified")) + } + + // Return early if any errors exist. + if len(errs) != 0 { + return utilerrors.NewAggregate(errs) + } + + if err := ValidateJWTProvider(spec.JWT.Providers); err != nil { + errs = append(errs, err) + } + + return utilerrors.NewAggregate(errs) +} + +// ValidateJWTProvider validates the provided JWT authentication configuration. +func ValidateJWTProvider(providers []egv1a1.JWTProvider) error { + var errs []error + + var names []string + for _, provider := range providers { + switch { + case len(provider.Name) == 0: + errs = append(errs, errors.New("jwt provider cannot be an empty string")) + case len(provider.Issuer) != 0: + // Issuer can take the format of a URL or an email address. + if _, err := url.ParseRequestURI(provider.Issuer); err != nil { + _, err := mail.ParseAddress(provider.Issuer) + if err != nil { + errs = append(errs, fmt.Errorf("invalid issuer; must be a URL or email address: %v", err)) + } + } + case len(provider.RemoteJWKS.URI) == 0: + errs = append(errs, fmt.Errorf("uri must be set for remote JWKS provider: %s", provider.Name)) + } + if _, err := url.ParseRequestURI(provider.RemoteJWKS.URI); err != nil { + errs = append(errs, fmt.Errorf("invalid remote JWKS URI: %v", err)) + } + + if len(errs) == 0 { + if strErrs := validation.IsQualifiedName(provider.Name); len(strErrs) != 0 { + for _, strErr := range strErrs { + errs = append(errs, errors.New(strErr)) + } + } + // Ensure uniqueness among provider names. + if names == nil { + names = append(names, provider.Name) + } else { + for _, name := range names { + if name == provider.Name { + errs = append(errs, fmt.Errorf("provider name %s must be unique", provider.Name)) + } else { + names = append(names, provider.Name) + } + } + } + } + + for _, claimToHeader := range provider.ClaimToHeaders { + switch { + case len(claimToHeader.Header) == 0: + errs = append(errs, fmt.Errorf("header must be set for claimToHeader provider: %s", claimToHeader.Header)) + case len(claimToHeader.Claim) == 0: + errs = append(errs, fmt.Errorf("claim must be set for claimToHeader provider: %s", claimToHeader.Claim)) + } + } + } + + return utilerrors.NewAggregate(errs) +} diff --git a/api/v1alpha1/validation/securitypolicy_validate_test.go b/api/v1alpha1/validation/securitypolicy_validate_test.go new file mode 100644 index 00000000000..489c7644f8b --- /dev/null +++ b/api/v1alpha1/validation/securitypolicy_validate_test.go @@ -0,0 +1,479 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package validation + +import ( + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func TestValidateSecurityPolicy(t *testing.T) { + testCases := []struct { + name string + policy *egv1a1.SecurityPolicy + expected bool + }{ + { + name: "nil security policy", + policy: nil, + expected: false, + }, + { + name: "empty security policy", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{}, + }, + expected: false, + }, + { + name: "valid security policy with url", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "https://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "valid security policy with email", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "test@test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "valid security policy with jwtClaimToHeader", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "test@test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + ClaimToHeaders: []egv1a1.ClaimToHeader{ + { + Header: "test", + Claim: "test", + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "unqualified authentication provider name", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "unqualified_...", + Issuer: "https://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "unspecified provider name", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "", + Issuer: "https://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "non unique provider names", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "unique", + Issuer: "https://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + { + Name: "non-unique", + Issuer: "https://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + { + Name: "non-unique", + Issuer: "https://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "invalid issuer uri", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "http://invalid url.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "http://www.test.local", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "inivalid issuer email", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "test@!123...", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "invalid remote jwks uri", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "http://www.test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "invalid/local", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "unspecified remote jwks uri", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "", + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "unspecified jwtClaimToHeader headerName", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "test@test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + ClaimToHeaders: []egv1a1.ClaimToHeader{ + { + Header: "", + Claim: "test", + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "unspecified jwtClaimToHeader claimName", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "test@test.local", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + ClaimToHeaders: []egv1a1.ClaimToHeader{ + { + Header: "test", + Claim: "", + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + { + name: "unspecified issuer", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Audiences: []string{"test.local"}, + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "unspecified audiences", + policy: &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test", + Issuer: "https://www.test.local", + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test.local/jwt/public-key/jwks.json", + }, + }, + }, + }, + }, + }, + expected: true, + }, + } + + for i := range testCases { + tc := testCases[i] + t.Run(tc.name, func(t *testing.T) { + err := ValidateSecurityPolicy(tc.policy) + if tc.expected { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/api/config/v1alpha1/validation/testdata/different-dynamic-resources-user-bootstrap.yaml b/api/v1alpha1/validation/testdata/different-dynamic-resources-user-bootstrap.yaml similarity index 100% rename from api/config/v1alpha1/validation/testdata/different-dynamic-resources-user-bootstrap.yaml rename to api/v1alpha1/validation/testdata/different-dynamic-resources-user-bootstrap.yaml diff --git a/api/config/v1alpha1/validation/testdata/different-xds-cluster-address-bootstrap.yaml b/api/v1alpha1/validation/testdata/different-xds-cluster-address-bootstrap.yaml similarity index 100% rename from api/config/v1alpha1/validation/testdata/different-xds-cluster-address-bootstrap.yaml rename to api/v1alpha1/validation/testdata/different-xds-cluster-address-bootstrap.yaml diff --git a/api/v1alpha1/validation/testdata/merge-user-bootstrap.yaml b/api/v1alpha1/validation/testdata/merge-user-bootstrap.yaml new file mode 100644 index 00000000000..58665ad040f --- /dev/null +++ b/api/v1alpha1/validation/testdata/merge-user-bootstrap.yaml @@ -0,0 +1,19 @@ +admin: + address: + socket_address: + port_value: 8080 +static_resources: + clusters: + - name: prometheus_stats + connect_timeout: 0.250s + type: STATIC + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: prometheus_stats + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 diff --git a/api/config/v1alpha1/validation/testdata/missing-admin-address-user-bootstrap.yaml b/api/v1alpha1/validation/testdata/missing-admin-address-user-bootstrap.yaml similarity index 100% rename from api/config/v1alpha1/validation/testdata/missing-admin-address-user-bootstrap.yaml rename to api/v1alpha1/validation/testdata/missing-admin-address-user-bootstrap.yaml diff --git a/api/config/v1alpha1/validation/testdata/valid-user-bootstrap.yaml b/api/v1alpha1/validation/testdata/valid-user-bootstrap.yaml similarity index 90% rename from api/config/v1alpha1/validation/testdata/valid-user-bootstrap.yaml rename to api/v1alpha1/validation/testdata/valid-user-bootstrap.yaml index 076b16a712b..ff46420fb37 100644 --- a/api/config/v1alpha1/validation/testdata/valid-user-bootstrap.yaml +++ b/api/v1alpha1/validation/testdata/valid-user-bootstrap.yaml @@ -22,22 +22,16 @@ dynamicResources: cdsConfig: ads: {} resourceApiVersion: V3 -layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 staticResources: clusters: - connectTimeout: 10s loadAssignment: clusterName: xds_cluster endpoints: - - lbEndpoints: - - endpoint: + - loadBalancingWeight: 1 + lbEndpoints: + - loadBalancingWeight: 1 + endpoint: address: socketAddress: address: envoy-gateway diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 2237b268296..78360fa1282 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated // Copyright Envoy Gateway Authors // SPDX-License-Identifier: Apache-2.0 @@ -11,30 +10,35 @@ package v1alpha1 import ( + appsv1 "k8s.io/api/apps/v1" + "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" + apisv1 "sigs.k8s.io/gateway-api/apis/v1" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthenticationFilter) DeepCopyInto(out *AuthenticationFilter) { +func (in *BackendTrafficPolicy) DeepCopyInto(out *BackendTrafficPolicy) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationFilter. -func (in *AuthenticationFilter) DeepCopy() *AuthenticationFilter { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicy. +func (in *BackendTrafficPolicy) DeepCopy() *BackendTrafficPolicy { if in == nil { return nil } - out := new(AuthenticationFilter) + out := new(BackendTrafficPolicy) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *AuthenticationFilter) DeepCopyObject() runtime.Object { +func (in *BackendTrafficPolicy) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -42,31 +46,31 @@ func (in *AuthenticationFilter) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthenticationFilterList) DeepCopyInto(out *AuthenticationFilterList) { +func (in *BackendTrafficPolicyList) DeepCopyInto(out *BackendTrafficPolicyList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]AuthenticationFilter, len(*in)) + *out = make([]BackendTrafficPolicy, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationFilterList. -func (in *AuthenticationFilterList) DeepCopy() *AuthenticationFilterList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicyList. +func (in *BackendTrafficPolicyList) DeepCopy() *BackendTrafficPolicyList { if in == nil { return nil } - out := new(AuthenticationFilterList) + out := new(BackendTrafficPolicyList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *AuthenticationFilterList) DeepCopyObject() runtime.Object { +func (in *BackendTrafficPolicyList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -74,60 +78,143 @@ func (in *AuthenticationFilterList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthenticationFilterSpec) DeepCopyInto(out *AuthenticationFilterSpec) { +func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec) { + *out = *in + in.TargetRef.DeepCopyInto(&out.TargetRef) + if in.RateLimit != nil { + in, out := &in.RateLimit, &out.RateLimit + *out = new(RateLimitSpec) + (*in).DeepCopyInto(*out) + } + if in.LoadBalancer != nil { + in, out := &in.LoadBalancer, &out.LoadBalancer + *out = new(LoadBalancer) + (*in).DeepCopyInto(*out) + } + if in.ProxyProtocol != nil { + in, out := &in.ProxyProtocol, &out.ProxyProtocol + *out = new(ProxyProtocol) + **out = **in + } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicySpec. +func (in *BackendTrafficPolicySpec) DeepCopy() *BackendTrafficPolicySpec { + if in == nil { + return nil + } + out := new(BackendTrafficPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendTrafficPolicyStatus) DeepCopyInto(out *BackendTrafficPolicyStatus) { *out = *in - if in.JwtProviders != nil { - in, out := &in.JwtProviders, &out.JwtProviders - *out = make([]JwtAuthenticationFilterProvider, len(*in)) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationFilterSpec. -func (in *AuthenticationFilterSpec) DeepCopy() *AuthenticationFilterSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicyStatus. +func (in *BackendTrafficPolicyStatus) DeepCopy() *BackendTrafficPolicyStatus { if in == nil { return nil } - out := new(AuthenticationFilterSpec) + out := new(BackendTrafficPolicyStatus) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClaimToHeader) DeepCopyInto(out *ClaimToHeader) { +func (in *BasicAuth) DeepCopyInto(out *BasicAuth) { *out = *in + in.Users.DeepCopyInto(&out.Users) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimToHeader. -func (in *ClaimToHeader) DeepCopy() *ClaimToHeader { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth. +func (in *BasicAuth) DeepCopy() *BasicAuth { if in == nil { return nil } - out := new(ClaimToHeader) + out := new(BasicAuth) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyJSONPatchConfig) DeepCopyInto(out *EnvoyJSONPatchConfig) { +func (in *CORS) DeepCopyInto(out *CORS) { *out = *in - in.Operation.DeepCopyInto(&out.Operation) + if in.AllowOrigins != nil { + in, out := &in.AllowOrigins, &out.AllowOrigins + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AllowMethods != nil { + in, out := &in.AllowMethods, &out.AllowMethods + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AllowHeaders != nil { + in, out := &in.AllowHeaders, &out.AllowHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExposeHeaders != nil { + in, out := &in.ExposeHeaders, &out.ExposeHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MaxAge != nil { + in, out := &in.MaxAge, &out.MaxAge + *out = new(v1.Duration) + **out = **in + } + if in.AllowCredentials != nil { + in, out := &in.AllowCredentials, &out.AllowCredentials + *out = new(bool) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyJSONPatchConfig. -func (in *EnvoyJSONPatchConfig) DeepCopy() *EnvoyJSONPatchConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CORS. +func (in *CORS) DeepCopy() *CORS { if in == nil { return nil } - out := new(EnvoyJSONPatchConfig) + out := new(CORS) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { +func (in *ClaimToHeader) DeepCopyInto(out *ClaimToHeader) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimToHeader. +func (in *ClaimToHeader) DeepCopy() *ClaimToHeader { + if in == nil { + return nil + } + out := new(ClaimToHeader) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientTrafficPolicy) DeepCopyInto(out *ClientTrafficPolicy) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -135,18 +222,18 @@ func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicy. -func (in *EnvoyPatchPolicy) DeepCopy() *EnvoyPatchPolicy { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTrafficPolicy. +func (in *ClientTrafficPolicy) DeepCopy() *ClientTrafficPolicy { if in == nil { return nil } - out := new(EnvoyPatchPolicy) + out := new(ClientTrafficPolicy) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyPatchPolicy) DeepCopyObject() runtime.Object { +func (in *ClientTrafficPolicy) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -154,31 +241,31 @@ func (in *EnvoyPatchPolicy) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyPatchPolicyList) DeepCopyInto(out *EnvoyPatchPolicyList) { +func (in *ClientTrafficPolicyList) DeepCopyInto(out *ClientTrafficPolicyList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]EnvoyPatchPolicy, len(*in)) + *out = make([]ClientTrafficPolicy, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyList. -func (in *EnvoyPatchPolicyList) DeepCopy() *EnvoyPatchPolicyList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTrafficPolicyList. +func (in *ClientTrafficPolicyList) DeepCopy() *ClientTrafficPolicyList { if in == nil { return nil } - out := new(EnvoyPatchPolicyList) + out := new(ClientTrafficPolicyList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyPatchPolicyList) DeepCopyObject() runtime.Object { +func (in *ClientTrafficPolicyList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -186,30 +273,33 @@ func (in *EnvoyPatchPolicyList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyPatchPolicySpec) DeepCopyInto(out *EnvoyPatchPolicySpec) { +func (in *ClientTrafficPolicySpec) DeepCopyInto(out *ClientTrafficPolicySpec) { *out = *in - if in.JSONPatches != nil { - in, out := &in.JSONPatches, &out.JSONPatches - *out = make([]EnvoyJSONPatchConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } in.TargetRef.DeepCopyInto(&out.TargetRef) + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } + if in.EnableProxyProtocol != nil { + in, out := &in.EnableProxyProtocol, &out.EnableProxyProtocol + *out = new(bool) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicySpec. -func (in *EnvoyPatchPolicySpec) DeepCopy() *EnvoyPatchPolicySpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTrafficPolicySpec. +func (in *ClientTrafficPolicySpec) DeepCopy() *ClientTrafficPolicySpec { if in == nil { return nil } - out := new(EnvoyPatchPolicySpec) + out := new(ClientTrafficPolicySpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyPatchPolicyStatus) DeepCopyInto(out *EnvoyPatchPolicyStatus) { +func (in *ClientTrafficPolicyStatus) DeepCopyInto(out *ClientTrafficPolicyStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions @@ -220,284 +310,2141 @@ func (in *EnvoyPatchPolicyStatus) DeepCopyInto(out *EnvoyPatchPolicyStatus) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyStatus. -func (in *EnvoyPatchPolicyStatus) DeepCopy() *EnvoyPatchPolicyStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTrafficPolicyStatus. +func (in *ClientTrafficPolicyStatus) DeepCopy() *ClientTrafficPolicyStatus { if in == nil { return nil } - out := new(EnvoyPatchPolicyStatus) + out := new(ClientTrafficPolicyStatus) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { +func (in *ConsistentHash) DeepCopyInto(out *ConsistentHash) { *out = *in - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]RateLimitRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRateLimit. -func (in *GlobalRateLimit) DeepCopy() *GlobalRateLimit { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsistentHash. +func (in *ConsistentHash) DeepCopy() *ConsistentHash { if in == nil { return nil } - out := new(GlobalRateLimit) + out := new(ConsistentHash) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HeaderMatch) DeepCopyInto(out *HeaderMatch) { +func (in *CustomTag) DeepCopyInto(out *CustomTag) { *out = *in - if in.Type != nil { - in, out := &in.Type, &out.Type - *out = new(HeaderMatchType) + if in.Literal != nil { + in, out := &in.Literal, &out.Literal + *out = new(LiteralCustomTag) **out = **in } - if in.Value != nil { - in, out := &in.Value, &out.Value + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = new(EnvironmentCustomTag) + (*in).DeepCopyInto(*out) + } + if in.RequestHeader != nil { + in, out := &in.RequestHeader, &out.RequestHeader + *out = new(RequestHeaderCustomTag) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomTag. +func (in *CustomTag) DeepCopy() *CustomTag { + if in == nil { + return nil + } + out := new(CustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentCustomTag) DeepCopyInto(out *EnvironmentCustomTag) { + *out = *in + if in.DefaultValue != nil { + in, out := &in.DefaultValue, &out.DefaultValue *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderMatch. -func (in *HeaderMatch) DeepCopy() *HeaderMatch { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentCustomTag. +func (in *EnvironmentCustomTag) DeepCopy() *EnvironmentCustomTag { if in == nil { return nil } - out := new(HeaderMatch) + out := new(EnvironmentCustomTag) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JSONPatchOperation) DeepCopyInto(out *JSONPatchOperation) { +func (in *EnvoyGateway) DeepCopyInto(out *EnvoyGateway) { *out = *in - in.Value.DeepCopyInto(&out.Value) + out.TypeMeta = in.TypeMeta + in.EnvoyGatewaySpec.DeepCopyInto(&out.EnvoyGatewaySpec) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONPatchOperation. -func (in *JSONPatchOperation) DeepCopy() *JSONPatchOperation { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGateway. +func (in *EnvoyGateway) DeepCopy() *EnvoyGateway { if in == nil { return nil } - out := new(JSONPatchOperation) + out := new(EnvoyGateway) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyGateway) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JwtAuthenticationFilterProvider) DeepCopyInto(out *JwtAuthenticationFilterProvider) { +func (in *EnvoyGatewayAdmin) DeepCopyInto(out *EnvoyGatewayAdmin) { *out = *in - if in.Audiences != nil { - in, out := &in.Audiences, &out.Audiences - *out = make([]string, len(*in)) - copy(*out, *in) - } - out.RemoteJWKS = in.RemoteJWKS - if in.ClaimToHeaders != nil { - in, out := &in.ClaimToHeaders, &out.ClaimToHeaders - *out = make([]ClaimToHeader, len(*in)) - copy(*out, *in) + if in.Address != nil { + in, out := &in.Address, &out.Address + *out = new(EnvoyGatewayAdminAddress) + **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtAuthenticationFilterProvider. -func (in *JwtAuthenticationFilterProvider) DeepCopy() *JwtAuthenticationFilterProvider { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayAdmin. +func (in *EnvoyGatewayAdmin) DeepCopy() *EnvoyGatewayAdmin { if in == nil { return nil } - out := new(JwtAuthenticationFilterProvider) + out := new(EnvoyGatewayAdmin) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitFilter) DeepCopyInto(out *RateLimitFilter) { +func (in *EnvoyGatewayAdminAddress) DeepCopyInto(out *EnvoyGatewayAdminAddress) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitFilter. -func (in *RateLimitFilter) DeepCopy() *RateLimitFilter { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayAdminAddress. +func (in *EnvoyGatewayAdminAddress) DeepCopy() *EnvoyGatewayAdminAddress { if in == nil { return nil } - out := new(RateLimitFilter) + out := new(EnvoyGatewayAdminAddress) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RateLimitFilter) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewayCustomProvider) DeepCopyInto(out *EnvoyGatewayCustomProvider) { + *out = *in + in.Resource.DeepCopyInto(&out.Resource) + in.Infrastructure.DeepCopyInto(&out.Infrastructure) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayCustomProvider. +func (in *EnvoyGatewayCustomProvider) DeepCopy() *EnvoyGatewayCustomProvider { + if in == nil { + return nil } - return nil + out := new(EnvoyGatewayCustomProvider) + in.DeepCopyInto(out) + return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitFilterList) DeepCopyInto(out *RateLimitFilterList) { +func (in *EnvoyGatewayFileResourceProvider) DeepCopyInto(out *EnvoyGatewayFileResourceProvider) { *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]RateLimitFilter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitFilterList. -func (in *RateLimitFilterList) DeepCopy() *RateLimitFilterList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayFileResourceProvider. +func (in *EnvoyGatewayFileResourceProvider) DeepCopy() *EnvoyGatewayFileResourceProvider { if in == nil { return nil } - out := new(RateLimitFilterList) + out := new(EnvoyGatewayFileResourceProvider) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RateLimitFilterList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitFilterSpec) DeepCopyInto(out *RateLimitFilterSpec) { +func (in *EnvoyGatewayHostInfrastructureProvider) DeepCopyInto(out *EnvoyGatewayHostInfrastructureProvider) { *out = *in - if in.Global != nil { - in, out := &in.Global, &out.Global - *out = new(GlobalRateLimit) - (*in).DeepCopyInto(*out) - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitFilterSpec. -func (in *RateLimitFilterSpec) DeepCopy() *RateLimitFilterSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayHostInfrastructureProvider. +func (in *EnvoyGatewayHostInfrastructureProvider) DeepCopy() *EnvoyGatewayHostInfrastructureProvider { if in == nil { return nil } - out := new(RateLimitFilterSpec) + out := new(EnvoyGatewayHostInfrastructureProvider) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) { +func (in *EnvoyGatewayInfrastructureProvider) DeepCopyInto(out *EnvoyGatewayInfrastructureProvider) { *out = *in - if in.ClientSelectors != nil { - in, out := &in.ClientSelectors, &out.ClientSelectors - *out = make([]RateLimitSelectCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.Host != nil { + in, out := &in.Host, &out.Host + *out = new(EnvoyGatewayHostInfrastructureProvider) + **out = **in } - out.Limit = in.Limit } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRule. -func (in *RateLimitRule) DeepCopy() *RateLimitRule { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayInfrastructureProvider. +func (in *EnvoyGatewayInfrastructureProvider) DeepCopy() *EnvoyGatewayInfrastructureProvider { if in == nil { return nil } - out := new(RateLimitRule) + out := new(EnvoyGatewayInfrastructureProvider) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitSelectCondition) DeepCopyInto(out *RateLimitSelectCondition) { +func (in *EnvoyGatewayKubernetesProvider) DeepCopyInto(out *EnvoyGatewayKubernetesProvider) { *out = *in - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = make([]HeaderMatch, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.RateLimitDeployment != nil { + in, out := &in.RateLimitDeployment, &out.RateLimitDeployment + *out = new(KubernetesDeploymentSpec) + (*in).DeepCopyInto(*out) } - if in.SourceIP != nil { - in, out := &in.SourceIP, &out.SourceIP - *out = new(string) + if in.Watch != nil { + in, out := &in.Watch, &out.Watch + *out = new(KubernetesWatchMode) + (*in).DeepCopyInto(*out) + } + if in.Deploy != nil { + in, out := &in.Deploy, &out.Deploy + *out = new(KubernetesDeployMode) **out = **in } - if in.SourceCIDR != nil { - in, out := &in.SourceCIDR, &out.SourceCIDR - *out = new(SourceMatch) - (*in).DeepCopyInto(*out) + if in.OverwriteControlPlaneCerts != nil { + in, out := &in.OverwriteControlPlaneCerts, &out.OverwriteControlPlaneCerts + *out = new(bool) + **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitSelectCondition. -func (in *RateLimitSelectCondition) DeepCopy() *RateLimitSelectCondition { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayKubernetesProvider. +func (in *EnvoyGatewayKubernetesProvider) DeepCopy() *EnvoyGatewayKubernetesProvider { if in == nil { return nil } - out := new(RateLimitSelectCondition) + out := new(EnvoyGatewayKubernetesProvider) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitValue) DeepCopyInto(out *RateLimitValue) { +func (in *EnvoyGatewayLogging) DeepCopyInto(out *EnvoyGatewayLogging) { *out = *in + if in.Level != nil { + in, out := &in.Level, &out.Level + *out = make(map[EnvoyGatewayLogComponent]LogLevel, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitValue. -func (in *RateLimitValue) DeepCopy() *RateLimitValue { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayLogging. +func (in *EnvoyGatewayLogging) DeepCopy() *EnvoyGatewayLogging { if in == nil { return nil } - out := new(RateLimitValue) + out := new(EnvoyGatewayLogging) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemoteJWKS) DeepCopyInto(out *RemoteJWKS) { +func (in *EnvoyGatewayMetricSink) DeepCopyInto(out *EnvoyGatewayMetricSink) { *out = *in + if in.OpenTelemetry != nil { + in, out := &in.OpenTelemetry, &out.OpenTelemetry + *out = new(EnvoyGatewayOpenTelemetrySink) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteJWKS. -func (in *RemoteJWKS) DeepCopy() *RemoteJWKS { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayMetricSink. +func (in *EnvoyGatewayMetricSink) DeepCopy() *EnvoyGatewayMetricSink { if in == nil { return nil } - out := new(RemoteJWKS) + out := new(EnvoyGatewayMetricSink) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SourceMatch) DeepCopyInto(out *SourceMatch) { +func (in *EnvoyGatewayMetrics) DeepCopyInto(out *EnvoyGatewayMetrics) { *out = *in - if in.Type != nil { - in, out := &in.Type, &out.Type - *out = new(SourceMatchType) + if in.Sinks != nil { + in, out := &in.Sinks, &out.Sinks + *out = make([]EnvoyGatewayMetricSink, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Prometheus != nil { + in, out := &in.Prometheus, &out.Prometheus + *out = new(EnvoyGatewayPrometheusProvider) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceMatch. -func (in *SourceMatch) DeepCopy() *SourceMatch { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayMetrics. +func (in *EnvoyGatewayMetrics) DeepCopy() *EnvoyGatewayMetrics { if in == nil { return nil } - out := new(SourceMatch) + out := new(EnvoyGatewayMetrics) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewayOpenTelemetrySink) DeepCopyInto(out *EnvoyGatewayOpenTelemetrySink) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayOpenTelemetrySink. +func (in *EnvoyGatewayOpenTelemetrySink) DeepCopy() *EnvoyGatewayOpenTelemetrySink { + if in == nil { + return nil + } + out := new(EnvoyGatewayOpenTelemetrySink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewayPrometheusProvider) DeepCopyInto(out *EnvoyGatewayPrometheusProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayPrometheusProvider. +func (in *EnvoyGatewayPrometheusProvider) DeepCopy() *EnvoyGatewayPrometheusProvider { + if in == nil { + return nil + } + out := new(EnvoyGatewayPrometheusProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewayProvider) DeepCopyInto(out *EnvoyGatewayProvider) { + *out = *in + if in.Kubernetes != nil { + in, out := &in.Kubernetes, &out.Kubernetes + *out = new(EnvoyGatewayKubernetesProvider) + (*in).DeepCopyInto(*out) + } + if in.Custom != nil { + in, out := &in.Custom, &out.Custom + *out = new(EnvoyGatewayCustomProvider) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayProvider. +func (in *EnvoyGatewayProvider) DeepCopy() *EnvoyGatewayProvider { + if in == nil { + return nil + } + out := new(EnvoyGatewayProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewayResourceProvider) DeepCopyInto(out *EnvoyGatewayResourceProvider) { + *out = *in + if in.File != nil { + in, out := &in.File, &out.File + *out = new(EnvoyGatewayFileResourceProvider) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayResourceProvider. +func (in *EnvoyGatewayResourceProvider) DeepCopy() *EnvoyGatewayResourceProvider { + if in == nil { + return nil + } + out := new(EnvoyGatewayResourceProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewaySpec) DeepCopyInto(out *EnvoyGatewaySpec) { + *out = *in + if in.Gateway != nil { + in, out := &in.Gateway, &out.Gateway + *out = new(Gateway) + **out = **in + } + if in.Provider != nil { + in, out := &in.Provider, &out.Provider + *out = new(EnvoyGatewayProvider) + (*in).DeepCopyInto(*out) + } + if in.Logging != nil { + in, out := &in.Logging, &out.Logging + *out = new(EnvoyGatewayLogging) + (*in).DeepCopyInto(*out) + } + if in.Admin != nil { + in, out := &in.Admin, &out.Admin + *out = new(EnvoyGatewayAdmin) + (*in).DeepCopyInto(*out) + } + if in.Telemetry != nil { + in, out := &in.Telemetry, &out.Telemetry + *out = new(EnvoyGatewayTelemetry) + (*in).DeepCopyInto(*out) + } + if in.RateLimit != nil { + in, out := &in.RateLimit, &out.RateLimit + *out = new(RateLimit) + (*in).DeepCopyInto(*out) + } + if in.ExtensionManager != nil { + in, out := &in.ExtensionManager, &out.ExtensionManager + *out = new(ExtensionManager) + (*in).DeepCopyInto(*out) + } + if in.ExtensionAPIs != nil { + in, out := &in.ExtensionAPIs, &out.ExtensionAPIs + *out = new(ExtensionAPISettings) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewaySpec. +func (in *EnvoyGatewaySpec) DeepCopy() *EnvoyGatewaySpec { + if in == nil { + return nil + } + out := new(EnvoyGatewaySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyGatewayTelemetry) DeepCopyInto(out *EnvoyGatewayTelemetry) { + *out = *in + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(EnvoyGatewayMetrics) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayTelemetry. +func (in *EnvoyGatewayTelemetry) DeepCopy() *EnvoyGatewayTelemetry { + if in == nil { + return nil + } + out := new(EnvoyGatewayTelemetry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyJSONPatchConfig) DeepCopyInto(out *EnvoyJSONPatchConfig) { + *out = *in + in.Operation.DeepCopyInto(&out.Operation) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyJSONPatchConfig. +func (in *EnvoyJSONPatchConfig) DeepCopy() *EnvoyJSONPatchConfig { + if in == nil { + return nil + } + out := new(EnvoyJSONPatchConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicy. +func (in *EnvoyPatchPolicy) DeepCopy() *EnvoyPatchPolicy { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyPatchPolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicyList) DeepCopyInto(out *EnvoyPatchPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EnvoyPatchPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyList. +func (in *EnvoyPatchPolicyList) DeepCopy() *EnvoyPatchPolicyList { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyPatchPolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicySpec) DeepCopyInto(out *EnvoyPatchPolicySpec) { + *out = *in + if in.JSONPatches != nil { + in, out := &in.JSONPatches, &out.JSONPatches + *out = make([]EnvoyJSONPatchConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.TargetRef.DeepCopyInto(&out.TargetRef) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicySpec. +func (in *EnvoyPatchPolicySpec) DeepCopy() *EnvoyPatchPolicySpec { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicyStatus) DeepCopyInto(out *EnvoyPatchPolicyStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyStatus. +func (in *EnvoyPatchPolicyStatus) DeepCopy() *EnvoyPatchPolicyStatus { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxy) DeepCopyInto(out *EnvoyProxy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxy. +func (in *EnvoyProxy) DeepCopy() *EnvoyProxy { + if in == nil { + return nil + } + out := new(EnvoyProxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyProxy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyKubernetesProvider) DeepCopyInto(out *EnvoyProxyKubernetesProvider) { + *out = *in + if in.EnvoyDeployment != nil { + in, out := &in.EnvoyDeployment, &out.EnvoyDeployment + *out = new(KubernetesDeploymentSpec) + (*in).DeepCopyInto(*out) + } + if in.EnvoyService != nil { + in, out := &in.EnvoyService, &out.EnvoyService + *out = new(KubernetesServiceSpec) + (*in).DeepCopyInto(*out) + } + if in.EnvoyHpa != nil { + in, out := &in.EnvoyHpa, &out.EnvoyHpa + *out = new(KubernetesHorizontalPodAutoscalerSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyKubernetesProvider. +func (in *EnvoyProxyKubernetesProvider) DeepCopy() *EnvoyProxyKubernetesProvider { + if in == nil { + return nil + } + out := new(EnvoyProxyKubernetesProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyList) DeepCopyInto(out *EnvoyProxyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EnvoyProxy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyList. +func (in *EnvoyProxyList) DeepCopy() *EnvoyProxyList { + if in == nil { + return nil + } + out := new(EnvoyProxyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyProxyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyProvider) DeepCopyInto(out *EnvoyProxyProvider) { + *out = *in + if in.Kubernetes != nil { + in, out := &in.Kubernetes, &out.Kubernetes + *out = new(EnvoyProxyKubernetesProvider) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyProvider. +func (in *EnvoyProxyProvider) DeepCopy() *EnvoyProxyProvider { + if in == nil { + return nil + } + out := new(EnvoyProxyProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxySpec) DeepCopyInto(out *EnvoyProxySpec) { + *out = *in + if in.Provider != nil { + in, out := &in.Provider, &out.Provider + *out = new(EnvoyProxyProvider) + (*in).DeepCopyInto(*out) + } + in.Logging.DeepCopyInto(&out.Logging) + if in.Telemetry != nil { + in, out := &in.Telemetry, &out.Telemetry + *out = new(ProxyTelemetry) + (*in).DeepCopyInto(*out) + } + if in.Bootstrap != nil { + in, out := &in.Bootstrap, &out.Bootstrap + *out = new(ProxyBootstrap) + (*in).DeepCopyInto(*out) + } + if in.Concurrency != nil { + in, out := &in.Concurrency, &out.Concurrency + *out = new(int32) + **out = **in + } + if in.MergeGateways != nil { + in, out := &in.MergeGateways, &out.MergeGateways + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxySpec. +func (in *EnvoyProxySpec) DeepCopy() *EnvoyProxySpec { + if in == nil { + return nil + } + out := new(EnvoyProxySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyStatus) DeepCopyInto(out *EnvoyProxyStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyStatus. +func (in *EnvoyProxyStatus) DeepCopy() *EnvoyProxyStatus { + if in == nil { + return nil + } + out := new(EnvoyProxyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtensionAPISettings) DeepCopyInto(out *ExtensionAPISettings) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionAPISettings. +func (in *ExtensionAPISettings) DeepCopy() *ExtensionAPISettings { + if in == nil { + return nil + } + out := new(ExtensionAPISettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtensionHooks) DeepCopyInto(out *ExtensionHooks) { + *out = *in + if in.XDSTranslator != nil { + in, out := &in.XDSTranslator, &out.XDSTranslator + *out = new(XDSTranslatorHooks) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionHooks. +func (in *ExtensionHooks) DeepCopy() *ExtensionHooks { + if in == nil { + return nil + } + out := new(ExtensionHooks) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtensionManager) DeepCopyInto(out *ExtensionManager) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]GroupVersionKind, len(*in)) + copy(*out, *in) + } + if in.Hooks != nil { + in, out := &in.Hooks, &out.Hooks + *out = new(ExtensionHooks) + (*in).DeepCopyInto(*out) + } + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(ExtensionService) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionManager. +func (in *ExtensionManager) DeepCopy() *ExtensionManager { + if in == nil { + return nil + } + out := new(ExtensionManager) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtensionService) DeepCopyInto(out *ExtensionService) { + *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(ExtensionTLS) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionService. +func (in *ExtensionService) DeepCopy() *ExtensionService { + if in == nil { + return nil + } + out := new(ExtensionService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtensionTLS) DeepCopyInto(out *ExtensionTLS) { + *out = *in + in.CertificateRef.DeepCopyInto(&out.CertificateRef) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionTLS. +func (in *ExtensionTLS) DeepCopy() *ExtensionTLS { + if in == nil { + return nil + } + out := new(ExtensionTLS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileEnvoyProxyAccessLog) DeepCopyInto(out *FileEnvoyProxyAccessLog) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileEnvoyProxyAccessLog. +func (in *FileEnvoyProxyAccessLog) DeepCopy() *FileEnvoyProxyAccessLog { + if in == nil { + return nil + } + out := new(FileEnvoyProxyAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Gateway) DeepCopyInto(out *Gateway) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Gateway. +func (in *Gateway) DeepCopy() *Gateway { + if in == nil { + return nil + } + out := new(Gateway) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]RateLimitRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRateLimit. +func (in *GlobalRateLimit) DeepCopy() *GlobalRateLimit { + if in == nil { + return nil + } + out := new(GlobalRateLimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GroupVersionKind) DeepCopyInto(out *GroupVersionKind) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupVersionKind. +func (in *GroupVersionKind) DeepCopy() *GroupVersionKind { + if in == nil { + return nil + } + out := new(GroupVersionKind) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HeaderMatch) DeepCopyInto(out *HeaderMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(HeaderMatchType) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderMatch. +func (in *HeaderMatch) DeepCopy() *HeaderMatch { + if in == nil { + return nil + } + out := new(HeaderMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONPatchOperation) DeepCopyInto(out *JSONPatchOperation) { + *out = *in + in.Value.DeepCopyInto(&out.Value) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONPatchOperation. +func (in *JSONPatchOperation) DeepCopy() *JSONPatchOperation { + if in == nil { + return nil + } + out := new(JSONPatchOperation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWT) DeepCopyInto(out *JWT) { + *out = *in + if in.Providers != nil { + in, out := &in.Providers, &out.Providers + *out = make([]JWTProvider, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWT. +func (in *JWT) DeepCopy() *JWT { + if in == nil { + return nil + } + out := new(JWT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTExtractor) DeepCopyInto(out *JWTExtractor) { + *out = *in + if in.Cookies != nil { + in, out := &in.Cookies, &out.Cookies + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTExtractor. +func (in *JWTExtractor) DeepCopy() *JWTExtractor { + if in == nil { + return nil + } + out := new(JWTExtractor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTProvider) DeepCopyInto(out *JWTProvider) { + *out = *in + if in.Audiences != nil { + in, out := &in.Audiences, &out.Audiences + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.RemoteJWKS = in.RemoteJWKS + if in.ClaimToHeaders != nil { + in, out := &in.ClaimToHeaders, &out.ClaimToHeaders + *out = make([]ClaimToHeader, len(*in)) + copy(*out, *in) + } + if in.ExtractFrom != nil { + in, out := &in.ExtractFrom, &out.ExtractFrom + *out = new(JWTExtractor) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProvider. +func (in *JWTProvider) DeepCopy() *JWTProvider { + if in == nil { + return nil + } + out := new(JWTProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesContainerSpec) DeepCopyInto(out *KubernetesContainerSpec) { + *out = *in + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]corev1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesContainerSpec. +func (in *KubernetesContainerSpec) DeepCopy() *KubernetesContainerSpec { + if in == nil { + return nil + } + out := new(KubernetesContainerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesDeployMode) DeepCopyInto(out *KubernetesDeployMode) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesDeployMode. +func (in *KubernetesDeployMode) DeepCopy() *KubernetesDeployMode { + if in == nil { + return nil + } + out := new(KubernetesDeployMode) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesDeploymentSpec) DeepCopyInto(out *KubernetesDeploymentSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Strategy != nil { + in, out := &in.Strategy, &out.Strategy + *out = new(appsv1.DeploymentStrategy) + (*in).DeepCopyInto(*out) + } + if in.Pod != nil { + in, out := &in.Pod, &out.Pod + *out = new(KubernetesPodSpec) + (*in).DeepCopyInto(*out) + } + if in.Container != nil { + in, out := &in.Container, &out.Container + *out = new(KubernetesContainerSpec) + (*in).DeepCopyInto(*out) + } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]corev1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesDeploymentSpec. +func (in *KubernetesDeploymentSpec) DeepCopy() *KubernetesDeploymentSpec { + if in == nil { + return nil + } + out := new(KubernetesDeploymentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesHorizontalPodAutoscalerSpec) DeepCopyInto(out *KubernetesHorizontalPodAutoscalerSpec) { + *out = *in + if in.MinReplicas != nil { + in, out := &in.MinReplicas, &out.MinReplicas + *out = new(int32) + **out = **in + } + if in.MaxReplicas != nil { + in, out := &in.MaxReplicas, &out.MaxReplicas + *out = new(int32) + **out = **in + } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = make([]v2.MetricSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Behavior != nil { + in, out := &in.Behavior, &out.Behavior + *out = new(v2.HorizontalPodAutoscalerBehavior) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesHorizontalPodAutoscalerSpec. +func (in *KubernetesHorizontalPodAutoscalerSpec) DeepCopy() *KubernetesHorizontalPodAutoscalerSpec { + if in == nil { + return nil + } + out := new(KubernetesHorizontalPodAutoscalerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesPodSpec) DeepCopyInto(out *KubernetesPodSpec) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(corev1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]corev1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]corev1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesPodSpec. +func (in *KubernetesPodSpec) DeepCopy() *KubernetesPodSpec { + if in == nil { + return nil + } + out := new(KubernetesPodSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesServiceSpec) DeepCopyInto(out *KubernetesServiceSpec) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(ServiceType) + **out = **in + } + if in.LoadBalancerClass != nil { + in, out := &in.LoadBalancerClass, &out.LoadBalancerClass + *out = new(string) + **out = **in + } + if in.AllocateLoadBalancerNodePorts != nil { + in, out := &in.AllocateLoadBalancerNodePorts, &out.AllocateLoadBalancerNodePorts + *out = new(bool) + **out = **in + } + if in.LoadBalancerIP != nil { + in, out := &in.LoadBalancerIP, &out.LoadBalancerIP + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesServiceSpec. +func (in *KubernetesServiceSpec) DeepCopy() *KubernetesServiceSpec { + if in == nil { + return nil + } + out := new(KubernetesServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesWatchMode) DeepCopyInto(out *KubernetesWatchMode) { + *out = *in + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NamespaceSelectors != nil { + in, out := &in.NamespaceSelectors, &out.NamespaceSelectors + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesWatchMode. +func (in *KubernetesWatchMode) DeepCopy() *KubernetesWatchMode { + if in == nil { + return nil + } + out := new(KubernetesWatchMode) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LiteralCustomTag) DeepCopyInto(out *LiteralCustomTag) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LiteralCustomTag. +func (in *LiteralCustomTag) DeepCopy() *LiteralCustomTag { + if in == nil { + return nil + } + out := new(LiteralCustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { + *out = *in + if in.ConsistentHash != nil { + in, out := &in.ConsistentHash, &out.ConsistentHash + *out = new(ConsistentHash) + **out = **in + } + if in.SlowStart != nil { + in, out := &in.SlowStart, &out.SlowStart + *out = new(SlowStart) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. +func (in *LoadBalancer) DeepCopy() *LoadBalancer { + if in == nil { + return nil + } + out := new(LoadBalancer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDC) DeepCopyInto(out *OIDC) { + *out = *in + in.Provider.DeepCopyInto(&out.Provider) + in.ClientSecret.DeepCopyInto(&out.ClientSecret) + if in.Scopes != nil { + in, out := &in.Scopes, &out.Scopes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDC. +func (in *OIDC) DeepCopy() *OIDC { + if in == nil { + return nil + } + out := new(OIDC) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProvider) DeepCopyInto(out *OIDCProvider) { + *out = *in + if in.AuthorizationEndpoint != nil { + in, out := &in.AuthorizationEndpoint, &out.AuthorizationEndpoint + *out = new(string) + **out = **in + } + if in.TokenEndpoint != nil { + in, out := &in.TokenEndpoint, &out.TokenEndpoint + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProvider. +func (in *OIDCProvider) DeepCopy() *OIDCProvider { + if in == nil { + return nil + } + out := new(OIDCProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryEnvoyProxyAccessLog) DeepCopyInto(out *OpenTelemetryEnvoyProxyAccessLog) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryEnvoyProxyAccessLog. +func (in *OpenTelemetryEnvoyProxyAccessLog) DeepCopy() *OpenTelemetryEnvoyProxyAccessLog { + if in == nil { + return nil + } + out := new(OpenTelemetryEnvoyProxyAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLog) DeepCopyInto(out *ProxyAccessLog) { + *out = *in + if in.Settings != nil { + in, out := &in.Settings, &out.Settings + *out = make([]ProxyAccessLogSetting, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLog. +func (in *ProxyAccessLog) DeepCopy() *ProxyAccessLog { + if in == nil { + return nil + } + out := new(ProxyAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLogFormat) DeepCopyInto(out *ProxyAccessLogFormat) { + *out = *in + if in.Text != nil { + in, out := &in.Text, &out.Text + *out = new(string) + **out = **in + } + if in.JSON != nil { + in, out := &in.JSON, &out.JSON + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogFormat. +func (in *ProxyAccessLogFormat) DeepCopy() *ProxyAccessLogFormat { + if in == nil { + return nil + } + out := new(ProxyAccessLogFormat) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLogSetting) DeepCopyInto(out *ProxyAccessLogSetting) { + *out = *in + in.Format.DeepCopyInto(&out.Format) + if in.Sinks != nil { + in, out := &in.Sinks, &out.Sinks + *out = make([]ProxyAccessLogSink, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogSetting. +func (in *ProxyAccessLogSetting) DeepCopy() *ProxyAccessLogSetting { + if in == nil { + return nil + } + out := new(ProxyAccessLogSetting) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLogSink) DeepCopyInto(out *ProxyAccessLogSink) { + *out = *in + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileEnvoyProxyAccessLog) + **out = **in + } + if in.OpenTelemetry != nil { + in, out := &in.OpenTelemetry, &out.OpenTelemetry + *out = new(OpenTelemetryEnvoyProxyAccessLog) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogSink. +func (in *ProxyAccessLogSink) DeepCopy() *ProxyAccessLogSink { + if in == nil { + return nil + } + out := new(ProxyAccessLogSink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyBootstrap) DeepCopyInto(out *ProxyBootstrap) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(BootstrapType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyBootstrap. +func (in *ProxyBootstrap) DeepCopy() *ProxyBootstrap { + if in == nil { + return nil + } + out := new(ProxyBootstrap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyLogging) DeepCopyInto(out *ProxyLogging) { + *out = *in + if in.Level != nil { + in, out := &in.Level, &out.Level + *out = make(map[ProxyLogComponent]LogLevel, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyLogging. +func (in *ProxyLogging) DeepCopy() *ProxyLogging { + if in == nil { + return nil + } + out := new(ProxyLogging) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyMetricSink) DeepCopyInto(out *ProxyMetricSink) { + *out = *in + if in.OpenTelemetry != nil { + in, out := &in.OpenTelemetry, &out.OpenTelemetry + *out = new(ProxyOpenTelemetrySink) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyMetricSink. +func (in *ProxyMetricSink) DeepCopy() *ProxyMetricSink { + if in == nil { + return nil + } + out := new(ProxyMetricSink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyMetrics) DeepCopyInto(out *ProxyMetrics) { + *out = *in + if in.Prometheus != nil { + in, out := &in.Prometheus, &out.Prometheus + *out = new(ProxyPrometheusProvider) + **out = **in + } + if in.Sinks != nil { + in, out := &in.Sinks, &out.Sinks + *out = make([]ProxyMetricSink, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyMetrics. +func (in *ProxyMetrics) DeepCopy() *ProxyMetrics { + if in == nil { + return nil + } + out := new(ProxyMetrics) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyOpenTelemetrySink) DeepCopyInto(out *ProxyOpenTelemetrySink) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyOpenTelemetrySink. +func (in *ProxyOpenTelemetrySink) DeepCopy() *ProxyOpenTelemetrySink { + if in == nil { + return nil + } + out := new(ProxyOpenTelemetrySink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyPrometheusProvider) DeepCopyInto(out *ProxyPrometheusProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyPrometheusProvider. +func (in *ProxyPrometheusProvider) DeepCopy() *ProxyPrometheusProvider { + if in == nil { + return nil + } + out := new(ProxyPrometheusProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyProtocol) DeepCopyInto(out *ProxyProtocol) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyProtocol. +func (in *ProxyProtocol) DeepCopy() *ProxyProtocol { + if in == nil { + return nil + } + out := new(ProxyProtocol) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyTelemetry) DeepCopyInto(out *ProxyTelemetry) { + *out = *in + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(ProxyAccessLog) + (*in).DeepCopyInto(*out) + } + if in.Tracing != nil { + in, out := &in.Tracing, &out.Tracing + *out = new(ProxyTracing) + (*in).DeepCopyInto(*out) + } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(ProxyMetrics) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyTelemetry. +func (in *ProxyTelemetry) DeepCopy() *ProxyTelemetry { + if in == nil { + return nil + } + out := new(ProxyTelemetry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyTracing) DeepCopyInto(out *ProxyTracing) { + *out = *in + if in.SamplingRate != nil { + in, out := &in.SamplingRate, &out.SamplingRate + *out = new(uint32) + **out = **in + } + if in.CustomTags != nil { + in, out := &in.CustomTags, &out.CustomTags + *out = make(map[string]CustomTag, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.Provider = in.Provider +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyTracing. +func (in *ProxyTracing) DeepCopy() *ProxyTracing { + if in == nil { + return nil + } + out := new(ProxyTracing) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimit) DeepCopyInto(out *RateLimit) { + *out = *in + in.Backend.DeepCopyInto(&out.Backend) + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimit. +func (in *RateLimit) DeepCopy() *RateLimit { + if in == nil { + return nil + } + out := new(RateLimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitDatabaseBackend) DeepCopyInto(out *RateLimitDatabaseBackend) { + *out = *in + if in.Redis != nil { + in, out := &in.Redis, &out.Redis + *out = new(RateLimitRedisSettings) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitDatabaseBackend. +func (in *RateLimitDatabaseBackend) DeepCopy() *RateLimitDatabaseBackend { + if in == nil { + return nil + } + out := new(RateLimitDatabaseBackend) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitRedisSettings) DeepCopyInto(out *RateLimitRedisSettings) { + *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(RedisTLSSettings) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRedisSettings. +func (in *RateLimitRedisSettings) DeepCopy() *RateLimitRedisSettings { + if in == nil { + return nil + } + out := new(RateLimitRedisSettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) { + *out = *in + if in.ClientSelectors != nil { + in, out := &in.ClientSelectors, &out.ClientSelectors + *out = make([]RateLimitSelectCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.Limit = in.Limit +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRule. +func (in *RateLimitRule) DeepCopy() *RateLimitRule { + if in == nil { + return nil + } + out := new(RateLimitRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitSelectCondition) DeepCopyInto(out *RateLimitSelectCondition) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make([]HeaderMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SourceCIDR != nil { + in, out := &in.SourceCIDR, &out.SourceCIDR + *out = new(SourceMatch) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitSelectCondition. +func (in *RateLimitSelectCondition) DeepCopy() *RateLimitSelectCondition { + if in == nil { + return nil + } + out := new(RateLimitSelectCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitSpec) DeepCopyInto(out *RateLimitSpec) { + *out = *in + if in.Global != nil { + in, out := &in.Global, &out.Global + *out = new(GlobalRateLimit) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitSpec. +func (in *RateLimitSpec) DeepCopy() *RateLimitSpec { + if in == nil { + return nil + } + out := new(RateLimitSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitValue) DeepCopyInto(out *RateLimitValue) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitValue. +func (in *RateLimitValue) DeepCopy() *RateLimitValue { + if in == nil { + return nil + } + out := new(RateLimitValue) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisTLSSettings) DeepCopyInto(out *RedisTLSSettings) { + *out = *in + if in.CertificateRef != nil { + in, out := &in.CertificateRef, &out.CertificateRef + *out = new(apisv1.SecretObjectReference) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisTLSSettings. +func (in *RedisTLSSettings) DeepCopy() *RedisTLSSettings { + if in == nil { + return nil + } + out := new(RedisTLSSettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteJWKS) DeepCopyInto(out *RemoteJWKS) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteJWKS. +func (in *RemoteJWKS) DeepCopy() *RemoteJWKS { + if in == nil { + return nil + } + out := new(RemoteJWKS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestHeaderCustomTag) DeepCopyInto(out *RequestHeaderCustomTag) { + *out = *in + if in.DefaultValue != nil { + in, out := &in.DefaultValue, &out.DefaultValue + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestHeaderCustomTag. +func (in *RequestHeaderCustomTag) DeepCopy() *RequestHeaderCustomTag { + if in == nil { + return nil + } + out := new(RequestHeaderCustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityPolicy) DeepCopyInto(out *SecurityPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityPolicy. +func (in *SecurityPolicy) DeepCopy() *SecurityPolicy { + if in == nil { + return nil + } + out := new(SecurityPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SecurityPolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityPolicyList) DeepCopyInto(out *SecurityPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SecurityPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityPolicyList. +func (in *SecurityPolicyList) DeepCopy() *SecurityPolicyList { + if in == nil { + return nil + } + out := new(SecurityPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SecurityPolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityPolicySpec) DeepCopyInto(out *SecurityPolicySpec) { + *out = *in + in.TargetRef.DeepCopyInto(&out.TargetRef) + if in.CORS != nil { + in, out := &in.CORS, &out.CORS + *out = new(CORS) + (*in).DeepCopyInto(*out) + } + if in.BasicAuth != nil { + in, out := &in.BasicAuth, &out.BasicAuth + *out = new(BasicAuth) + (*in).DeepCopyInto(*out) + } + if in.JWT != nil { + in, out := &in.JWT, &out.JWT + *out = new(JWT) + (*in).DeepCopyInto(*out) + } + if in.OIDC != nil { + in, out := &in.OIDC, &out.OIDC + *out = new(OIDC) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityPolicySpec. +func (in *SecurityPolicySpec) DeepCopy() *SecurityPolicySpec { + if in == nil { + return nil + } + out := new(SecurityPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityPolicyStatus) DeepCopyInto(out *SecurityPolicyStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityPolicyStatus. +func (in *SecurityPolicyStatus) DeepCopy() *SecurityPolicyStatus { + if in == nil { + return nil + } + out := new(SecurityPolicyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlowStart) DeepCopyInto(out *SlowStart) { + *out = *in + if in.Window != nil { + in, out := &in.Window, &out.Window + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowStart. +func (in *SlowStart) DeepCopy() *SlowStart { + if in == nil { + return nil + } + out := new(SlowStart) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceMatch) DeepCopyInto(out *SourceMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(SourceMatchType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceMatch. +func (in *SourceMatch) DeepCopy() *SourceMatch { + if in == nil { + return nil + } + out := new(SourceMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StringMatch) DeepCopyInto(out *StringMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(StringMatchType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. +func (in *StringMatch) DeepCopy() *StringMatch { + if in == nil { + return nil + } + out := new(StringMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPKeepalive) DeepCopyInto(out *TCPKeepalive) { + *out = *in + if in.Probes != nil { + in, out := &in.Probes, &out.Probes + *out = new(uint32) + **out = **in + } + if in.IdleTime != nil { + in, out := &in.IdleTime, &out.IdleTime + *out = new(apisv1.Duration) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(apisv1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPKeepalive. +func (in *TCPKeepalive) DeepCopy() *TCPKeepalive { + if in == nil { + return nil + } + out := new(TCPKeepalive) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TracingProvider) DeepCopyInto(out *TracingProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TracingProvider. +func (in *TracingProvider) DeepCopy() *TracingProvider { + if in == nil { + return nil + } + out := new(TracingProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XDSTranslatorHooks) DeepCopyInto(out *XDSTranslatorHooks) { + *out = *in + if in.Pre != nil { + in, out := &in.Pre, &out.Pre + *out = make([]XDSTranslatorHook, len(*in)) + copy(*out, *in) + } + if in.Post != nil { + in, out := &in.Post, &out.Post + *out = make([]XDSTranslatorHook, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XDSTranslatorHooks. +func (in *XDSTranslatorHooks) DeepCopy() *XDSTranslatorHooks { + if in == nil { + return nil + } + out := new(XDSTranslatorHooks) in.DeepCopyInto(out) return out } diff --git a/charts/gateway-helm/Chart.yaml b/charts/gateway-helm/Chart.yaml index 81c232c8e5c..f29f1384f01 100755 --- a/charts/gateway-helm/Chart.yaml +++ b/charts/gateway-helm/Chart.yaml @@ -5,6 +5,7 @@ type: application version: v0.0.0-latest appVersion: "latest" +icon: https://raw.githubusercontent.com/envoyproxy/gateway/main/site/assets/icons/logo.svg maintainers: - name: envoy-gateway-steering-committee diff --git a/charts/gateway-helm/README.md b/charts/gateway-helm/README.md index 01e32f4dbe0..f5f2b420d21 100644 --- a/charts/gateway-helm/README.md +++ b/charts/gateway-helm/README.md @@ -69,6 +69,7 @@ To uninstall the chart: | deployment.envoyGateway.resources.requests.memory | string | `"64Mi"` | | | deployment.kubeRbacProxy.image.repository | string | `"gcr.io/kubebuilder/kube-rbac-proxy"` | | | deployment.kubeRbacProxy.image.tag | string | `"v0.11.0"` | | +| deployment.kubeRbacProxy.imagePullPolicy | string | `"IfNotPresent"` | | | deployment.kubeRbacProxy.resources.limits.cpu | string | `"500m"` | | | deployment.kubeRbacProxy.resources.limits.memory | string | `"128Mi"` | | | deployment.kubeRbacProxy.resources.requests.cpu | string | `"5m"` | | diff --git a/charts/gateway-helm/crds/gatewayapi-crds.yaml b/charts/gateway-helm/crds/gatewayapi-crds.yaml index 779565a11b5..bbb71f11f65 100644 --- a/charts/gateway-helm/crds/gatewayapi-crds.yaml +++ b/charts/gateway-helm/crds/gatewayapi-crds.yaml @@ -17,14 +17,499 @@ # --- # +# config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466 + gateway.networking.k8s.io/bundle-version: v1.0.0 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + gateway.networking.k8s.io/policy: Direct + name: backendtlspolicies.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: BackendTLSPolicy + listKind: BackendTLSPolicyList + plural: backendtlspolicies + shortNames: + - btlspolicy + singular: backendtlspolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: BackendTLSPolicy provides a way to configure how a Gateway connects + to a Backend via TLS. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendTLSPolicy. + properties: + targetRef: + description: "TargetRef identifies an API object to apply the policy + to. Only Services have Extended support. Implementations MAY support + additional objects, with Implementation Specific support. Note that + this config applies to the entire referenced resource by default, + but this default may change in the future to provide a more granular + application of the policy. \n Support: Extended for Kubernetes Service + \n Support: Implementation-specific for any other resource" + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When + unspecified, the local namespace is inferred. Even when policy + targets a resource in a different namespace, it MUST only apply + to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + description: "SectionName is the name of a section within the + target resource. When unspecified, this targetRef targets the + entire resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name * + Service: Port Name \n If a SectionName is specified, but does + not exist on the targeted object, the Policy must fail to attach, + and the policy implementation should record a `ResolvedRefs` + or similar Condition in the Policy's status." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - group + - kind + - name + type: object + tls: + description: TLS contains backend TLS policy configuration. + properties: + caCertRefs: + description: "CACertRefs contains one or more references to Kubernetes + objects that contain a PEM-encoded TLS CA certificate bundle, + which is used to validate a TLS handshake between the Gateway + and backend Pod. \n If CACertRefs is empty or unspecified, then + WellKnownCACerts must be specified. Only one of CACertRefs or + WellKnownCACerts may be specified, not both. If CACertRefs is + empty or unspecified, the configuration for WellKnownCACerts + MUST be honored instead. \n References to a resource in a different + namespace are invalid for the moment, although we will revisit + this in the future. \n A single CACertRef to a Kubernetes ConfigMap + kind has \"Core\" support. Implementations MAY choose to support + attaching multiple certificates to a backend, but this behavior + is implementation-specific. \n Support: Core - An optional single + reference to a Kubernetes ConfigMap, with the CA certificate + in a key named `ca.crt`. \n Support: Implementation-specific + (More than one reference, or other kinds of resources)." + items: + description: "LocalObjectReference identifies an API object + within the namespace of the referrer. The API object must + be valid in the cluster; the Group and Kind must be registered + in the cluster for this reference to be valid. \n References + to objects with invalid Group and Kind are not valid, and + must be rejected by the implementation, with appropriate Conditions + set on the containing object." + properties: + group: + description: Group is the group of the referent. For example, + "gateway.networking.k8s.io". When unspecified or empty + string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" + or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + type: array + hostname: + description: "Hostname is used for two purposes in the connection + between Gateways and backends: \n 1. Hostname MUST be used as + the SNI to connect to the backend (RFC 6066). 2. Hostname MUST + be used for authentication and MUST match the certificate served + by the matching backend. \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 + wellKnownCACerts: + description: "WellKnownCACerts specifies whether system CA certificates + may be used in the TLS handshake between the gateway and backend + pod. \n If WellKnownCACerts is unspecified or empty (\"\"), + then CACertRefs must be specified with at least one entry for + a valid configuration. Only one of CACertRefs or WellKnownCACerts + may be specified, not both. \n Support: Core for \"System\"" + enum: + - System + type: string + required: + - hostname + type: object + x-kubernetes-validations: + - message: must not contain both CACertRefs and WellKnownCACerts + rule: '!(has(self.caCertRefs) && size(self.caCertRefs) > 0 && has(self.wellKnownCACerts) + && self.wellKnownCACerts != "")' + - message: must specify either CACertRefs or WellKnownCACerts + rule: (has(self.caCertRefs) && size(self.caCertRefs) > 0 || has(self.wellKnownCACerts) + && self.wellKnownCACerts != "") + required: + - targetRef + - tls + type: object + status: + description: Status defines the current state of BackendTLSPolicy. + properties: + ancestors: + description: "Ancestors is a list of ancestor resources (usually Gateways) + that are associated with the policy, and the status of the policy + with respect to each ancestor. When this policy attaches to a parent, + the controller that manages the parent and the ancestors MUST add + an entry to this list when the controller first sees the policy + and SHOULD update the entry as appropriate when the relevant ancestor + is modified. \n Note that choosing the relevant ancestor is left + to the Policy designers; an important part of Policy design is designing + the right object level at which to namespace this status. \n Note + also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations + MUST use the ControllerName field to uniquely identify the entries + in this list that they are responsible for. \n Note that to achieve + this, the list of PolicyAncestorStatus structs MUST be treated as + a map with a composite key, made up of the AncestorRef and ControllerName + fields combined. \n A maximum of 16 ancestors will be represented + in this list. An empty list means the Policy is not relevant for + any ancestors. \n If this slice is full, implementations MUST NOT + add further entries. Instead they MUST consider the policy unimplementable + and signal that on any related resources such as the ancestor that + would be referenced here. For example, if this list was full on + BackendTLSPolicy, no additional Gateways would be able to reference + the Service targeted by the BackendTLSPolicy." + items: + description: "PolicyAncestorStatus describes the status of a route + with respect to an associated Ancestor. \n Ancestors refer to + objects that are either the Target of a policy or above it in + terms of object hierarchy. For example, if a policy targets a + Service, the Policy's Ancestors are, in order, the Service, the + HTTPRoute, the Gateway, and the GatewayClass. Almost always, in + this hierarchy, the Gateway will be the most useful object to + place Policy status on, so we recommend that implementations SHOULD + use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. \n In the context of policy + attachment, the Ancestor is used to distinguish which resource + results in a distinct application of this policy. For example, + if a policy targets a Service, it may have a distinct result per + attached Gateway. \n Policies targeting the same resource may + have different effects depending on the ancestors of those resources. + For example, different Gateways targeting the same Service may + have different capabilities, especially if they have different + underlying implementations. \n For example, in BackendTLSPolicy, + the Policy attaches to a Service that is used as a backend in + a HTTPRoute that is itself attached to a Gateway. In this case, + the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. \n Note that a parent + is also an ancestor, so for objects where the parent is the relevant + object for status, this struct SHOULD still be used. \n This struct + is intended to be used in a slice that's effectively a map, with + a composite key made up of the AncestorRef and the ControllerName." + properties: + ancestorRef: + description: AncestorRef corresponds with a ParentRef in the + spec that this PolicyAncestorStatus struct describes the status + of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the + core API group (such as for a \"Service\" kind referent), + Group must be explicitly set to \"\" (empty string). \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 + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- 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 + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# # config/crd/experimental/gateway.networking.k8s.io_gatewayclasses.yaml # apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -55,10 +540,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 @@ -99,6 +581,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 @@ -153,7 +638,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: @@ -236,6 +723,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 @@ -299,6 +815,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 @@ -353,7 +872,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: @@ -436,6 +957,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 @@ -458,8 +1008,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -489,10 +1039,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 @@ -522,17 +1069,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 @@ -551,40 +1110,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. @@ -657,7 +1330,7 @@ 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 @@ -833,14 +1506,14 @@ 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 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" + 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. + \n Support: Core" maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ @@ -888,6 +1561,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 @@ -899,6 +1577,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 @@ -919,13 +1615,29 @@ spec: 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 @@ -944,6 +1656,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: @@ -1042,8 +1759,23 @@ spec: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: - description: AttachedRoutes represents the total number of accepted + 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: @@ -1225,17 +1957,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 @@ -1254,40 +1998,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. @@ -1360,7 +2218,7 @@ 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 @@ -1536,14 +2394,14 @@ 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 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" + 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. + \n Support: Core" maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ @@ -1591,6 +2449,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 @@ -1602,6 +2465,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 @@ -1622,13 +2503,29 @@ spec: 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 @@ -1647,6 +2544,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: @@ -1745,8 +2647,23 @@ spec: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: - description: AttachedRoutes represents the total number of accepted + 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: @@ -1899,8 +2816,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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: grpcroutes.gateway.networking.k8s.io @@ -1945,7 +2862,7 @@ spec: 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. \n Support: Extended" + HTTP/1, i.e. without prior knowledge." properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -2024,30 +2941,58 @@ spec: 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 @@ -2061,8 +3006,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])?$ @@ -2082,7 +3030,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])?$ @@ -2097,18 +3053,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 @@ -2119,19 +3079,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])?)*$ @@ -2141,11 +3105,30 @@ 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: - default: - - matches: - - method: - type: Exact description: Rules are a list of GRPC matchers, filters and actions. items: description: GRPCRouteRule defines the semantics for matching a @@ -2173,8 +3156,25 @@ spec: Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" items: - description: GRPCBackendRef defines how a GRPCRoute forwards - a gRPC request. + 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 @@ -2198,7 +3198,9 @@ spec: 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" + 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. @@ -2288,6 +3290,7 @@ spec: 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 @@ -2336,29 +3339,35 @@ spec: 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 Support: Extended" + 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 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" + 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: "" @@ -2425,6 +3434,10 @@ 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' required: - backendRef type: object @@ -2491,6 +3504,7 @@ spec: 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 @@ -2571,8 +3585,45 @@ spec: 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, @@ -2648,6 +3699,10 @@ 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 type: array filters: @@ -2660,8 +3715,13 @@ spec: all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. - \n Specifying a core filter multiple times has unspecified - or implementation-specific conformance. Support: Core" + \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. @@ -2677,7 +3737,8 @@ spec: 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" + 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 @@ -2762,6 +3823,7 @@ spec: 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 @@ -2807,27 +3869,33 @@ spec: 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 Support: Extended" + 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 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" + 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: "" @@ -2889,6 +3957,10 @@ 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' required: - backendRef type: object @@ -2951,6 +4023,7 @@ spec: 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 @@ -3025,8 +4098,44 @@ spec: 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, @@ -3140,6 +4249,21 @@ spec: - 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 @@ -3292,9 +4416,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])?$ @@ -3314,7 +4440,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])?$ @@ -3330,8 +4465,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 @@ -3342,7 +4481,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 @@ -3353,8 +4492,13 @@ 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 @@ -3402,8 +4546,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -3425,10 +4569,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - deprecated: true - deprecationWarning: The v1alpha2 version of HTTPRoute 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: HTTPRoute provides a way to route HTTP requests. This includes @@ -3452,15 +4593,17 @@ spec: description: Spec defines the desired state of HTTPRoute. properties: hostnames: - description: "Hostnames defines a set of hostname that should match + 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. \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 + 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 @@ -3512,30 +4655,58 @@ spec: 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 @@ -3549,8 +4720,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])?$ @@ -3570,7 +4744,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])?$ @@ -3585,18 +4767,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 @@ -3607,19 +4793,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])?)*$ @@ -3629,6 +4819,29 @@ 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: default: - matches: @@ -3662,8 +4875,25 @@ spec: for Kubernetes ServiceImport \n Support: Implementation-specific for any other resource \n Support for weight: Core" items: - description: HTTPBackendRef defines how a HTTPRoute should - forward an HTTP request. + 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 @@ -3687,7 +4917,8 @@ spec: 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" + 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. @@ -3777,6 +5008,7 @@ spec: 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 @@ -3825,29 +5057,35 @@ spec: 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 Support: Extended" + 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 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" + 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: "" @@ -3914,6 +5152,10 @@ 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' required: - backendRef type: object @@ -3950,8 +5192,9 @@ spec: 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\" would - be modified to \"/bar\". \n Note that + 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 @@ -3959,7 +5202,29 @@ spec: 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." + `/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: @@ -3980,6 +5245,23 @@ spec: 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 @@ -4103,6 +5385,7 @@ spec: 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 @@ -4215,8 +5498,9 @@ spec: 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\" would - be modified to \"/bar\". \n Note that + 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 @@ -4224,7 +5508,29 @@ spec: 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." + `/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: @@ -4245,12 +5551,94 @@ spec: 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, @@ -4326,6 +5714,10 @@ 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 type: array filters: @@ -4338,14 +5730,15 @@ spec: all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying - a core filter multiple times has unspecified or implementation-specific - conformance. \n All filters are expected to be compatible - with each other except for the URLRewrite and RequestRedirect + 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 all cases where incompatible - or unsupported filters are specified, implementations MUST - add a warning condition to status. \n Support: Core" + 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. @@ -4361,7 +5754,8 @@ spec: 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" + 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 @@ -4446,6 +5840,7 @@ spec: 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 @@ -4491,27 +5886,33 @@ spec: 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 Support: Extended" + 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 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" + 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: "" @@ -4573,6 +5974,10 @@ 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' required: - backendRef type: object @@ -4608,7 +6013,8 @@ spec: 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\" would be modified to \"/bar\". + 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 @@ -4616,7 +6022,26 @@ spec: 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." + `/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: @@ -4635,6 +6060,23 @@ spec: 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 @@ -4750,6 +6192,7 @@ spec: 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 @@ -4855,7 +6298,8 @@ spec: 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\" would be modified to \"/bar\". + 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 @@ -4863,7 +6307,26 @@ spec: 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." + `/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: @@ -4882,12 +6345,86 @@ spec: 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: @@ -5031,6 +6568,52 @@ spec: 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, @@ -5096,7 +6679,89 @@ spec: 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 @@ -5245,9 +6910,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])?$ @@ -5267,7 +6934,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])?$ @@ -5283,8 +6959,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 @@ -5295,7 +6975,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 @@ -5306,8 +6986,13 @@ 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 @@ -5374,15 +7059,17 @@ spec: description: Spec defines the desired state of HTTPRoute. properties: hostnames: - description: "Hostnames defines a set of hostname that should match + 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. \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 + 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 @@ -5434,30 +7121,58 @@ spec: 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 @@ -5471,8 +7186,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])?$ @@ -5492,7 +7210,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])?$ @@ -5507,18 +7233,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 @@ -5529,19 +7259,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])?)*$ @@ -5551,6 +7285,29 @@ 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: default: - matches: @@ -5584,8 +7341,25 @@ spec: for Kubernetes ServiceImport \n Support: Implementation-specific for any other resource \n Support for weight: Core" items: - description: HTTPBackendRef defines how a HTTPRoute should - forward an HTTP request. + 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 @@ -5609,7 +7383,8 @@ spec: 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" + 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. @@ -5699,6 +7474,7 @@ spec: 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 @@ -5747,29 +7523,35 @@ spec: 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 Support: Extended" + 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 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" + 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: "" @@ -5836,6 +7618,10 @@ 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' required: - backendRef type: object @@ -5872,8 +7658,9 @@ spec: 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\" would - be modified to \"/bar\". \n Note that + 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 @@ -5881,7 +7668,29 @@ spec: 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." + `/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: @@ -5902,6 +7711,23 @@ spec: 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 @@ -6025,6 +7851,7 @@ spec: 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 @@ -6137,8 +7964,9 @@ spec: 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\" would - be modified to \"/bar\". \n Note that + 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 @@ -6146,7 +7974,29 @@ spec: 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." + `/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: @@ -6167,12 +8017,94 @@ spec: 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, @@ -6248,6 +8180,10 @@ 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 type: array filters: @@ -6260,14 +8196,15 @@ spec: all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying - a core filter multiple times has unspecified or implementation-specific - conformance. \n All filters are expected to be compatible - with each other except for the URLRewrite and RequestRedirect + 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 all cases where incompatible - or unsupported filters are specified, implementations MUST - add a warning condition to status. \n Support: Core" + 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. @@ -6283,7 +8220,8 @@ spec: 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" + 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 @@ -6368,6 +8306,7 @@ spec: 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 @@ -6413,27 +8352,33 @@ spec: 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 Support: Extended" + 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 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" + 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: "" @@ -6495,6 +8440,10 @@ 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' required: - backendRef type: object @@ -6530,7 +8479,8 @@ spec: 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\" would be modified to \"/bar\". + 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 @@ -6538,7 +8488,26 @@ spec: 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." + `/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: @@ -6557,6 +8526,23 @@ spec: 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 @@ -6672,6 +8658,7 @@ spec: 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 @@ -6777,7 +8764,8 @@ spec: 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\" would be modified to \"/bar\". + 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 @@ -6785,7 +8773,26 @@ spec: 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." + `/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: @@ -6804,12 +8811,86 @@ spec: 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: @@ -6953,6 +9034,52 @@ spec: 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, @@ -7018,7 +9145,89 @@ spec: 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 @@ -7167,9 +9376,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])?$ @@ -7189,7 +9400,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])?$ @@ -7205,8 +9425,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 @@ -7217,7 +9441,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 @@ -7228,8 +9452,13 @@ 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 @@ -7279,8 +9508,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -7301,6 +9530,9 @@ spec: - 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: @@ -7309,13 +9541,17 @@ spec: 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. \n Support: Core" + 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 @@ -7420,7 +9656,7 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp @@ -7440,7 +9676,7 @@ spec: 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. \n Support: Core" + access that the grant allowed." properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -7545,7 +9781,7 @@ spec: type: object type: object served: true - storage: false + storage: true subresources: {} status: acceptedNames: @@ -7561,8 +9797,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -7608,30 +9844,58 @@ spec: 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 @@ -7645,8 +9909,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])?$ @@ -7666,7 +9933,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])?$ @@ -7681,18 +9956,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 @@ -7703,19 +9982,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])?)*$ @@ -7725,6 +10008,29 @@ 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 TCP matchers and actions. items: @@ -7747,7 +10053,24 @@ spec: 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." + 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: "" @@ -7824,6 +10147,10 @@ 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 @@ -7979,9 +10306,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])?$ @@ -8001,7 +10330,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])?$ @@ -8017,8 +10355,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 @@ -8029,7 +10371,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 @@ -8040,8 +10382,13 @@ 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 @@ -8091,8 +10438,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -8184,30 +10531,58 @@ spec: 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 @@ -8221,8 +10596,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])?$ @@ -8242,7 +10620,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])?$ @@ -8257,18 +10643,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 @@ -8279,19 +10669,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])?)*$ @@ -8301,6 +10695,29 @@ 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 TLS matchers and actions. items: @@ -8326,7 +10743,24 @@ spec: 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." + 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: "" @@ -8403,6 +10837,10 @@ 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 @@ -8558,9 +10996,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])?$ @@ -8580,7 +11020,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])?$ @@ -8596,8 +11045,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 @@ -8608,7 +11061,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 @@ -8619,8 +11072,13 @@ 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 @@ -8670,8 +11128,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 - gateway.networking.k8s.io/bundle-version: v0.7.1 + 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 @@ -8717,30 +11175,58 @@ spec: 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 @@ -8754,8 +11240,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])?$ @@ -8775,7 +11264,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])?$ @@ -8790,18 +11287,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 @@ -8812,19 +11313,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])?)*$ @@ -8834,6 +11339,29 @@ 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. items: @@ -8856,7 +11384,24 @@ spec: 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." + 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: "" @@ -8933,6 +11478,10 @@ 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 @@ -9088,9 +11637,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])?$ @@ -9110,7 +11661,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])?$ @@ -9126,8 +11686,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 @@ -9138,7 +11702,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 @@ -9149,8 +11713,13 @@ 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 @@ -9192,249 +11761,3 @@ status: plural: "" conditions: null storedVersions: null ---- -# -# config/webhook/0-namespace.yaml -# -apiVersion: v1 -kind: Namespace -metadata: - name: gateway-system ---- -# -# config/webhook/admission_webhook.yaml -# -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: gateway-api-admission -webhooks: -- name: validate.gateway.networking.k8s.io - matchPolicy: Equivalent - rules: - - operations: [ "CREATE" , "UPDATE" ] - apiGroups: [ "gateway.networking.k8s.io" ] - apiVersions: [ "v1alpha2", "v1beta1" ] - resources: [ "gateways", "gatewayclasses", "httproutes" ] - failurePolicy: Fail - sideEffects: None - admissionReviewVersions: - - v1 - clientConfig: - service: - name: gateway-api-admission-server - namespace: gateway-system - path: "/validate" ---- -apiVersion: v1 -kind: Service -metadata: - labels: - name: gateway-api-webhook-server - name: gateway-api-admission-server - namespace: gateway-system -spec: - type: ClusterIP - ports: - - name: https-webhook - port: 443 - targetPort: 8443 - selector: - name: gateway-api-admission-server ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gateway-api-admission-server - namespace: gateway-system - labels: - name: gateway-api-admission-server -spec: - replicas: 1 - selector: - matchLabels: - name: gateway-api-admission-server - template: - metadata: - name: gateway-api-admission-server - labels: - name: gateway-api-admission-server - spec: - containers: - - name: webhook - image: registry.k8s.io/gateway-api/admission-server:v0.7.1 - imagePullPolicy: Always - args: - - -logtostderr - - --tlsCertFile=/etc/certs/cert - - --tlsKeyFile=/etc/certs/key - - -v=10 - - 2>&1 - ports: - - containerPort: 8443 - name: webhook - resources: - limits: - memory: 50Mi - cpu: 100m - requests: - memory: 50Mi - cpu: 100m - volumeMounts: - - name: webhook-certs - mountPath: /etc/certs - readOnly: true - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: webhook-certs - secret: - secretName: gateway-api-admission ---- -# -# config/webhook/certificate_config.yaml -# -apiVersion: v1 -kind: ServiceAccount -metadata: - name: gateway-api-admission - labels: - name: gateway-api-webhook - namespace: gateway-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: gateway-api-admission - labels: - name: gateway-api -rules: -- apiGroups: - - admissionregistration.k8s.io - resources: - - validatingwebhookconfigurations - verbs: - - get - - update ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: gateway-api-admission - annotations: - labels: - name: gateway-api-webhook -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: gateway-api-admission -subjects: -- kind: ServiceAccount - name: gateway-api-admission - namespace: gateway-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: gateway-api-admission - annotations: - labels: - name: gateway-api-webhook - namespace: gateway-system -rules: -- apiGroups: - - '' - resources: - - secrets - verbs: - - get - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: gateway-api-admission - annotations: - labels: - name: gateway-api-webhook - namespace: gateway-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: gateway-api-admission -subjects: -- kind: ServiceAccount - name: gateway-api-admission - namespace: gateway-system ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: gateway-api-admission - annotations: - labels: - name: gateway-api-webhook - namespace: gateway-system -spec: - template: - metadata: - name: gateway-api-admission-create - labels: - name: gateway-api-webhook - spec: - containers: - - name: create - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.1.1 - imagePullPolicy: IfNotPresent - args: - - create - - --host=gateway-api-admission-server,gateway-api-admission-server.gateway-system.svc - - --namespace=gateway-system - - --secret-name=gateway-api-admission - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - restartPolicy: OnFailure - serviceAccountName: gateway-api-admission - securityContext: - runAsNonRoot: true - runAsUser: 2000 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: gateway-api-admission-patch - labels: - name: gateway-api-webhook - namespace: gateway-system -spec: - template: - metadata: - name: gateway-api-admission-patch - labels: - name: gateway-api-webhook - spec: - containers: - - name: patch - image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.1.1 - imagePullPolicy: IfNotPresent - args: - - patch - - --webhook-name=gateway-api-admission - - --namespace=gateway-system - - --patch-mutating=false - - --patch-validating=true - - --secret-name=gateway-api-admission - - --patch-failure-policy=Fail - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - restartPolicy: OnFailure - serviceAccountName: gateway-api-admission - securityContext: - runAsNonRoot: true - runAsUser: 2000 diff --git a/charts/gateway-helm/crds/generated/config.gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/config.gateway.envoyproxy.io_envoyproxies.yaml deleted file mode 100644 index 2107e48ca00..00000000000 --- a/charts/gateway-helm/crds/generated/config.gateway.envoyproxy.io_envoyproxies.yaml +++ /dev/null @@ -1,4075 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null - name: envoyproxies.config.gateway.envoyproxy.io -spec: - group: config.gateway.envoyproxy.io - names: - kind: EnvoyProxy - listKind: EnvoyProxyList - plural: envoyproxies - singular: envoyproxy - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: EnvoyProxy is the schema for the envoyproxies 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: EnvoyProxySpec defines the desired state of EnvoyProxy. - properties: - bootstrap: - description: Bootstrap defines the Envoy Bootstrap as a YAML string. - Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap - to learn more about the syntax. If set, this is the Bootstrap configuration - used for the managed Envoy Proxy fleet instead of the default Bootstrap - configuration set by Envoy Gateway. Some fields within the Bootstrap - that are required to communicate with the xDS Server (Envoy Gateway) - and receive xDS resources from it are not configurable and will - result in the `EnvoyProxy` resource being rejected. Backward compatibility - across minor versions is not guaranteed. We strongly recommend using - `egctl x translate` to generate a `EnvoyProxy` resource with the - `Bootstrap` field set to the default Bootstrap configuration used. - You can edit this configuration, and rerun `egctl x translate` to - ensure there are no validation errors. - type: string - logging: - default: - level: - default: warn - description: Logging defines logging parameters for managed proxies. - properties: - level: - additionalProperties: - description: LogLevel defines a log level for Envoy Gateway - and EnvoyProxy system logs. This type is not implemented for - EnvoyProxy until https://github.com/envoyproxy/gateway/issues/280 - is fixed. - enum: - - debug - - info - - error - - warn - type: string - default: - default: warn - description: 'Level is a map of logging level per component, where - the component is the key and the log level is the value. If - unspecified, defaults to "default: warn".' - type: object - type: object - provider: - description: Provider defines the desired resource provider and provider-specific - configuration. If unspecified, the "Kubernetes" resource provider - is used with default configuration parameters. - properties: - kubernetes: - description: Kubernetes defines the desired state of the Kubernetes - resource provider. Kubernetes provides infrastructure resources - for running the data plane, e.g. Envoy proxy. If unspecified - and type is "Kubernetes", default settings for managed Kubernetes - resources are applied. - properties: - envoyDeployment: - description: EnvoyDeployment defines the desired state of - the Envoy deployment resource. If unspecified, default settings - for the manged Envoy deployment resource are applied. - properties: - container: - description: Container defines the resources and securityContext - of container. - properties: - env: - description: List of environment variables to set - in the 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 - image: - description: Image specifies the EnvoyProxy container - image to be used, instead of the default image. - type: string - resources: - description: 'Resources required by this container. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field - and requires enabling the DynamicResourceAllocation - feature gate. \n 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 defines the security - options the container should be run with. If set, - the fields of SecurityContext override the equivalent - fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - 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 only be set - if type is "Localhost". - type: string - type: - description: "type indicates which kind of - seccomp profile will be applied. Valid options - are: \n 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. - This field is alpha-level and will only - be honored by components that enable the - WindowsHostProcessContainers feature flag. - Setting this field without the feature flag - will result in errors when validating the - Pod. 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 - volumeMounts: - description: VolumeMounts are volumes to mount into - the container's filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting of - a Volume within a container. - properties: - mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. - type: string - mountPropagation: - description: mountPropagation determines how - mounts are propagated from the host to container - and the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults - to false. - type: boolean - subPath: - description: Path within the volume from which - the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume - from which the container's volume should be - mounted. Behaves similarly to SubPath but - environment variable references $(VAR_NAME) - are expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - type: object - pod: - description: Pod defines the desired annotations and securityContext - of container. - properties: - affinity: - description: If specified, the pod's 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. - 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 - 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. - 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 - 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. - 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 - 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. - 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 - 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: Annotations are the annotations that - should be appended to the pods. By default, no pod - annotations are appended. - type: object - labels: - additionalProperties: - type: string - description: Labels are the additional labels that - should be tagged to the pods. By default, no additional - pod labels are tagged. - type: object - securityContext: - description: 'SecurityContext holds pod-level security - attributes and common container settings. Optional: - Defaults to empty. See type description for default - values of each field.' - 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: \n 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---- \n 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 only be set - if type is "Localhost". - type: string - type: - description: "type indicates which kind of - seccomp profile will be applied. Valid options - are: \n 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. - This field is alpha-level and will only - be honored by components that enable the - WindowsHostProcessContainers feature flag. - Setting this field without the feature flag - will result in errors when validating the - Pod. 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 - 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 - volumes: - description: 'Volumes that can be mounted by containers - belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' - items: - description: Volume represents a named volume in - a pod that may be accessed by any container in - the pod. - properties: - awsElasticBlockStore: - description: 'awsElasticBlockStore represents - an AWS Disk resource that is attached to a - kubelet''s host machine and then exposed to - the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'fsType is the filesystem type - of the volume that you want to mount. - Tip: Ensure that the filesystem type is - supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - partition: - description: 'partition is the partition - in the volume that you want to mount. - If omitted, the default is to mount by - volume name. Examples: For volume /dev/sda1, - you specify the partition as "1". Similarly, - the volume partition for /dev/sda is "0" - (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'readOnly value true will force - the readOnly setting in VolumeMounts. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'volumeID is unique ID of the - persistent disk resource in AWS (Amazon - EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: azureDisk represents an Azure Data - Disk mount on the host and bind mount to the - pod. - properties: - cachingMode: - description: 'cachingMode is the Host Caching - mode: None, Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the - data disk in the blob storage - type: string - diskURI: - description: diskURI is the URI of data - disk in the blob storage - type: string - fsType: - description: fsType is Filesystem type to - mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - kind: - description: 'kind expected values are Shared: - multiple blob disks per storage account Dedicated: - single blob disk per storage account Managed: - azure managed data disk (only in managed - availability set). defaults to shared' - type: string - readOnly: - description: readOnly Defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File - Service mount on the host and bind mount to - the pod. - properties: - readOnly: - description: readOnly defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of - secret that contains Azure Storage Account - Name and Key - type: string - shareName: - description: shareName is the azure share - Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: cephFS represents a Ceph FS mount - on the host that shares a pod's lifetime - properties: - monitors: - description: 'monitors is Required: Monitors - is a collection of Ceph monitors More - info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'path is Optional: Used as - the mounted root, rather than the full - Ceph tree, default is /' - type: string - readOnly: - description: 'readOnly is Optional: Defaults - to false (read/write). ReadOnly here will - force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'secretFile is Optional: SecretFile - is the path to key ring for User, default - is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'secretRef is Optional: SecretRef - is reference to the authentication secret - for User, default is empty. More info: - https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - 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 - user: - description: 'user is optional: User is - the rados user name, default is admin - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'cinder represents a cinder volume - attached and mounted on kubelets host machine. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type - to mount. Must be a filesystem type supported - by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: - https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'readOnly defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'secretRef is optional: points - to a secret object containing parameters - used to connect to OpenStack.' - 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 - volumeID: - description: 'volumeID used to identify - the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap - that should populate this volume - properties: - defaultMode: - description: 'defaultMode is optional: mode - bits used to set permissions on created - files by default. Must be an octal value - between 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. - Directories within the path are not affected - by this setting. This might be in conflict - with other options that affect the file - mode, like fsGroup, and the result can - be other mode bits set.' - format: int32 - type: integer - items: - description: items if unspecified, each - key-value pair in the Data field of the - referenced ConfigMap will be projected - into the volume as a file whose name is - the key and content is the value. If specified, - the listed keys will be projected into - the specified paths, and unlisted keys - will not be present. If a key is specified - which is not present in the ConfigMap, - the volume setup will error unless it - is marked optional. Paths must be relative - and may not contain the '..' path or start - with '..'. - items: - description: Maps a string key to a path - within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode - bits used to set permissions on - this file. Must be an octal value - between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts - both octal and decimal values, JSON - requires decimal values for mode - bits. If not specified, the volume - defaultMode will be used. This might - be in conflict with other options - that affect the file mode, like - fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - 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: optional specify whether the - ConfigMap or its keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - description: csi (Container Storage Interface) - represents ephemeral storage that is handled - by certain external CSI drivers (Beta feature). - properties: - driver: - description: driver is the name of the CSI - driver that handles this volume. Consult - with your admin for the correct name as - registered in the cluster. - type: string - fsType: - description: fsType to mount. Ex. "ext4", - "xfs", "ntfs". If not provided, the empty - value is passed to the associated CSI - driver which will determine the default - filesystem to apply. - type: string - nodePublishSecretRef: - description: nodePublishSecretRef is a reference - to the secret object containing sensitive - information to pass to the CSI driver - to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This field - is optional, and may be empty if no secret - is required. If the secret object contains - more than one secret, all secret references - are passed. - 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 - readOnly: - description: readOnly specifies a read-only - configuration for the volume. Defaults - to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: volumeAttributes stores driver-specific - properties that are passed to the CSI - driver. Consult your driver's documentation - for supported values. - type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward - API about the pod that should populate this - volume - properties: - defaultMode: - description: 'Optional: mode bits to use - on created files by default. Must be a - Optional: mode bits used to set permissions - on created files by default. Must be an - octal value between 0000 and 0777 or a - decimal value between 0 and 511. YAML - accepts both octal and decimal values, - JSON requires decimal values for mode - bits. Defaults to 0644. Directories within - the path are not affected by this setting. - This might be in conflict with other options - that affect the file mode, like fsGroup, - and the result can be other mode bits - set.' - format: int32 - type: integer - items: - description: Items is a list of downward - API volume file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects a - field of the pod: only annotations, - labels, name and namespace are supported.' - 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 - mode: - description: 'Optional: mode bits - used to set permissions on this - file, must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, JSON - requires decimal values for mode - bits. If not specified, the volume - defaultMode will be used. This might - be in conflict with other options - that affect the file mode, like - fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the - relative path name of the file to - be created. Must not be absolute - or contain the ''..'' path. Must - be utf-8 encoded. The first item - of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of - the container: only resources limits - and requests (limits.cpu, limits.memory, - requests.cpu and requests.memory) - are currently supported.' - 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 - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'emptyDir represents a temporary - directory that shares a pod''s lifetime. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'medium represents what type - of storage medium should back this directory. - The default is "" which means to use the - node''s default medium. Must be an empty - string (default) or Memory. More info: - https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount - of local storage required for this EmptyDir - volume. The size limit is also applicable - for memory medium. The maximum usage on - memory medium EmptyDir would be the minimum - value between the SizeLimit specified - here and the sum of memory limits of all - containers in a pod. The default is nil - which means that the limit is undefined. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: "ephemeral represents a volume - that is handled by a cluster storage driver. - The volume's lifecycle is tied to the pod - that defines it - it will be created before - the pod starts, and deleted when the pod is - removed. \n Use this if: a) the volume is - only needed while the pod runs, b) features - of normal volumes like restoring from snapshot - or capacity tracking are needed, c) the storage - driver is specified through a storage class, - and d) the storage driver supports dynamic - volume provisioning through a PersistentVolumeClaim - (see EphemeralVolumeSource for more information - on the connection between this volume type - and PersistentVolumeClaim). \n Use PersistentVolumeClaim - or one of the vendor-specific APIs for volumes - that persist for longer than the lifecycle - of an individual pod. \n Use CSI for light-weight - local ephemeral volumes if the CSI driver - is meant to be used that way - see the documentation - of the driver for more information. \n A pod - can use both types of ephemeral volumes and - persistent volumes at the same time." - properties: - volumeClaimTemplate: - description: "Will be used to create a stand-alone - PVC to provision the volume. The pod in - which this EphemeralVolumeSource is embedded - will be the owner of the PVC, i.e. the - PVC will be deleted together with the - pod. The name of the PVC will be `-` where `` - is the name from the `PodSpec.Volumes` - array entry. Pod validation will reject - the pod if the concatenated name is not - valid for a PVC (for example, too long). - \n An existing PVC with that name that - is not owned by the pod will *not* be - used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is - then blocked until the unrelated PVC is - removed. If such a pre-created PVC is - meant to be used by the pod, the PVC has - to updated with an owner reference to - the pod once the pod exists. Normally - this should not be necessary, but it may - be useful when manually reconstructing - a broken cluster. \n This field is read-only - and no changes will be made by Kubernetes - to the PVC after it has been created. - \n Required, must not be nil." - properties: - metadata: - description: May contain labels and - annotations that will be copied into - the PVC when creating it. No other - fields are allowed and will be rejected - during validation. - type: object - spec: - description: The specification for the - PersistentVolumeClaim. The entire - content is copied unchanged into the - PVC that gets created from this template. - The same fields as in a PersistentVolumeClaim - are also valid here. - properties: - accessModes: - description: 'accessModes contains - the desired access modes the volume - should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can - be used to specify either: * An - existing VolumeSnapshot object - (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external - controller can support the specified - data source, it will create a - new volume based on the contents - of the specified data source. - When the AnyVolumeDataSource feature - gate is enabled, dataSource contents - will be copied to dataSourceRef, - and dataSourceRef contents will - be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace - is specified, then dataSourceRef - will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the - group for the resource being - referenced. If APIGroup is - not specified, the specified - Kind must be in the core API - group. For any other third-party - types, APIGroup is required. - type: string - kind: - description: Kind is the type - of resource being referenced - type: string - name: - description: Name is the name - of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: 'dataSourceRef specifies - the object from which to populate - the volume with data, if a non-empty - volume is desired. This may be - any object from a non-empty API - group (non core object) or a PersistentVolumeClaim - object. When this field is specified, - volume binding will only succeed - if the type of the specified object - matches some installed volume - populator or dynamic provisioner. - This field will replace the functionality - of the dataSource field and as - such if both fields are non-empty, - they must have the same value. - For backwards compatibility, when - namespace isn''t specified in - dataSourceRef, both fields (dataSource - and dataSourceRef) will be set - to the same value automatically - if one of them is empty and the - other is non-empty. When namespace - is specified in dataSourceRef, - dataSource isn''t set to the same - value and must be empty. There - are three important differences - between dataSource and dataSourceRef: - * While dataSource only allows - two specific types of objects, - dataSourceRef allows any non-core - object, as well as PersistentVolumeClaim - objects. * While dataSource ignores - disallowed values (dropping them), - dataSourceRef preserves all values, - and generates an error if a disallowed - value is specified. * While dataSource - only allows local objects, dataSourceRef - allows objects in any namespaces. - (Beta) Using this field requires - the AnyVolumeDataSource feature - gate to be enabled. (Alpha) Using - the namespace field of dataSourceRef - requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the - group for the resource being - referenced. If APIGroup is - not specified, the specified - Kind must be in the core API - group. For any other third-party - types, APIGroup is required. - type: string - kind: - description: Kind is the type - of resource being referenced - type: string - name: - description: Name is the name - of resource being referenced - type: string - namespace: - description: Namespace is the - namespace of resource being - referenced Note that when - a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant - object is required in the - referent namespace to allow - that namespace's owner to - accept the reference. See - the ReferenceGrant documentation - for details. (Alpha) This - field requires the CrossNamespaceVolumeDataSource - feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents - the minimum resources the volume - should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed - to specify resource requirements - that are lower than previous value - but must still be higher than - capacity recorded in the status - field of the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the - names of resources, defined - in spec.resourceClaims, that - are used by this container. - \n This is an alpha field - and requires enabling the - DynamicResourceAllocation - feature gate. \n 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 - selector: - description: selector is a label - query over volumes to consider - for binding. - 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 - storageClassName: - description: 'storageClassName is - the name of the StorageClass required - by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines - what type of volume is required - by the claim. Value of Filesystem - is implied when not included in - claim spec. - type: string - volumeName: - description: volumeName is the binding - reference to the PersistentVolume - backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource - that is attached to a kubelet's host machine - and then exposed to the pod. - properties: - fsType: - description: 'fsType is the filesystem type - to mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. TODO: how do - we prevent errors in the filesystem from - compromising the machine' - type: string - lun: - description: 'lun is Optional: FC target - lun number' - format: int32 - type: integer - readOnly: - description: 'readOnly is Optional: Defaults - to false (read/write). ReadOnly here will - force the ReadOnly setting in VolumeMounts.' - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC - target worldwide names (WWNs)' - items: - type: string - type: array - wwids: - description: 'wwids Optional: FC volume - world wide identifiers (wwids) Either - wwids or combination of targetWWNs and - lun must be set, but not both simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: flexVolume represents a generic - volume resource that is provisioned/attached - using an exec based plugin. - properties: - driver: - description: driver is the name of the driver - to use for this volume. - type: string - fsType: - description: fsType is the filesystem type - to mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". The default filesystem - depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this - field holds extra command options if any.' - type: object - readOnly: - description: 'readOnly is Optional: defaults - to false (read/write). ReadOnly here will - force the ReadOnly setting in VolumeMounts.' - type: boolean - secretRef: - description: 'secretRef is Optional: secretRef - is reference to the secret object containing - sensitive information to pass to the plugin - scripts. This may be empty if no secret - object is specified. If the secret object - contains more than one secret, all secrets - are passed to the plugin scripts.' - 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 - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume - attached to a kubelet's host machine. This - depends on the Flocker control service being - running - properties: - datasetName: - description: datasetName is Name of the - dataset stored as metadata -> name on - the dataset for Flocker should be considered - as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of - the dataset. This is unique identifier - of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'gcePersistentDisk represents a - GCE Disk resource that is attached to a kubelet''s - host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'fsType is filesystem type - of the volume that you want to mount. - Tip: Ensure that the filesystem type is - supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - partition: - description: 'partition is the partition - in the volume that you want to mount. - If omitted, the default is to mount by - volume name. Examples: For volume /dev/sda1, - you specify the partition as "1". Similarly, - the volume partition for /dev/sda is "0" - (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'pdName is unique name of the - PD resource in GCE. Used to identify the - disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'readOnly here will force the - ReadOnly setting in VolumeMounts. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'gitRepo represents a git repository - at a particular revision. DEPRECATED: GitRepo - is deprecated. To provision a container with - a git repo, mount an EmptyDir into an InitContainer - that clones the repo using git, then mount - the EmptyDir into the Pod''s container.' - properties: - directory: - description: directory is the target directory - name. Must not contain or start with '..'. If - '.' is supplied, the volume directory - will be the git repository. Otherwise, - if specified, the volume will contain - the git repository in the subdirectory - with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash - for the specified revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'glusterfs represents a Glusterfs - mount on the host that shares a pod''s lifetime. - More info: https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'endpoints is the endpoint - name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'path is the Glusterfs volume - path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'readOnly here will force the - Glusterfs volume to be mounted with read-only - permissions. Defaults to false. More info: - https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'hostPath represents a pre-existing - file or directory on the host machine that - is directly exposed to the container. This - is generally used for system agents or other - privileged things that are allowed to see - the host machine. Most containers will NOT - need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who - can use host directory mounts and who can/can - not mount host directories as read/write.' - properties: - path: - description: 'path of the directory on the - host. If the path is a symlink, it will - follow the link to the real path. More - info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'iscsi represents an ISCSI Disk - resource that is attached to a kubelet''s - host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether - support iSCSI Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether - support iSCSI Session CHAP authentication - type: boolean - fsType: - description: 'fsType is the filesystem type - of the volume that you want to mount. - Tip: Ensure that the filesystem type is - supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - initiatorName: - description: initiatorName is the custom - iSCSI Initiator Name. If initiatorName - is specified with iscsiInterface simultaneously, - new iSCSI interface : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified - Name. - type: string - iscsiInterface: - description: iscsiInterface is the interface - Name that uses an iSCSI transport. Defaults - to 'default' (tcp). - type: string - lun: - description: lun represents iSCSI Target - Lun number. - format: int32 - type: integer - portals: - description: portals is the iSCSI Target - Portal List. The portal is either an IP - or ip_addr:port if the port is other than - default (typically TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: readOnly here will force the - ReadOnly setting in VolumeMounts. Defaults - to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret - for iSCSI target and initiator authentication - 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 - targetPortal: - description: targetPortal is iSCSI Target - Portal. The Portal is either an IP or - ip_addr:port if the port is other than - default (typically TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'name of the volume. Must be a - DNS_LABEL and unique within the pod. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'nfs represents an NFS mount on - the host that shares a pod''s lifetime More - info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'path that is exported by the - NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'readOnly here will force the - NFS export to be mounted with read-only - permissions. Defaults to false. More info: - https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'server is the hostname or - IP address of the NFS server. More info: - https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource - represents a reference to a PersistentVolumeClaim - in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'claimName is the name of a - PersistentVolumeClaim in the same namespace - as the pod using this volume. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: readOnly Will force the ReadOnly - setting in VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents - a PhotonController persistent disk attached - and mounted on kubelets host machine - properties: - fsType: - description: fsType is the filesystem type - to mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - pdID: - description: pdID is the ID that identifies - Photon Controller persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx - volume attached and mounted on kubelets host - machine - properties: - fsType: - description: fSType represents the filesystem - type to mount Must be a filesystem type - supported by the host operating system. - Ex. "ext4", "xfs". Implicitly inferred - to be "ext4" if unspecified. - type: string - readOnly: - description: readOnly defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies - a Portworx volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one - resources secrets, configmaps, and downward - API - properties: - defaultMode: - description: defaultMode are the mode bits - used to set permissions on created files - by default. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal - values for mode bits. Directories within - the path are not affected by this setting. - This might be in conflict with other options - that affect the file mode, like fsGroup, - and the result can be other mode bits - set. - format: int32 - type: integer - sources: - description: sources is the list of volume - projections - items: - description: Projection that may be projected - along with other supported volume types - properties: - configMap: - description: configMap information - about the configMap data to project - properties: - items: - description: items if unspecified, - each key-value pair in the Data - field of the referenced ConfigMap - will be projected into the volume - as a file whose name is the - key and content is the value. - If specified, the listed keys - will be projected into the specified - paths, and unlisted keys will - not be present. If a key is - specified which is not present - in the ConfigMap, the volume - setup will error unless it is - marked optional. Paths must - be relative and may not contain - the '..' path or start with - '..'. - items: - description: Maps a string key - to a path within a volume. - properties: - key: - description: key is the - key to project. - type: string - mode: - description: 'mode is Optional: - mode bits used to set - permissions on this file. - Must be an octal value - between 0000 and 0777 - or a decimal value between - 0 and 511. YAML accepts - both octal and decimal - values, JSON requires - decimal values for mode - bits. If not specified, - the volume defaultMode - will be used. This might - be in conflict with other - options that affect the - file mode, like fsGroup, - and the result can be - other mode bits set.' - format: int32 - type: integer - path: - description: path is the - relative path of the file - to map the key to. May - not be an absolute path. - May not contain the path - element '..'. May not - start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - 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: optional specify - whether the ConfigMap or its - keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - description: downwardAPI information - about the downwardAPI data to project - properties: - items: - description: Items is a list of - DownwardAPIVolume file - items: - description: DownwardAPIVolumeFile - represents information to - create the file containing - the pod field - properties: - fieldRef: - description: 'Required: - Selects a field of the - pod: only annotations, - labels, name and namespace - are supported.' - 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 - mode: - description: 'Optional: - mode bits used to set - permissions on this file, - must be an octal value - between 0000 and 0777 - or a decimal value between - 0 and 511. YAML accepts - both octal and decimal - values, JSON requires - decimal values for mode - bits. If not specified, - the volume defaultMode - will be used. This might - be in conflict with other - options that affect the - file mode, like fsGroup, - and the result can be - other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: - Path is the relative - path name of the file - to be created. Must not - be absolute or contain - the ''..'' path. Must - be utf-8 encoded. The - first item of the relative - path must not start with - ''..''' - type: string - resourceFieldRef: - description: 'Selects a - resource of the container: - only resources limits - and requests (limits.cpu, - limits.memory, requests.cpu - and requests.memory) are - currently supported.' - 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 - required: - - path - type: object - type: array - type: object - secret: - description: secret information about - the secret data to project - properties: - items: - description: items if unspecified, - each key-value pair in the Data - field of the referenced Secret - will be projected into the volume - as a file whose name is the - key and content is the value. - If specified, the listed keys - will be projected into the specified - paths, and unlisted keys will - not be present. If a key is - specified which is not present - in the Secret, the volume setup - will error unless it is marked - optional. Paths must be relative - and may not contain the '..' - path or start with '..'. - items: - description: Maps a string key - to a path within a volume. - properties: - key: - description: key is the - key to project. - type: string - mode: - description: 'mode is Optional: - mode bits used to set - permissions on this file. - Must be an octal value - between 0000 and 0777 - or a decimal value between - 0 and 511. YAML accepts - both octal and decimal - values, JSON requires - decimal values for mode - bits. If not specified, - the volume defaultMode - will be used. This might - be in conflict with other - options that affect the - file mode, like fsGroup, - and the result can be - other mode bits set.' - format: int32 - type: integer - path: - description: path is the - relative path of the file - to map the key to. May - not be an absolute path. - May not contain the path - element '..'. May not - start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - 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: optional field specify - whether the Secret or its key - must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - description: serviceAccountToken is - information about the serviceAccountToken - data to project - properties: - audience: - description: audience is the intended - audience of the token. A recipient - of a token must identify itself - with an identifier specified - in the audience of the token, - and otherwise should reject - the token. The audience defaults - to the identifier of the apiserver. - type: string - expirationSeconds: - description: expirationSeconds - is the requested duration of - validity of the service account - token. As the token approaches - expiration, the kubelet volume - plugin will proactively rotate - the service account token. The - kubelet will start trying to - rotate the token if the token - is older than 80 percent of - its time to live or if the token - is older than 24 hours.Defaults - to 1 hour and must be at least - 10 minutes. - format: int64 - type: integer - path: - description: path is the path - relative to the mount point - of the file to project the token - into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount - on the host that shares a pod's lifetime - properties: - group: - description: group to map volume access - to Default is no group - type: string - readOnly: - description: readOnly here will force the - Quobyte volume to be mounted with read-only - permissions. Defaults to false. - type: boolean - registry: - description: registry represents a single - or multiple Quobyte Registry services - specified as a string as host:port pair - (multiple entries are separated with commas) - which acts as the central registry for - volumes - type: string - tenant: - description: tenant owning the given Quobyte - volume in the Backend Used with dynamically - provisioned Quobyte volumes, value is - set by the plugin - type: string - user: - description: user to map volume access to - Defaults to serivceaccount user - type: string - volume: - description: volume is a string that references - an already created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'rbd represents a Rados Block Device - mount on the host that shares a pod''s lifetime. - More info: https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type - of the volume that you want to mount. - Tip: Ensure that the filesystem type is - supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - image: - description: 'image is the rados image name. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'keyring is the path to key - ring for RBDUser. Default is /etc/ceph/keyring. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'monitors is a collection of - Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'pool is the rados pool name. - Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'readOnly here will force the - ReadOnly setting in VolumeMounts. Defaults - to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'secretRef is name of the authentication - secret for RBDUser. If provided overrides - keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - 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 - user: - description: 'user is the rados user name. - Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent - volume attached and mounted on Kubernetes - nodes. - properties: - fsType: - description: fsType is the filesystem type - to mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Default is "xfs". - type: string - gateway: - description: gateway is the host address - of the ScaleIO API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name - of the ScaleIO Protection Domain for the - configured storage. - type: string - readOnly: - description: readOnly Defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: secretRef references to the - secret for ScaleIO user and other sensitive - information. If this is not provided, - Login operation will fail. - 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 - sslEnabled: - description: sslEnabled Flag enable/disable - SSL communication with Gateway, default - false - type: boolean - storageMode: - description: storageMode indicates whether - the storage for a volume should be ThickProvisioned - or ThinProvisioned. Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO - Storage Pool associated with the protection - domain. - type: string - system: - description: system is the name of the storage - system as configured in ScaleIO. - type: string - volumeName: - description: volumeName is the name of a - volume already created in the ScaleIO - system that is associated with this volume - source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'secret represents a secret that - should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'defaultMode is Optional: mode - bits used to set permissions on created - files by default. Must be an octal value - between 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. - Directories within the path are not affected - by this setting. This might be in conflict - with other options that affect the file - mode, like fsGroup, and the result can - be other mode bits set.' - format: int32 - type: integer - items: - description: items If unspecified, each - key-value pair in the Data field of the - referenced Secret will be projected into - the volume as a file whose name is the - key and content is the value. If specified, - the listed keys will be projected into - the specified paths, and unlisted keys - will not be present. If a key is specified - which is not present in the Secret, the - volume setup will error unless it is marked - optional. Paths must be relative and may - not contain the '..' path or start with - '..'. - items: - description: Maps a string key to a path - within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode - bits used to set permissions on - this file. Must be an octal value - between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts - both octal and decimal values, JSON - requires decimal values for mode - bits. If not specified, the volume - defaultMode will be used. This might - be in conflict with other options - that affect the file mode, like - fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether - the Secret or its keys must be defined - type: boolean - secretName: - description: 'secretName is the name of - the secret in the pod''s namespace to - use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: storageOS represents a StorageOS - volume attached and mounted on Kubernetes - nodes. - properties: - fsType: - description: fsType is the filesystem type - to mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - readOnly: - description: readOnly defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: secretRef specifies the secret - to use for obtaining the StorageOS API - credentials. If not specified, default - values will be attempted. - 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 - volumeName: - description: volumeName is the human-readable - name of the StorageOS volume. Volume - names are only unique within a namespace. - type: string - volumeNamespace: - description: volumeNamespace specifies the - scope of the volume within StorageOS. If - no namespace is specified then the Pod's - namespace will be used. This allows the - Kubernetes name scoping to be mirrored - within StorageOS for tighter integration. - Set VolumeName to any name to override - the default behaviour. Set to "default" - if you are not using namespaces within - StorageOS. Namespaces that do not pre-exist - within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere - volume attached and mounted on kubelets host - machine - properties: - fsType: - description: fsType is filesystem type to - mount. Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage - Policy Based Management (SPBM) profile - ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage - Policy Based Management (SPBM) profile - name. - type: string - volumePath: - description: volumePath is the path that - identifies vSphere volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - type: object - replicas: - description: Replicas is the number of desired pods. Defaults - to 1. - format: int32 - type: integer - strategy: - description: The deployment strategy to use to replace - existing pods with new ones. - properties: - rollingUpdate: - description: 'Rolling update config params. Present - only if DeploymentStrategyType = RollingUpdate. - --- TODO: Update this to follow our convention for - oneOf, whatever we decide it to be.' - properties: - maxSurge: - anyOf: - - type: integer - - type: string - description: 'The maximum number of pods that - can be scheduled above the desired number of - pods. Value can be an absolute number (ex: 5) - or a percentage of desired pods (ex: 10%). This - can not be 0 if MaxUnavailable is 0. Absolute - number is calculated from percentage by rounding - up. Defaults to 25%. Example: when this is set - to 30%, the new ReplicaSet can be scaled up - immediately when the rolling update starts, - such that the total number of old and new pods - do not exceed 130% of desired pods. Once old - pods have been killed, new ReplicaSet can be - scaled up further, ensuring that total number - of pods running at any time during the update - is at most 130% of desired pods.' - x-kubernetes-int-or-string: true - maxUnavailable: - anyOf: - - type: integer - - type: string - description: 'The maximum number of pods that - can be unavailable during the update. Value - can be an absolute number (ex: 5) or a percentage - of desired pods (ex: 10%). Absolute number is - calculated from percentage by rounding down. - This can not be 0 if MaxSurge is 0. Defaults - to 25%. Example: when this is set to 30%, the - old ReplicaSet can be scaled down to 70% of - desired pods immediately when the rolling update - starts. Once new pods are ready, old ReplicaSet - can be scaled down further, followed by scaling - up the new ReplicaSet, ensuring that the total - number of pods available at all times during - the update is at least 70% of desired pods.' - x-kubernetes-int-or-string: true - type: object - type: - description: Type of deployment. Can be "Recreate" - or "RollingUpdate". Default is RollingUpdate. - type: string - type: object - type: object - envoyService: - description: EnvoyService defines the desired state of the - Envoy service resource. If unspecified, default settings - for the manged Envoy service resource are applied. - properties: - annotations: - additionalProperties: - type: string - description: Annotations that should be appended to the - service. By default, no annotations are appended. - type: object - type: - default: LoadBalancer - description: Type determines how the Service is exposed. - Defaults to LoadBalancer. Valid options are ClusterIP, - LoadBalancer and NodePort. "LoadBalancer" means a service - will be exposed via an external load balancer (if the - cloud provider supports it). "ClusterIP" means a service - will only be accessible inside the cluster, via the - cluster IP. "NodePort" means a service will be exposed - on a static Port on all Nodes of the cluster. - enum: - - ClusterIP - - LoadBalancer - - NodePort - type: string - type: object - type: object - type: - description: Type is the type of resource provider to use. A resource - provider provides infrastructure resources for running the data - plane, e.g. Envoy proxy, and optional auxiliary control planes. - Supported types are "Kubernetes". - enum: - - Kubernetes - type: string - required: - - type - type: object - telemetry: - description: Telemetry defines telemetry parameters for managed proxies. - properties: - accessLog: - description: AccessLogs defines accesslog parameters for managed - proxies. If unspecified, will send default format to stdout. - properties: - disable: - description: Disable disables access logging for managed proxies - if set to true. - type: boolean - settings: - description: Settings defines accesslog settings for managed - proxies. If unspecified, will send default format to stdout. - items: - properties: - format: - description: Format defines the format of accesslog. - properties: - json: - additionalProperties: - type: string - description: JSON is additional attributes that - describe the specific event occurrence. Structured - format for the envoy access logs. Envoy [command - operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) - can be used as values for fields within the Struct. - It's required when the format type is "JSON". - type: object - text: - description: Text defines the text accesslog format, - following Envoy accesslog formatting, empty value - results in proxy's default access log format. - It's required when the format type is "Text". - Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) - may be used in the format. The [format string - documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) - provides more information. - type: string - type: - description: Type defines the type of accesslog - format. - enum: - - Text - - JSON - type: string - type: object - sinks: - description: Sinks defines the sinks of accesslog. - items: - properties: - file: - description: File defines the file accesslog sink. - properties: - path: - description: Path defines the file path used - to expose envoy access log(e.g. /dev/stdout). - Empty value disables accesslog. - type: string - type: object - openTelemetry: - description: OpenTelemetry defines the OpenTelemetry - accesslog sink. - properties: - host: - description: Host define the extension service - hostname. - type: string - port: - default: 4317 - description: Port defines the port the extension - service is exposed on. - format: int32 - minimum: 0 - type: integer - resources: - additionalProperties: - type: string - description: Resources is a set of labels - that describe the source of a log entry, - including envoy node info. It's recommended - to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). - type: object - required: - - host - type: object - type: - description: Type defines the type of accesslog - sink. - enum: - - File - - OpenTelemetry - type: string - type: object - minItems: 1 - type: array - required: - - format - - sinks - type: object - type: array - type: object - metrics: - description: Metrics defines metrics configuration for managed - proxies. - properties: - prometheus: - description: Prometheus defines the configuration for Admin - endpoint `/stats/prometheus`. - type: object - sinks: - description: Sinks defines the metric sinks where metrics - are sent to. - items: - properties: - openTelemetry: - description: OpenTelemetry defines the configuration - for OpenTelemetry sink. It's required if the sink - type is OpenTelemetry. - properties: - host: - description: Host define the service hostname. - type: string - port: - default: 4317 - description: Port defines the port the service is - exposed on. - format: int32 - maximum: 65535 - minimum: 0 - type: integer - required: - - host - type: object - type: - default: OpenTelemetry - description: Type defines the metric sink type. EG currently - only supports OpenTelemetry. - enum: - - OpenTelemetry - type: string - required: - - type - type: object - type: array - type: object - tracing: - description: Tracing defines tracing configuration for managed - proxies. If unspecified, will not send tracing data. - properties: - customTags: - additionalProperties: - properties: - environment: - description: Environment adds value from environment - variable to each span. It's required when the type - is "Environment". - properties: - defaultValue: - description: DefaultValue defines the default value - to use if the environment variable is not set. - type: string - name: - description: Name defines the name of the environment - variable which to extract the value from. - type: string - required: - - name - type: object - literal: - description: Literal adds hard-coded value to each span. - It's required when the type is "Literal". - properties: - value: - description: Value defines the hard-coded value - to add to each span. - type: string - required: - - value - type: object - requestHeader: - description: RequestHeader adds value from request header - to each span. It's required when the type is "RequestHeader". - properties: - defaultValue: - description: DefaultValue defines the default value - to use if the request header is not set. - type: string - name: - description: Name defines the name of the request - header which to extract the value from. - type: string - required: - - name - type: object - type: - default: Literal - description: Type defines the type of custom tag. - enum: - - Literal - - Environment - - RequestHeader - type: string - required: - - type - type: object - description: CustomTags defines the custom tags to add to - each span. If provider is kubernetes, pod name and namespace - are added by default. - type: object - provider: - description: Provider defines the tracing provider. Only OpenTelemetry - is supported currently. - properties: - host: - description: Host define the provider service hostname. - type: string - port: - default: 4317 - description: Port defines the port the provider service - is exposed on. - format: int32 - minimum: 0 - type: integer - type: - default: OpenTelemetry - description: Type defines the tracing provider type. EG - currently only supports OpenTelemetry. - enum: - - OpenTelemetry - type: string - required: - - host - - type - type: object - samplingRate: - default: 100 - description: SamplingRate controls the rate at which traffic - will be selected for tracing if no prior sampling decision - has been made. Defaults to 100, valid values [0-100]. 100 - indicates 100% sampling. - format: int32 - maximum: 100 - minimum: 0 - type: integer - required: - - provider - type: object - type: object - type: object - status: - description: EnvoyProxyStatus defines the actual state of EnvoyProxy. - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml deleted file mode 100644 index e8e60773648..00000000000 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml +++ /dev/null @@ -1,132 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null - name: authenticationfilters.gateway.envoyproxy.io -spec: - group: gateway.envoyproxy.io - names: - kind: AuthenticationFilter - listKind: AuthenticationFilterList - plural: authenticationfilters - singular: authenticationfilter - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - 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 the AuthenticationFilter - type. - properties: - jwtProviders: - description: JWT defines the JSON Web Token (JWT) authentication provider - type. When multiple jwtProviders are specified, the JWT is considered - valid if any of the providers successfully validate the JWT. For - additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. - items: - description: 'JwtAuthenticationFilterProvider defines the JSON Web - Token (JWT) authentication provider type and how JWTs should be - verified:' - properties: - audiences: - description: Audiences is a list of JWT audiences allowed access. - For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. - If not provided, JWT audiences are not checked. - items: - type: string - maxItems: 8 - type: array - claimToHeaders: - description: 'ClaimToHeaders is a list of JWT claims that must - be extracted into HTTP request headers For examples, following - config: The claim must be of type; string, int, double, bool. - Array type claims are not supported' - items: - description: ClaimToHeader defines a configuration to convert - JWT claims into HTTP headers - properties: - claim: - description: 'Claim is the JWT Claim that should be saved - into the header : it can be a nested claim of type (eg. - "claim.nested.key", "sub"). The nested claim name must - use dot "." to separate the JSON name path.' - type: string - header: - description: Header defines the name of the HTTP request - header that the JWT Claim will be saved into. - type: string - required: - - claim - - header - type: object - type: array - issuer: - description: Issuer is the principal that issued the JWT and - takes the form of a URL or email address. For additional details, - see https://tools.ietf.org/html/rfc7519#section-4.1.1 for - URL format and https://rfc-editor.org/rfc/rfc5322.html for - email format. If not provided, the JWT issuer is not checked. - maxLength: 253 - type: string - name: - description: Name defines a unique name for the JWT provider. - A name can have a variety of forms, including RFC1123 subdomains, - RFC 1123 labels, or RFC 1035 labels. - maxLength: 253 - minLength: 1 - type: string - remoteJWKS: - description: RemoteJWKS defines how to fetch and cache JSON - Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. - properties: - uri: - description: URI is the HTTPS URI to fetch the JWKS. Envoy's - system trust bundle is used to validate the server certificate. - maxLength: 253 - minLength: 1 - type: string - required: - - uri - type: object - required: - - name - - remoteJWKS - type: object - maxItems: 4 - type: array - type: - description: Type defines the type of authentication provider to use. - Supported provider types are "JWT". - enum: - - JWT - type: string - required: - - type - type: object - required: - - spec - type: object - served: true - storage: true - subresources: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml new file mode 100644 index 00000000000..6606d2dd859 --- /dev/null +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -0,0 +1,421 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: backendtrafficpolicies.gateway.envoyproxy.io +spec: + group: gateway.envoyproxy.io + names: + kind: BackendTrafficPolicy + listKind: BackendTrafficPolicyList + plural: backendtrafficpolicies + shortNames: + - btp + singular: backendtrafficpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: BackendTrafficPolicy allows the user to configure the behavior + of the connection between the downstream client and Envoy Proxy 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 BackendTrafficPolicy. + properties: + loadBalancer: + description: LoadBalancer policy to apply when routing traffic from + the gateway to the backend endpoints + properties: + consistentHash: + description: ConsistentHash defines the configuration when the + load balancer type is set to ConsistentHash + properties: + type: + description: ConsistentHashType defines the type of input + to hash on. + enum: + - SourceIP + type: string + required: + - type + type: object + slowStart: + description: SlowStart defines the configuration related to the + slow start load balancer policy. If set, during slow start window, + traffic sent to the newly added hosts will gradually increase. + Currently this is only supported for RoundRobin and LeastRequest + load balancers + properties: + window: + description: Window defines the duration of the warm up period + for newly added host. During slow start window, traffic + sent to the newly added hosts will gradually increase. Currently + only supports linear growth of traffic. For additional details, + see https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster-slowstartconfig + type: string + required: + - window + type: object + type: + description: Type decides the type of Load Balancer policy. Valid + LoadBalancerType values are "ConsistentHash", "LeastRequest", + "Random", "RoundRobin", + enum: + - ConsistentHash + - LeastRequest + - Random + - RoundRobin + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: If LoadBalancer type is consistentHash, consistentHash + field needs to be set. + rule: 'self.type == ''ConsistentHash'' ? has(self.consistentHash) + : !has(self.consistentHash)' + - message: Currently SlowStart is only supported for RoundRobin and + LeastRequest load balancers. + rule: 'self.type in [''Random'', ''ConsistentHash''] ? !has(self.slowStart) + : true ' + proxyProtocol: + description: ProxyProtocol enables the Proxy Protocol when communicating + with the backend. + properties: + version: + description: Version of ProxyProtol Valid ProxyProtocolVersion + values are "V1" "V2" + enum: + - V1 + - V2 + type: string + required: + - version + type: object + rateLimit: + description: RateLimit allows the user to limit the number of incoming + requests to a predefined value based on attributes within the traffic + flow. + properties: + global: + description: Global defines global rate limit configuration. + properties: + rules: + description: Rules are a list of RateLimit selectors and limits. + Each rule and its associated limit is applied in a mutually + exclusive way i.e. if multiple rules get selected, each + of their associated limits get applied, so a single traffic + request might increase the rate limit counters for multiple + rules if selected. + items: + description: RateLimitRule defines the semantics for matching + attributes from the incoming requests, and setting limits + for them. + properties: + clientSelectors: + description: ClientSelectors holds the list of select + conditions to select specific clients using attributes + from the traffic flow. All individual select conditions + must hold True for this rule and its limit to be applied. + If this field is empty, it is equivalent to True, + and the limit is applied. + items: + description: RateLimitSelectCondition specifies the + attributes within the traffic flow that can be used + to select a subset of clients to be ratelimited. + All the individual conditions must hold True for + the overall condition to hold True. + properties: + headers: + description: Headers is a list of request headers + to match. Multiple header values are ANDed together, + meaning, a request MUST match all the specified + headers. + items: + description: HeaderMatch defines the match attributes + within the HTTP Headers of the request. + properties: + name: + description: Name of the HTTP header. + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: Type specifies how to match + against the value of the header. + enum: + - Exact + - RegularExpression + - Distinct + type: string + value: + description: Value within the HTTP header. + Due to the case-insensitivity of header + names, "foo" and "Foo" are considered + equivalent. Do not set this field when + Type="Distinct", implying matching on + any/all unique values within the header. + maxLength: 1024 + type: string + required: + - name + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + sourceCIDR: + description: SourceCIDR is the client IP Address + range to match on. + properties: + type: + default: Exact + type: string + value: + description: Value is the IP CIDR that represents + the range of Source IP Addresses of the + client. These could also be the intermediate + addresses through which the request has + flown through and is part of the `X-Forwarded-For` + header. For example, `192.168.0.1/32`, `192.168.0.0/24`, + `001:db8::/64`. + maxLength: 256 + minLength: 1 + type: string + required: + - value + type: object + type: object + maxItems: 8 + type: array + limit: + description: Limit holds the rate limit values. This + limit is applied for traffic flows when the selectors + compute to True, causing the request to be counted + towards the limit. The limit is enforced and the request + is ratelimited, i.e. a response with 429 HTTP status + code is sent back to the client when the selected + requests have reached the limit. + properties: + requests: + type: integer + unit: + description: RateLimitUnit specifies the intervals + for setting rate limits. Valid RateLimitUnit values + are "Second", "Minute", "Hour", and "Day". + enum: + - Second + - Minute + - Hour + - Day + type: string + required: + - requests + - unit + type: object + required: + - limit + type: object + maxItems: 16 + type: array + required: + - rules + type: object + type: + description: Type decides the scope for the RateLimits. Valid + RateLimitType values are "Global". + enum: + - Global + type: string + required: + - type + type: object + targetRef: + description: targetRef is the name of the resource this policy is + being attached to. This Policy and the TargetRef MUST be in the + same namespace for this Policy to have effect and be applied to + the Gateway. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When + unspecified, the local namespace is inferred. Even when policy + targets a resource in a different namespace, it MUST only apply + to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + description: "SectionName is the name of a section within the + target resource. When unspecified, this targetRef targets the + entire resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name * + Service: Port Name \n If a SectionName is specified, but does + not exist on the targeted object, the Policy must fail to attach, + and the policy implementation should record a `ResolvedRefs` + or similar Condition in the Policy's status." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - group + - kind + - name + type: object + x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' + - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: self.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', + 'TCPRoute', 'TLSRoute'] + - message: this policy does not yet support the sectionName field + rule: '!has(self.sectionName)' + tcpKeepalive: + description: TcpKeepalive settings associated with the upstream client + connection. Disabled by default. + properties: + idleTime: + description: The duration a connection needs to be idle before + keep-alive probes start being sent. The duration format is Defaults + to `7200s`. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + interval: + description: The duration between keep-alive probes. Defaults + to `75s`. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + probes: + description: The total number of unacknowledged probes to send + before deciding the connection is dead. Defaults to 9. + format: int32 + type: integer + type: object + required: + - targetRef + type: object + status: + description: status defines the current status of BackendTrafficPolicy. + properties: + conditions: + description: Conditions describe the current conditions of the BackendTrafficPolicy. + 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 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml new file mode 100644 index 00000000000..351b25f95c1 --- /dev/null +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -0,0 +1,217 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: clienttrafficpolicies.gateway.envoyproxy.io +spec: + group: gateway.envoyproxy.io + names: + kind: ClientTrafficPolicy + listKind: ClientTrafficPolicyList + plural: clienttrafficpolicies + shortNames: + - ctp + singular: clienttrafficpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClientTrafficPolicy allows the user to configure the behavior + of the connection between the downstream client and Envoy Proxy 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 ClientTrafficPolicy. + properties: + enableProxyProtocol: + description: EnableProxyProtocol interprets the ProxyProtocol header + and adds the Client Address into the X-Forwarded-For header. Note + Proxy Protocol must be present when this field is set, else the + connection is closed. + type: boolean + targetRef: + description: TargetRef is the name of the Gateway resource this policy + is being attached to. This Policy and the TargetRef MUST be in the + same namespace for this Policy to have effect and be applied to + the Gateway. TargetRef + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When + unspecified, the local namespace is inferred. Even when policy + targets a resource in a different namespace, it MUST only apply + to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + description: "SectionName is the name of a section within the + target resource. When unspecified, this targetRef targets the + entire resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name * + Service: Port Name \n If a SectionName is specified, but does + not exist on the targeted object, the Policy must fail to attach, + and the policy implementation should record a `ResolvedRefs` + or similar Condition in the Policy's status." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - group + - kind + - name + type: object + x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' + - message: this policy can only have a targetRef.kind of Gateway + rule: self.kind == 'Gateway' + - message: this policy does not yet support the sectionName field + rule: '!has(self.sectionName)' + tcpKeepalive: + description: TcpKeepalive settings associated with the downstream + client connection. If defined, sets SO_KEEPALIVE on the listener + socket to enable TCP Keepalives. Disabled by default. + properties: + idleTime: + description: The duration a connection needs to be idle before + keep-alive probes start being sent. The duration format is Defaults + to `7200s`. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + interval: + description: The duration between keep-alive probes. Defaults + to `75s`. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + probes: + description: The total number of unacknowledged probes to send + before deciding the connection is dead. Defaults to 9. + format: int32 + type: integer + type: object + required: + - targetRef + type: object + status: + description: Status defines the current status of ClientTrafficPolicy. + properties: + conditions: + description: Conditions describe the current conditions of the ClientTrafficPolicy. + 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 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml index 7296a9e5127..e5b61e5d87c 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.13.0 name: envoypatchpolicies.gateway.envoyproxy.io spec: group: gateway.envoyproxy.io @@ -12,6 +11,8 @@ spec: kind: EnvoyPatchPolicy listKind: EnvoyPatchPolicyList plural: envoypatchpolicies + shortNames: + - epp singular: envoypatchpolicy scope: Namespaced versions: diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml new file mode 100644 index 00000000000..3228d86ccf9 --- /dev/null +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -0,0 +1,6318 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: envoyproxies.gateway.envoyproxy.io +spec: + group: gateway.envoyproxy.io + names: + kind: EnvoyProxy + listKind: EnvoyProxyList + plural: envoyproxies + shortNames: + - eproxy + singular: envoyproxy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: EnvoyProxy is the schema for the envoyproxies 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: EnvoyProxySpec defines the desired state of EnvoyProxy. + properties: + bootstrap: + description: Bootstrap defines the Envoy Bootstrap as a YAML string. + Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + to learn more about the syntax. If set, this is the Bootstrap configuration + used for the managed Envoy Proxy fleet instead of the default Bootstrap + configuration set by Envoy Gateway. Some fields within the Bootstrap + that are required to communicate with the xDS Server (Envoy Gateway) + and receive xDS resources from it are not configurable and will + result in the `EnvoyProxy` resource being rejected. Backward compatibility + across minor versions is not guaranteed. We strongly recommend using + `egctl x translate` to generate a `EnvoyProxy` resource with the + `Bootstrap` field set to the default Bootstrap configuration used. + You can edit this configuration, and rerun `egctl x translate` to + ensure there are no validation errors. + properties: + type: + default: Replace + description: Type is the type of the bootstrap configuration, + it should be either Replace or Merge. If unspecified, it defaults + to Replace. + enum: + - Merge + - Replace + type: string + value: + description: Value is a YAML string of the bootstrap. + type: string + required: + - value + type: object + concurrency: + description: Concurrency defines the number of worker threads to run. + If unset, it defaults to the number of cpuset threads on the platform. + format: int32 + type: integer + logging: + default: + level: + default: warn + description: Logging defines logging parameters for managed proxies. + properties: + level: + additionalProperties: + description: LogLevel defines a log level for Envoy Gateway + and EnvoyProxy system logs. + enum: + - debug + - info + - error + - warn + type: string + default: + default: warn + description: 'Level is a map of logging level per component, where + the component is the key and the log level is the value. If + unspecified, defaults to "default: warn".' + type: object + type: object + mergeGateways: + description: MergeGateways defines if Gateway resources should be + merged onto the same Envoy Proxy Infrastructure. Setting this field + to true would merge all Gateway Listeners under the parent Gateway + Class. This means that the port, protocol and hostname tuple must + be unique for every listener. If a duplicate listener is detected, + the newer listener (based on timestamp) will be rejected and its + status will be updated with a "Accepted=False" condition. + type: boolean + provider: + description: Provider defines the desired resource provider and provider-specific + configuration. If unspecified, the "Kubernetes" resource provider + is used with default configuration parameters. + properties: + kubernetes: + description: Kubernetes defines the desired state of the Kubernetes + resource provider. Kubernetes provides infrastructure resources + for running the data plane, e.g. Envoy proxy. If unspecified + and type is "Kubernetes", default settings for managed Kubernetes + resources are applied. + properties: + envoyDeployment: + description: EnvoyDeployment defines the desired state of + the Envoy deployment resource. If unspecified, default settings + for the managed Envoy deployment resource are applied. + properties: + container: + description: Container defines the desired specification + of main container. + properties: + env: + description: List of environment variables to set + in the 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 + image: + description: Image specifies the EnvoyProxy container + image to be used, instead of the default image. + type: string + resources: + description: 'Resources required by this container. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate. \n 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 defines the security + options the container should be run with. If set, + the fields of SecurityContext override the equivalent + fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + 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: \n 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 + volumeMounts: + description: VolumeMounts are volumes to mount into + the container's filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting of + a Volume within a container. + properties: + mountPath: + description: Path within the container at which + the volume should be mounted. Must not contain + ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should be + mounted. Behaves similarly to SubPath but + environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + type: object + initContainers: + description: 'List of initialization containers belonging + to the pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + items: + description: A single application container that you + want to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The container + image''s CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using + the container''s environment. If a variable cannot + be resolved, the reference in the input string + will be unchanged. Double $$ are reduced to a + single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string + literal "$(VAR_NAME)". Escaped references will + never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The container image''s ENTRYPOINT is + used if this is not provided. Variable references + $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, + the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" + will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + 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 container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will + take precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot be + updated. + 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 + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config + management to default or override container images + in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always if :latest + tag is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system + should take in response to container lifecycle + events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to + take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name. This will be canonicalized + upon output, so case-variant + names will be understood as + the same header. + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT + supported as a LifecycleHandler and kept + for the backward compatibility. There + are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler + is specified. + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due to an + API request or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The Pod''s termination grace + period countdown begins before the PreStop + hook is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period (unless delayed by finalizers). Other + management of the container blocks until the + hook completes or until the termination grace + period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to + take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name. This will be canonicalized + upon output, so case-variant + names will be understood as + the same header. + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT + supported as a LifecycleHandler and kept + for the backward compatibility. There + are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler + is specified. + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving + a GRPC port. + properties: + port: + description: Port number of the gRPC service. + Number must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the + service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default + behavior is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name. + This will be canonicalized upon + output, so case-variant names will + be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the + pod needs to terminate gracefully upon probe + failure. The grace period is the duration + in seconds after the processes running in + the pod are sent a termination signal and + the time when the processes are forcibly halted + with a kill signal. Set this value longer + than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides + the value provided by the pod spec. Value + must be non-negative integer. The value zero + indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta + field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as + a DNS_LABEL. Each container in a pod must have + a unique name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Not specifying a port here DOES NOT prevent that + port from being exposed. Any port which is listening + on the default "0.0.0.0" address inside a container + will be accessible from the network. Modifying + this array with strategic merge patch may corrupt + the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid + port number, 0 < x < 65536. If HostNetwork + is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an + IANA_SVC_NAME and unique within the pod. + Each named port in a pod must have a unique + name. Name for the port that can be referred + to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service + readiness. Container will be removed from service + endpoints if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving + a GRPC port. + properties: + port: + description: Port number of the gRPC service. + Number must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the + service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default + behavior is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name. + This will be canonicalized upon + output, so case-variant names will + be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the + pod needs to terminate gracefully upon probe + failure. The grace period is the duration + in seconds after the processes running in + the pod are sent a termination signal and + the time when the processes are forcibly halted + with a kill signal. Set this value longer + than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides + the value provided by the pod spec. Value + must be non-negative integer. The value zero + indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta + field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents + resource resize policy for the container. + properties: + resourceName: + description: 'Name of the resource to which + this resource resize policy applies. Supported + values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when + specified resource is resized. If not specified, + it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this + container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate. \n 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 + restartPolicy: + description: 'RestartPolicy defines the restart + behavior of individual containers in a pod. This + field may only be set for init containers, and + the only allowed value is "Always". For non-init + containers or when this field is not specified, + the restart behavior is defined by the Pod''s + restart policy and the container type. Setting + the RestartPolicy as "Always" for the init container + will have the following effect: this init container + will be continually restarted on exit until all + regular containers have terminated. Once all regular + containers have completed, all init containers + with restartPolicy "Always" will be shut down. + This lifecycle differs from normal init containers + and is often referred to as a "sidecar" container. + Although this init container still starts in the + init container sequence, it does not wait for + the container to complete before proceeding to + the next init container. Instead, the next init + container starts immediately after this init container + is started, or after any startupProbe has successfully + completed.' + type: string + securityContext: + description: 'SecurityContext defines the security + options the container should be run with. If set, + the fields of SecurityContext override the equivalent + fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + 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: \n 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 + startupProbe: + description: 'StartupProbe indicates that the Pod + has successfully initialized. If specified, no + other probes are executed until this completes + successfully. If this probe fails, the Pod will + be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it + might take a long time to load data or warm a + cache, than during steady-state operation. This + cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving + a GRPC port. + properties: + port: + description: Port number of the gRPC service. + Number must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the + service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default + behavior is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name. + This will be canonicalized upon + output, so case-variant names will + be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the + pod needs to terminate gracefully upon probe + failure. The grace period is the duration + in seconds after the processes running in + the pod are sent a termination signal and + the time when the processes are forcibly halted + with a kill signal. Set this value longer + than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides + the value provided by the pod spec. Value + must be non-negative integer. The value zero + indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta + field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is + opened on container start, is empty until the + first client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains closed + until the container is restarted. If this flag + is false, a container processes that reads from + stdin will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final + status, such as an assertion failure message. + Will be truncated by the node if greater than + 4096 bytes. The total message length across all + containers will be limited to 12kb. Defaults to + /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output + is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be + true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block + devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside + of the container that the device will be + mapped to. + type: string + name: + description: name must match the name of a + persistentVolumeClaim in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container at + which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, + MountPropagationNone is used. This field + is beta in 1.10. + type: string + name: + description: This must match the Name of a + Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should + be mounted. Behaves similarly to SubPath + but environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + pod: + description: Pod defines the desired specification of + pod. + properties: + affinity: + description: If specified, the pod's 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. + 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 + 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. + 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 + 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. + 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 + 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. + 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 + 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: Annotations are the annotations that + should be appended to the pods. By default, no pod + annotations are appended. + type: object + hostNetwork: + description: HostNetwork, If this is set to true, + the pod will use host's network namespace. + type: boolean + labels: + additionalProperties: + type: string + description: Labels are the additional labels that + should be tagged to the pods. By default, no additional + pod labels are tagged. + type: object + securityContext: + description: 'SecurityContext holds pod-level security + attributes and common container settings. Optional: + Defaults to empty. See type description for default + values of each field.' + 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: \n 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---- \n 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: \n 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 + 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 + volumes: + description: 'Volumes that can be mounted by containers + belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' + items: + description: Volume represents a named volume in + a pod that may be accessed by any container in + the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents + an AWS Disk resource that is attached to a + kubelet''s host machine and then exposed to + the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type + of the volume that you want to mount. + Tip: Ensure that the filesystem type is + supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + partition: + description: 'partition is the partition + in the volume that you want to mount. + If omitted, the default is to mount by + volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, + the volume partition for /dev/sda is "0" + (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force + the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the + persistent disk resource in AWS (Amazon + EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data + Disk mount on the host and bind mount to the + pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching + mode: None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the + data disk in the blob storage + type: string + diskURI: + description: diskURI is the URI of data + disk in the blob storage + type: string + fsType: + description: fsType is Filesystem type to + mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: + multiple blob disks per storage account Dedicated: + single blob disk per storage account Managed: + azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: readOnly Defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File + Service mount on the host and bind mount to + the pod. + properties: + readOnly: + description: readOnly defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of + secret that contains Azure Storage Account + Name and Key + type: string + shareName: + description: shareName is the azure share + Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount + on the host that shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors + is a collection of Ceph monitors More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as + the mounted root, rather than the full + Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults + to false (read/write). ReadOnly here will + force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile + is the path to key ring for User, default + is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef + is reference to the authentication secret + for User, default is empty. More info: + https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + 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 + user: + description: 'user is optional: User is + the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume + attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type + to mount. Must be a filesystem type supported + by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points + to a secret object containing parameters + used to connect to OpenStack.' + 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 + volumeID: + description: 'volumeID used to identify + the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap + that should populate this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode + bits used to set permissions on created + files by default. Must be an octal value + between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. + Directories within the path are not affected + by this setting. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: items if unspecified, each + key-value pair in the Data field of the + referenced ConfigMap will be projected + into the volume as a file whose name is + the key and content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or start + with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on + this file. Must be an octal value + between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts + both octal and decimal values, JSON + requires decimal values for mode + bits. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options + that affect the file mode, like + fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: path is the relative + path of the file to map the key + to. May not be an absolute path. + May not contain the path element + '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + 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: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) + represents ephemeral storage that is handled + by certain external CSI drivers (Beta feature). + properties: + driver: + description: driver is the name of the CSI + driver that handles this volume. Consult + with your admin for the correct name as + registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", + "xfs", "ntfs". If not provided, the empty + value is passed to the associated CSI + driver which will determine the default + filesystem to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference + to the secret object containing sensitive + information to pass to the CSI driver + to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field + is optional, and may be empty if no secret + is required. If the secret object contains + more than one secret, all secret references + are passed. + 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 + readOnly: + description: readOnly specifies a read-only + configuration for the volume. Defaults + to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific + properties that are passed to the CSI + driver. Consult your driver's documentation + for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward + API about the pod that should populate this + volume + properties: + defaultMode: + description: 'Optional: mode bits to use + on created files by default. Must be a + Optional: mode bits used to set permissions + on created files by default. Must be an + octal value between 0000 and 0777 or a + decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. Defaults to 0644. Directories within + the path are not affected by this setting. + This might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: Items is a list of downward + API volume file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a + field of the pod: only annotations, + labels, name and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits + used to set permissions on this + file, must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, JSON + requires decimal values for mode + bits. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options + that affect the file mode, like + fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute + or contain the ''..'' path. Must + be utf-8 encoded. The first item + of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of + the container: only resources limits + and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) + are currently supported.' + 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 + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary + directory that shares a pod''s lifetime. More + info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type + of storage medium should back this directory. + The default is "" which means to use the + node''s default medium. Must be an empty + string (default) or Memory. More info: + https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount + of local storage required for this EmptyDir + volume. The size limit is also applicable + for memory medium. The maximum usage on + memory medium EmptyDir would be the minimum + value between the SizeLimit specified + here and the sum of memory limits of all + containers in a pod. The default is nil + which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume + that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod + that defines it - it will be created before + the pod starts, and deleted when the pod is + removed. \n Use this if: a) the volume is + only needed while the pod runs, b) features + of normal volumes like restoring from snapshot + or capacity tracking are needed, c) the storage + driver is specified through a storage class, + and d) the storage driver supports dynamic + volume provisioning through a PersistentVolumeClaim + (see EphemeralVolumeSource for more information + on the connection between this volume type + and PersistentVolumeClaim). \n Use PersistentVolumeClaim + or one of the vendor-specific APIs for volumes + that persist for longer than the lifecycle + of an individual pod. \n Use CSI for light-weight + local ephemeral volumes if the CSI driver + is meant to be used that way - see the documentation + of the driver for more information. \n A pod + can use both types of ephemeral volumes and + persistent volumes at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone + PVC to provision the volume. The pod in + which this EphemeralVolumeSource is embedded + will be the owner of the PVC, i.e. the + PVC will be deleted together with the + pod. The name of the PVC will be `-` where `` + is the name from the `PodSpec.Volumes` + array entry. Pod validation will reject + the pod if the concatenated name is not + valid for a PVC (for example, too long). + \n An existing PVC with that name that + is not owned by the pod will *not* be + used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is + then blocked until the unrelated PVC is + removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has + to updated with an owner reference to + the pod once the pod exists. Normally + this should not be necessary, but it may + be useful when manually reconstructing + a broken cluster. \n This field is read-only + and no changes will be made by Kubernetes + to the PVC after it has been created. + \n Required, must not be nil." + properties: + metadata: + description: May contain labels and + annotations that will be copied into + the PVC when creating it. No other + fields are allowed and will be rejected + during validation. + type: object + spec: + description: The specification for the + PersistentVolumeClaim. The entire + content is copied unchanged into the + PVC that gets created from this template. + The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: 'accessModes contains + the desired access modes the volume + should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can + be used to specify either: * An + existing VolumeSnapshot object + (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external + controller can support the specified + data source, it will create a + new volume based on the contents + of the specified data source. + When the AnyVolumeDataSource feature + gate is enabled, dataSource contents + will be copied to dataSourceRef, + and dataSourceRef contents will + be copied to dataSource when dataSourceRef.namespace + is not specified. If the namespace + is specified, then dataSourceRef + will not be copied to dataSource.' + properties: + apiGroup: + description: APIGroup is the + group for the resource being + referenced. If APIGroup is + not specified, the specified + Kind must be in the core API + group. For any other third-party + types, APIGroup is required. + type: string + kind: + description: Kind is the type + of resource being referenced + type: string + name: + description: Name is the name + of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: 'dataSourceRef specifies + the object from which to populate + the volume with data, if a non-empty + volume is desired. This may be + any object from a non-empty API + group (non core object) or a PersistentVolumeClaim + object. When this field is specified, + volume binding will only succeed + if the type of the specified object + matches some installed volume + populator or dynamic provisioner. + This field will replace the functionality + of the dataSource field and as + such if both fields are non-empty, + they must have the same value. + For backwards compatibility, when + namespace isn''t specified in + dataSourceRef, both fields (dataSource + and dataSourceRef) will be set + to the same value automatically + if one of them is empty and the + other is non-empty. When namespace + is specified in dataSourceRef, + dataSource isn''t set to the same + value and must be empty. There + are three important differences + between dataSource and dataSourceRef: + * While dataSource only allows + two specific types of objects, + dataSourceRef allows any non-core + object, as well as PersistentVolumeClaim + objects. * While dataSource ignores + disallowed values (dropping them), + dataSourceRef preserves all values, + and generates an error if a disallowed + value is specified. * While dataSource + only allows local objects, dataSourceRef + allows objects in any namespaces. + (Beta) Using this field requires + the AnyVolumeDataSource feature + gate to be enabled. (Alpha) Using + the namespace field of dataSourceRef + requires the CrossNamespaceVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the + group for the resource being + referenced. If APIGroup is + not specified, the specified + Kind must be in the core API + group. For any other third-party + types, APIGroup is required. + type: string + kind: + description: Kind is the type + of resource being referenced + type: string + name: + description: Name is the name + of resource being referenced + type: string + namespace: + description: Namespace is the + namespace of resource being + referenced Note that when + a namespace is specified, + a gateway.networking.k8s.io/ReferenceGrant + object is required in the + referent namespace to allow + that namespace's owner to + accept the reference. See + the ReferenceGrant documentation + for details. (Alpha) This + field requires the CrossNamespaceVolumeDataSource + feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents + the minimum resources the volume + should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed + to specify resource requirements + that are lower than previous value + but must still be higher than + capacity recorded in the status + field of the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + claims: + description: "Claims lists the + names of resources, defined + in spec.resourceClaims, that + are used by this container. + \n This is an alpha field + and requires enabling the + DynamicResourceAllocation + feature gate. \n 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 + selector: + description: selector is a label + query over volumes to consider + for binding. + 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 + storageClassName: + description: 'storageClassName is + the name of the StorageClass required + by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines + what type of volume is required + by the claim. Value of Filesystem + is implied when not included in + claim spec. + type: string + volumeName: + description: volumeName is the binding + reference to the PersistentVolume + backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource + that is attached to a kubelet's host machine + and then exposed to the pod. + properties: + fsType: + description: 'fsType is the filesystem type + to mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. TODO: how do + we prevent errors in the filesystem from + compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target + lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults + to false (read/write). ReadOnly here will + force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC + target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume + world wide identifiers (wwids) Either + wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic + volume resource that is provisioned/attached + using an exec based plugin. + properties: + driver: + description: driver is the name of the driver + to use for this volume. + type: string + fsType: + description: fsType is the filesystem type + to mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem + depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this + field holds extra command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults + to false (read/write). ReadOnly here will + force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef + is reference to the secret object containing + sensitive information to pass to the plugin + scripts. This may be empty if no secret + object is specified. If the secret object + contains more than one secret, all secrets + are passed to the plugin scripts.' + 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 + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume + attached to a kubelet's host machine. This + depends on the Flocker control service being + running + properties: + datasetName: + description: datasetName is Name of the + dataset stored as metadata -> name on + the dataset for Flocker should be considered + as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of + the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a + GCE Disk resource that is attached to a kubelet''s + host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type + of the volume that you want to mount. + Tip: Ensure that the filesystem type is + supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + partition: + description: 'partition is the partition + in the volume that you want to mount. + If omitted, the default is to mount by + volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, + the volume partition for /dev/sda is "0" + (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the + PD resource in GCE. Used to identify the + disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the + ReadOnly setting in VolumeMounts. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository + at a particular revision. DEPRECATED: GitRepo + is deprecated. To provision a container with + a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount + the EmptyDir into the Pod''s container.' + properties: + directory: + description: directory is the target directory + name. Must not contain or start with '..'. If + '.' is supplied, the volume directory + will be the git repository. Otherwise, + if specified, the volume will contain + the git repository in the subdirectory + with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash + for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs + mount on the host that shares a pod''s lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint + name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume + path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the + Glusterfs volume to be mounted with read-only + permissions. Defaults to false. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing + file or directory on the host machine that + is directly exposed to the container. This + is generally used for system agents or other + privileged things that are allowed to see + the host machine. Most containers will NOT + need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who + can use host directory mounts and who can/can + not mount host directories as read/write.' + properties: + path: + description: 'path of the directory on the + host. If the path is a symlink, it will + follow the link to the real path. More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults + to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk + resource that is attached to a kubelet''s + host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether + support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether + support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type + of the volume that you want to mount. + Tip: Ensure that the filesystem type is + supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom + iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, + new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified + Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface + Name that uses an iSCSI transport. Defaults + to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target + Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target + Portal List. The portal is either an IP + or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the + ReadOnly setting in VolumeMounts. Defaults + to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret + for iSCSI target and initiator authentication + 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 + targetPortal: + description: targetPortal is iSCSI Target + Portal. The Portal is either an IP or + ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a + DNS_LABEL and unique within the pod. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on + the host that shares a pod''s lifetime More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the + NFS export to be mounted with read-only + permissions. Defaults to false. More info: + https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or + IP address of the NFS server. More info: + https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource + represents a reference to a PersistentVolumeClaim + in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a + PersistentVolumeClaim in the same namespace + as the pod using this volume. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly + setting in VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents + a PhotonController persistent disk attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type + to mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies + Photon Controller persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx + volume attached and mounted on kubelets host + machine + properties: + fsType: + description: fSType represents the filesystem + type to mount Must be a filesystem type + supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies + a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one + resources secrets, configmaps, and downward + API + properties: + defaultMode: + description: defaultMode are the mode bits + used to set permissions on created files + by default. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal + values for mode bits. Directories within + the path are not affected by this setting. + This might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits + set. + format: int32 + type: integer + sources: + description: sources is the list of volume + projections + items: + description: Projection that may be projected + along with other supported volume types + properties: + configMap: + description: configMap information + about the configMap data to project + properties: + items: + description: items if unspecified, + each key-value pair in the Data + field of the referenced ConfigMap + will be projected into the volume + as a file whose name is the + key and content is the value. + If specified, the listed keys + will be projected into the specified + paths, and unlisted keys will + not be present. If a key is + specified which is not present + in the ConfigMap, the volume + setup will error unless it is + marked optional. Paths must + be relative and may not contain + the '..' path or start with + '..'. + items: + description: Maps a string key + to a path within a volume. + properties: + key: + description: key is the + key to project. + type: string + mode: + description: 'mode is Optional: + mode bits used to set + permissions on this file. + Must be an octal value + between 0000 and 0777 + or a decimal value between + 0 and 511. YAML accepts + both octal and decimal + values, JSON requires + decimal values for mode + bits. If not specified, + the volume defaultMode + will be used. This might + be in conflict with other + options that affect the + file mode, like fsGroup, + and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: path is the + relative path of the file + to map the key to. May + not be an absolute path. + May not contain the path + element '..'. May not + start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + 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: optional specify + whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information + about the downwardAPI data to project + properties: + items: + description: Items is a list of + DownwardAPIVolume file + items: + description: DownwardAPIVolumeFile + represents information to + create the file containing + the pod field + properties: + fieldRef: + description: 'Required: + Selects a field of the + pod: only annotations, + labels, name and namespace + are supported.' + 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 + mode: + description: 'Optional: + mode bits used to set + permissions on this file, + must be an octal value + between 0000 and 0777 + or a decimal value between + 0 and 511. YAML accepts + both octal and decimal + values, JSON requires + decimal values for mode + bits. If not specified, + the volume defaultMode + will be used. This might + be in conflict with other + options that affect the + file mode, like fsGroup, + and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: + Path is the relative + path name of the file + to be created. Must not + be absolute or contain + the ''..'' path. Must + be utf-8 encoded. The + first item of the relative + path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a + resource of the container: + only resources limits + and requests (limits.cpu, + limits.memory, requests.cpu + and requests.memory) are + currently supported.' + 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 + required: + - path + type: object + type: array + type: object + secret: + description: secret information about + the secret data to project + properties: + items: + description: items if unspecified, + each key-value pair in the Data + field of the referenced Secret + will be projected into the volume + as a file whose name is the + key and content is the value. + If specified, the listed keys + will be projected into the specified + paths, and unlisted keys will + not be present. If a key is + specified which is not present + in the Secret, the volume setup + will error unless it is marked + optional. Paths must be relative + and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key + to a path within a volume. + properties: + key: + description: key is the + key to project. + type: string + mode: + description: 'mode is Optional: + mode bits used to set + permissions on this file. + Must be an octal value + between 0000 and 0777 + or a decimal value between + 0 and 511. YAML accepts + both octal and decimal + values, JSON requires + decimal values for mode + bits. If not specified, + the volume defaultMode + will be used. This might + be in conflict with other + options that affect the + file mode, like fsGroup, + and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: path is the + relative path of the file + to map the key to. May + not be an absolute path. + May not contain the path + element '..'. May not + start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + 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: optional field specify + whether the Secret or its key + must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is + information about the serviceAccountToken + data to project + properties: + audience: + description: audience is the intended + audience of the token. A recipient + of a token must identify itself + with an identifier specified + in the audience of the token, + and otherwise should reject + the token. The audience defaults + to the identifier of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds + is the requested duration of + validity of the service account + token. As the token approaches + expiration, the kubelet volume + plugin will proactively rotate + the service account token. The + kubelet will start trying to + rotate the token if the token + is older than 80 percent of + its time to live or if the token + is older than 24 hours.Defaults + to 1 hour and must be at least + 10 minutes. + format: int64 + type: integer + path: + description: path is the path + relative to the mount point + of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount + on the host that shares a pod's lifetime + properties: + group: + description: group to map volume access + to Default is no group + type: string + readOnly: + description: readOnly here will force the + Quobyte volume to be mounted with read-only + permissions. Defaults to false. + type: boolean + registry: + description: registry represents a single + or multiple Quobyte Registry services + specified as a string as host:port pair + (multiple entries are separated with commas) + which acts as the central registry for + volumes + type: string + tenant: + description: tenant owning the given Quobyte + volume in the Backend Used with dynamically + provisioned Quobyte volumes, value is + set by the plugin + type: string + user: + description: user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references + an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device + mount on the host that shares a pod''s lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type + of the volume that you want to mount. + Tip: Ensure that the filesystem type is + supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + image: + description: 'image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key + ring for RBDUser. Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of + Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. + Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the + ReadOnly setting in VolumeMounts. Defaults + to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication + secret for RBDUser. If provided overrides + keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + 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 + user: + description: 'user is the rados user name. + Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent + volume attached and mounted on Kubernetes + nodes. + properties: + fsType: + description: fsType is the filesystem type + to mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address + of the ScaleIO API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name + of the ScaleIO Protection Domain for the + configured storage. + type: string + readOnly: + description: readOnly Defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the + secret for ScaleIO user and other sensitive + information. If this is not provided, + Login operation will fail. + 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 + sslEnabled: + description: sslEnabled Flag enable/disable + SSL communication with Gateway, default + false + type: boolean + storageMode: + description: storageMode indicates whether + the storage for a volume should be ThickProvisioned + or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO + Storage Pool associated with the protection + domain. + type: string + system: + description: system is the name of the storage + system as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a + volume already created in the ScaleIO + system that is associated with this volume + source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that + should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode + bits used to set permissions on created + files by default. Must be an octal value + between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. + Directories within the path are not affected + by this setting. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: items If unspecified, each + key-value pair in the Data field of the + referenced Secret will be projected into + the volume as a file whose name is the + key and content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, the + volume setup will error unless it is marked + optional. Paths must be relative and may + not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on + this file. Must be an octal value + between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts + both octal and decimal values, JSON + requires decimal values for mode + bits. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options + that affect the file mode, like + fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: path is the relative + path of the file to map the key + to. May not be an absolute path. + May not contain the path element + '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether + the Secret or its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of + the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS + volume attached and mounted on Kubernetes + nodes. + properties: + fsType: + description: fsType is the filesystem type + to mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret + to use for obtaining the StorageOS API + credentials. If not specified, default + values will be attempted. + 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 + volumeName: + description: volumeName is the human-readable + name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the + scope of the volume within StorageOS. If + no namespace is specified then the Pod's + namespace will be used. This allows the + Kubernetes name scoping to be mirrored + within StorageOS for tighter integration. + Set VolumeName to any name to override + the default behaviour. Set to "default" + if you are not using namespaces within + StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere + volume attached and mounted on kubelets host + machine + properties: + fsType: + description: fsType is filesystem type to + mount. Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage + Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage + Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: volumePath is the path that + identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + replicas: + description: Replicas is the number of desired pods. Defaults + to 1. + format: int32 + type: integer + strategy: + description: The deployment strategy to use to replace + existing pods with new ones. + properties: + rollingUpdate: + description: 'Rolling update config params. Present + only if DeploymentStrategyType = RollingUpdate. + --- TODO: Update this to follow our convention for + oneOf, whatever we decide it to be.' + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: 'The maximum number of pods that + can be scheduled above the desired number of + pods. Value can be an absolute number (ex: 5) + or a percentage of desired pods (ex: 10%). This + can not be 0 if MaxUnavailable is 0. Absolute + number is calculated from percentage by rounding + up. Defaults to 25%. Example: when this is set + to 30%, the new ReplicaSet can be scaled up + immediately when the rolling update starts, + such that the total number of old and new pods + do not exceed 130% of desired pods. Once old + pods have been killed, new ReplicaSet can be + scaled up further, ensuring that total number + of pods running at any time during the update + is at most 130% of desired pods.' + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: 'The maximum number of pods that + can be unavailable during the update. Value + can be an absolute number (ex: 5) or a percentage + of desired pods (ex: 10%). Absolute number is + calculated from percentage by rounding down. + This can not be 0 if MaxSurge is 0. Defaults + to 25%. Example: when this is set to 30%, the + old ReplicaSet can be scaled down to 70% of + desired pods immediately when the rolling update + starts. Once new pods are ready, old ReplicaSet + can be scaled down further, followed by scaling + up the new ReplicaSet, ensuring that the total + number of pods available at all times during + the update is at least 70% of desired pods.' + x-kubernetes-int-or-string: true + type: object + type: + description: Type of deployment. Can be "Recreate" + or "RollingUpdate". Default is RollingUpdate. + type: string + type: object + type: object + envoyHpa: + description: EnvoyHpa defines the Horizontal Pod Autoscaler + settings for Envoy Proxy Deployment. Once the HPA is being + set, Replicas field from EnvoyDeployment will be ignored. + properties: + behavior: + description: behavior configures the scaling behavior + of the target in both Up and Down directions (scaleUp + and scaleDown fields respectively). If not set, the + default HPAScalingRules for scale up and scale down + are used. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. + properties: + scaleDown: + description: scaleDown is scaling policy for scaling + Down. If not set, the default value is to allow + to scale down to minReplicas pods, with a 300 second + stabilization window (i.e., the highest recommendation + for the last 300sec is used). + properties: + policies: + description: policies is a list of potential scaling + polices which can be used during scaling. At + least one policy must be specified, otherwise + the HPAScalingRules will be discarded as invalid + items: + description: HPAScalingPolicy is a single policy + which must hold true for a specified past + interval. + properties: + periodSeconds: + description: periodSeconds specifies the + window of time for which the policy should + hold true. PeriodSeconds must be greater + than zero and less than or equal to 1800 + (30 min). + format: int32 + type: integer + type: + description: type is used to specify the + scaling policy. + type: string + value: + description: value contains the amount of + change which is permitted by the policy. + It must be greater than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which + policy should be used. If not set, the default + value Max is used. + type: string + stabilizationWindowSeconds: + description: 'stabilizationWindowSeconds is the + number of seconds for which past recommendations + should be considered while scaling up or scaling + down. StabilizationWindowSeconds must be greater + than or equal to zero and less than or equal + to 3600 (one hour). If not set, use the default + values: - For scale up: 0 (i.e. no stabilization + is done). - For scale down: 300 (i.e. the stabilization + window is 300 seconds long).' + format: int32 + type: integer + type: object + scaleUp: + description: 'scaleUp is scaling policy for scaling + Up. If not set, the default value is the higher + of: * increase no more than 4 pods per 60 seconds + * double the number of pods per 60 seconds No stabilization + is used.' + properties: + policies: + description: policies is a list of potential scaling + polices which can be used during scaling. At + least one policy must be specified, otherwise + the HPAScalingRules will be discarded as invalid + items: + description: HPAScalingPolicy is a single policy + which must hold true for a specified past + interval. + properties: + periodSeconds: + description: periodSeconds specifies the + window of time for which the policy should + hold true. PeriodSeconds must be greater + than zero and less than or equal to 1800 + (30 min). + format: int32 + type: integer + type: + description: type is used to specify the + scaling policy. + type: string + value: + description: value contains the amount of + change which is permitted by the policy. + It must be greater than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which + policy should be used. If not set, the default + value Max is used. + type: string + stabilizationWindowSeconds: + description: 'stabilizationWindowSeconds is the + number of seconds for which past recommendations + should be considered while scaling up or scaling + down. StabilizationWindowSeconds must be greater + than or equal to zero and less than or equal + to 3600 (one hour). If not set, use the default + values: - For scale up: 0 (i.e. no stabilization + is done). - For scale down: 300 (i.e. the stabilization + window is 300 seconds long).' + format: int32 + type: integer + type: object + type: object + maxReplicas: + description: maxReplicas is the upper limit for the number + of replicas to which the autoscaler can scale up. It + cannot be less that minReplicas. + format: int32 + type: integer + metrics: + description: metrics contains the specifications for which + to use to calculate the desired replica count (the maximum + replica count across all metrics will be used). If left + empty, it defaults to being based on CPU utilization + with average on 80% usage. + items: + description: MetricSpec specifies how to scale based + on a single metric (only `type` and one other matching + field should be set at once). + properties: + containerResource: + description: containerResource refers to a resource + metric (such as those specified in requests and + limits) known to Kubernetes describing a single + container in each pod of the current scale target + (e.g. CPU or memory). Such metrics are built in + to Kubernetes, and have special scaling options + on top of those available to normal per-pod metrics + using the "pods" source. This is an alpha feature + and can be enabled by the HPAContainerMetrics + feature flag. + properties: + container: + description: container is the name of the container + in the pods of the scaling target + type: string + name: + description: name is the name of the resource + in question. + type: string + target: + description: target specifies the target value + for the given metric + properties: + averageUtilization: + description: averageUtilization is the target + value of the average of the resource metric + across all relevant pods, represented + as a percentage of the requested value + of the resource for the pods. Currently + only valid for Resource metric source + type + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target + value of the average of the metric across + all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the + metric type is Utilization, Value, or + AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of + the metric (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - container + - name + - target + type: object + external: + description: external refers to a global metric + that is not associated with any Kubernetes object. + It allows autoscaling based on information coming + from components running outside of cluster (for + example length of queue in cloud messaging service, + or QPS from loadbalancer running outside of cluster). + properties: + metric: + description: metric identifies the target metric + by name and selector + properties: + name: + description: name is the name of the given + metric + type: string + selector: + description: selector is the string-encoded + form of a standard kubernetes label selector + for the given metric When set, it is passed + as an additional parameter to the metrics + server for more specific metrics scoping. + When unset, just the metricName will be + used to gather metrics. + 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 + required: + - name + type: object + target: + description: target specifies the target value + for the given metric + properties: + averageUtilization: + description: averageUtilization is the target + value of the average of the resource metric + across all relevant pods, represented + as a percentage of the requested value + of the resource for the pods. Currently + only valid for Resource metric source + type + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target + value of the average of the metric across + all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the + metric type is Utilization, Value, or + AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of + the metric (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + object: + description: object refers to a metric describing + a single kubernetes object (for example, hits-per-second + on an Ingress object). + properties: + describedObject: + description: describedObject specifies the descriptions + of a object,such as kind,name apiVersion + properties: + apiVersion: + description: apiVersion is the API version + of the referent + type: string + kind: + description: 'kind is the kind of the referent; + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'name is the name of the referent; + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + required: + - kind + - name + type: object + metric: + description: metric identifies the target metric + by name and selector + properties: + name: + description: name is the name of the given + metric + type: string + selector: + description: selector is the string-encoded + form of a standard kubernetes label selector + for the given metric When set, it is passed + as an additional parameter to the metrics + server for more specific metrics scoping. + When unset, just the metricName will be + used to gather metrics. + 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 + required: + - name + type: object + target: + description: target specifies the target value + for the given metric + properties: + averageUtilization: + description: averageUtilization is the target + value of the average of the resource metric + across all relevant pods, represented + as a percentage of the requested value + of the resource for the pods. Currently + only valid for Resource metric source + type + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target + value of the average of the metric across + all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the + metric type is Utilization, Value, or + AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of + the metric (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + description: pods refers to a metric describing + each pod in the current scale target (for example, + transactions-processed-per-second). The values + will be averaged together before being compared + to the target value. + properties: + metric: + description: metric identifies the target metric + by name and selector + properties: + name: + description: name is the name of the given + metric + type: string + selector: + description: selector is the string-encoded + form of a standard kubernetes label selector + for the given metric When set, it is passed + as an additional parameter to the metrics + server for more specific metrics scoping. + When unset, just the metricName will be + used to gather metrics. + 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 + required: + - name + type: object + target: + description: target specifies the target value + for the given metric + properties: + averageUtilization: + description: averageUtilization is the target + value of the average of the resource metric + across all relevant pods, represented + as a percentage of the requested value + of the resource for the pods. Currently + only valid for Resource metric source + type + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target + value of the average of the metric across + all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the + metric type is Utilization, Value, or + AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of + the metric (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + resource: + description: resource refers to a resource metric + (such as those specified in requests and limits) + known to Kubernetes describing each pod in the + current scale target (e.g. CPU or memory). Such + metrics are built in to Kubernetes, and have special + scaling options on top of those available to normal + per-pod metrics using the "pods" source. + properties: + name: + description: name is the name of the resource + in question. + type: string + target: + description: target specifies the target value + for the given metric + properties: + averageUtilization: + description: averageUtilization is the target + value of the average of the resource metric + across all relevant pods, represented + as a percentage of the requested value + of the resource for the pods. Currently + only valid for Resource metric source + type + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target + value of the average of the metric across + all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the + metric type is Utilization, Value, or + AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of + the metric (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - name + - target + type: object + type: + description: 'type is the type of metric source. It + should be one of "ContainerResource", "External", + "Object", "Pods" or "Resource", each mapping to + a matching field in the object. Note: "ContainerResource" + type is available on when the feature-gate HPAContainerMetrics + is enabled' + type: string + required: + - type + type: object + type: array + minReplicas: + description: minReplicas is the lower limit for the number + of replicas to which the autoscaler can scale down. + It defaults to 1 replica. + format: int32 + type: integer + required: + - maxReplicas + type: object + x-kubernetes-validations: + - message: minReplicas must be greater than 0 + rule: '!has(self.minReplicas) || self.minReplicas > 0' + - message: maxReplicas must be greater than 0 + rule: '!has(self.maxReplicas) || self.maxReplicas > 0' + - message: maxReplicas cannot be less than or equal to minReplicas + rule: '!has(self.minReplicas) || self.maxReplicas > self.minReplicas' + envoyService: + description: EnvoyService defines the desired state of the + Envoy service resource. If unspecified, default settings + for the managed Envoy service resource are applied. + properties: + allocateLoadBalancerNodePorts: + description: AllocateLoadBalancerNodePorts defines if + NodePorts will be automatically allocated for services + with type LoadBalancer. Default is "true". It may be + set to "false" if the cluster load-balancer does not + rely on NodePorts. If the caller requests specific NodePorts + (by specifying a value), those requests will be respected, + regardless of this field. This field may only be set + for services with type LoadBalancer and will be cleared + if the type is changed to any other type. + type: boolean + annotations: + additionalProperties: + type: string + description: Annotations that should be appended to the + service. By default, no annotations are appended. + type: object + loadBalancerClass: + description: LoadBalancerClass, when specified, allows + for choosing the LoadBalancer provider implementation + if more than one are available or is otherwise expected + to be specified + type: string + loadBalancerIP: + description: LoadBalancerIP defines the IP Address of + the underlying load balancer service. This field may + be ignored if the load balancer provider does not support + this feature. This field has been deprecated in Kubernetes, + but it is still used for setting the IP Address in some + cloud providers such as GCP. + type: string + type: + default: LoadBalancer + description: Type determines how the Service is exposed. + Defaults to LoadBalancer. Valid options are ClusterIP, + LoadBalancer and NodePort. "LoadBalancer" means a service + will be exposed via an external load balancer (if the + cloud provider supports it). "ClusterIP" means a service + will only be accessible inside the cluster, via the + cluster IP. "NodePort" means a service will be exposed + on a static Port on all Nodes of the cluster. + enum: + - ClusterIP + - LoadBalancer + - NodePort + type: string + type: object + x-kubernetes-validations: + - message: allocateLoadBalancerNodePorts can only be set for + LoadBalancer type + rule: '!has(self.allocateLoadBalancerNodePorts) || self.type + == ''LoadBalancer''' + - message: loadBalancerIP can only be set for LoadBalancer + type + rule: '!has(self.loadBalancerIP) || self.type == ''LoadBalancer''' + - message: loadBalancerIP must be a valid IPv4 address + rule: '!has(self.loadBalancerIP) || self.loadBalancerIP.matches(r"^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})")' + type: object + type: + description: Type is the type of resource provider to use. A resource + provider provides infrastructure resources for running the data + plane, e.g. Envoy proxy, and optional auxiliary control planes. + Supported types are "Kubernetes". + enum: + - Kubernetes + type: string + required: + - type + type: object + telemetry: + description: Telemetry defines telemetry parameters for managed proxies. + properties: + accessLog: + description: AccessLogs defines accesslog parameters for managed + proxies. If unspecified, will send default format to stdout. + properties: + disable: + description: Disable disables access logging for managed proxies + if set to true. + type: boolean + settings: + description: Settings defines accesslog settings for managed + proxies. If unspecified, will send default format to stdout. + items: + properties: + format: + description: Format defines the format of accesslog. + properties: + json: + additionalProperties: + type: string + description: JSON is additional attributes that + describe the specific event occurrence. Structured + format for the envoy access logs. Envoy [command + operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) + can be used as values for fields within the Struct. + It's required when the format type is "JSON". + type: object + text: + description: Text defines the text accesslog format, + following Envoy accesslog formatting, It's required + when the format type is "Text". Envoy [command + operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) + may be used in the format. The [format string + documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) + provides more information. + type: string + type: + description: Type defines the type of accesslog + format. + enum: + - Text + - JSON + type: string + type: object + x-kubernetes-validations: + - message: If AccessLogFormat type is Text, text field + needs to be set. + rule: 'self.type == ''Text'' ? has(self.text) : !has(self.text)' + - message: If AccessLogFormat type is JSON, json field + needs to be set. + rule: 'self.type == ''JSON'' ? has(self.json) : !has(self.json)' + sinks: + description: Sinks defines the sinks of accesslog. + items: + description: ProxyAccessLogSink defines the sink of + accesslog. + properties: + file: + description: File defines the file accesslog sink. + properties: + path: + description: Path defines the file path used + to expose envoy access log(e.g. /dev/stdout). + minLength: 1 + type: string + type: object + openTelemetry: + description: OpenTelemetry defines the OpenTelemetry + accesslog sink. + properties: + host: + description: Host define the extension service + hostname. + type: string + port: + default: 4317 + description: Port defines the port the extension + service is exposed on. + format: int32 + minimum: 0 + type: integer + resources: + additionalProperties: + type: string + description: Resources is a set of labels + that describe the source of a log entry, + including envoy node info. It's recommended + to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). + type: object + required: + - host + type: object + type: + description: Type defines the type of accesslog + sink. + enum: + - File + - OpenTelemetry + type: string + type: object + x-kubernetes-validations: + - message: If AccessLogSink type is File, file field + needs to be set. + rule: 'self.type == ''File'' ? has(self.file) : + !has(self.file)' + - message: If AccessLogSink type is OpenTelemetry, + openTelemetry field needs to be set. + rule: 'self.type == ''OpenTelemetry'' ? has(self.openTelemetry) + : !has(self.openTelemetry)' + minItems: 1 + type: array + required: + - format + - sinks + type: object + type: array + type: object + metrics: + description: Metrics defines metrics configuration for managed + proxies. + properties: + enableVirtualHostStats: + description: EnableVirtualHostStats enables envoy stat metrics + for virtual hosts. + type: boolean + matches: + description: 'Matches defines configuration for selecting + specific metrics instead of generating all metrics stats + that are enabled by default. This helps reduce CPU and memory + overhead in Envoy, but eliminating some stats may after + critical functionality. Here are the stats that we strongly + recommend not disabling: `cluster_manager.warming_clusters`, + `cluster..membership_total`,`cluster..membership_healthy`, + `cluster..membership_degraded`,reference https://github.com/envoyproxy/envoy/issues/9856, + https://github.com/envoyproxy/envoy/issues/14610' + items: + description: StringMatch defines how to match any strings. + This is a general purpose match condition that can be + used by other EG APIs that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that the + match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + prometheus: + description: Prometheus defines the configuration for Admin + endpoint `/stats/prometheus`. + properties: + disable: + description: Disable the Prometheus endpoint. + type: boolean + type: object + sinks: + description: Sinks defines the metric sinks where metrics + are sent to. + items: + description: ProxyMetricSink defines the sink of metrics. + Default metrics sink is OpenTelemetry. + properties: + openTelemetry: + description: OpenTelemetry defines the configuration + for OpenTelemetry sink. It's required if the sink + type is OpenTelemetry. + properties: + host: + description: Host define the service hostname. + type: string + port: + default: 4317 + description: Port defines the port the service is + exposed on. + format: int32 + maximum: 65535 + minimum: 0 + type: integer + required: + - host + type: object + type: + default: OpenTelemetry + description: Type defines the metric sink type. EG currently + only supports OpenTelemetry. + enum: + - OpenTelemetry + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: If MetricSink type is OpenTelemetry, openTelemetry + field needs to be set. + rule: 'self.type == ''OpenTelemetry'' ? has(self.openTelemetry) + : !has(self.openTelemetry)' + type: array + type: object + tracing: + description: Tracing defines tracing configuration for managed + proxies. If unspecified, will not send tracing data. + properties: + customTags: + additionalProperties: + properties: + environment: + description: Environment adds value from environment + variable to each span. It's required when the type + is "Environment". + properties: + defaultValue: + description: DefaultValue defines the default value + to use if the environment variable is not set. + type: string + name: + description: Name defines the name of the environment + variable which to extract the value from. + type: string + required: + - name + type: object + literal: + description: Literal adds hard-coded value to each span. + It's required when the type is "Literal". + properties: + value: + description: Value defines the hard-coded value + to add to each span. + type: string + required: + - value + type: object + requestHeader: + description: RequestHeader adds value from request header + to each span. It's required when the type is "RequestHeader". + properties: + defaultValue: + description: DefaultValue defines the default value + to use if the request header is not set. + type: string + name: + description: Name defines the name of the request + header which to extract the value from. + type: string + required: + - name + type: object + type: + default: Literal + description: Type defines the type of custom tag. + enum: + - Literal + - Environment + - RequestHeader + type: string + required: + - type + type: object + description: CustomTags defines the custom tags to add to + each span. If provider is kubernetes, pod name and namespace + are added by default. + type: object + provider: + description: Provider defines the tracing provider. Only OpenTelemetry + is supported currently. + properties: + host: + description: Host define the provider service hostname. + type: string + port: + default: 4317 + description: Port defines the port the provider service + is exposed on. + format: int32 + minimum: 0 + type: integer + type: + default: OpenTelemetry + description: Type defines the tracing provider type. EG + currently only supports OpenTelemetry. + enum: + - OpenTelemetry + type: string + required: + - host + - type + type: object + samplingRate: + default: 100 + description: SamplingRate controls the rate at which traffic + will be selected for tracing if no prior sampling decision + has been made. Defaults to 100, valid values [0-100]. 100 + indicates 100% sampling. + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - provider + type: object + type: object + type: object + status: + description: EnvoyProxyStatus defines the actual state of EnvoyProxy. + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml deleted file mode 100644 index 7bfc7554cb3..00000000000 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml +++ /dev/null @@ -1,184 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null - name: ratelimitfilters.gateway.envoyproxy.io -spec: - group: gateway.envoyproxy.io - names: - kind: RateLimitFilter - listKind: RateLimitFilterList - plural: ratelimitfilters - singular: ratelimitfilter - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: RateLimitFilter allows the user to limit the number of incoming - requests to a predefined value based on attributes within the traffic flow. - 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 RateLimitFilter. - properties: - global: - description: Global defines global rate limit configuration. - properties: - rules: - description: Rules are a list of RateLimit selectors and limits. - Each rule and its associated limit is applied in a mutually - exclusive way i.e. if multiple rules get selected, each of their - associated limits get applied, so a single traffic request might - increase the rate limit counters for multiple rules if selected. - items: - description: RateLimitRule defines the semantics for matching - attributes from the incoming requests, and setting limits - for them. - properties: - clientSelectors: - description: ClientSelectors holds the list of select conditions - to select specific clients using attributes from the traffic - flow. All individual select conditions must hold True - for this rule and its limit to be applied. If this field - is empty, it is equivalent to True, and the limit is applied. - items: - description: RateLimitSelectCondition specifies the attributes - within the traffic flow that can be used to select a - subset of clients to be ratelimited. All the individual - conditions must hold True for the overall condition - to hold True. - properties: - headers: - description: Headers is a list of request headers - to match. Multiple header values are ANDed together, - meaning, a request MUST match all the specified - headers. - items: - description: HeaderMatch defines the match attributes - within the HTTP Headers of the request. - properties: - name: - description: Name of the HTTP header. - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: Type specifies how to match against - the value of the header. - enum: - - Exact - - RegularExpression - - Distinct - type: string - value: - description: Value within the HTTP header. Due - to the case-insensitivity of header names, - "foo" and "Foo" are considered equivalent. - Do not set this field when Type="Distinct", - implying matching on any/all unique values - within the header. - maxLength: 1024 - type: string - required: - - name - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - sourceCIDR: - description: SourceCIDR is the client IP Address range - to match on. - properties: - type: - default: Exact - type: string - value: - description: Value is the IP CIDR that represents - the range of Source IP Addresses of the client. - These could also be the intermediate addresses - through which the request has flown through - and is part of the `X-Forwarded-For` header. - For example, `192.168.0.1/32`, `192.168.0.0/24`, - `001:db8::/64`. - maxLength: 256 - minLength: 1 - type: string - required: - - value - type: object - sourceIP: - description: 'Deprecated: Use SourceCIDR instead.' - type: string - type: object - maxItems: 8 - type: array - limit: - description: Limit holds the rate limit values. This limit - is applied for traffic flows when the selectors compute - to True, causing the request to be counted towards the - limit. The limit is enforced and the request is ratelimited, - i.e. a response with 429 HTTP status code is sent back - to the client when the selected requests have reached - the limit. - properties: - requests: - type: integer - unit: - description: RateLimitUnit specifies the intervals for - setting rate limits. Valid RateLimitUnit values are - "Second", "Minute", "Hour", and "Day". - enum: - - Second - - Minute - - Hour - - Day - type: string - required: - - requests - - unit - type: object - required: - - limit - type: object - maxItems: 16 - type: array - required: - - rules - type: object - type: - description: Type decides the scope for the RateLimits. Valid RateLimitType - values are "Global". - enum: - - Global - type: string - required: - - type - type: object - required: - - spec - type: object - served: true - storage: true - subresources: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml new file mode 100644 index 00000000000..7c75d345b08 --- /dev/null +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -0,0 +1,492 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: securitypolicies.gateway.envoyproxy.io +spec: + group: gateway.envoyproxy.io + names: + kind: SecurityPolicy + listKind: SecurityPolicyList + plural: securitypolicies + shortNames: + - sp + singular: securitypolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecurityPolicy allows the user to configure various security + settings for a Gateway. + 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 SecurityPolicy. + properties: + basicAuth: + description: BasicAuth defines the configuration for the HTTP Basic + Authentication. + properties: + users: + description: "The Kubernetes secret which contains the username-password + pairs in htpasswd format, used to verify user credentials in + the \"Authorization\" header. \n This is an Opaque secret. The + username-password pairs should be stored in the key \".htpasswd\". + As the key name indicates, the value needs to be the htpasswd + format, for example: \"user1:{SHA}hashed_user1_password\". Right + now, only SHA hash algorithm is supported. Reference to https://httpd.apache.org/docs/2.4/programs/htpasswd.html + for more details. \n Note: The secret must be in the same namespace + as the SecurityPolicy." + 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. + \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 + required: + - name + type: object + required: + - users + type: object + cors: + description: CORS defines the configuration for Cross-Origin Resource + Sharing (CORS). + properties: + allowCredentials: + description: AllowCredentials indicates whether a request can + include user credentials like cookies, authentication headers, + or TLS client certificates. + type: boolean + allowHeaders: + description: AllowHeaders defines the headers that are allowed + to be sent with requests. + items: + type: string + type: array + allowMethods: + description: AllowMethods defines the methods that are allowed + to make requests. + items: + type: string + minItems: 1 + type: array + allowOrigins: + description: AllowOrigins defines the origins that are allowed + to make requests. + items: + description: StringMatch defines how to match any strings. This + is a general purpose match condition that can be used by other + EG APIs that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that the match + must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + minItems: 1 + type: array + exposeHeaders: + description: ExposeHeaders defines the headers that can be exposed + in the responses. + items: + type: string + type: array + maxAge: + description: MaxAge defines how long the results of a preflight + request can be cached. + type: string + type: object + jwt: + description: JWT defines the configuration for JSON Web Token (JWT) + authentication. + properties: + providers: + description: Providers defines the JSON Web Token (JWT) authentication + provider type. When multiple JWT providers are specified, the + JWT is considered valid if any of the providers successfully + validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. + items: + description: JWTProvider defines how a JSON Web Token (JWT) + can be verified. + properties: + audiences: + description: Audiences is a list of JWT audiences allowed + access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. + If not provided, JWT audiences are not checked. + items: + type: string + maxItems: 8 + type: array + claimToHeaders: + description: 'ClaimToHeaders is a list of JWT claims that + must be extracted into HTTP request headers For examples, + following config: The claim must be of type; string, int, + double, bool. Array type claims are not supported' + items: + description: ClaimToHeader defines a configuration to + convert JWT claims into HTTP headers + properties: + claim: + description: 'Claim is the JWT Claim that should be + saved into the header : it can be a nested claim + of type (eg. "claim.nested.key", "sub"). The nested + claim name must use dot "." to separate the JSON + name path.' + type: string + header: + description: Header defines the name of the HTTP request + header that the JWT Claim will be saved into. + type: string + required: + - claim + - header + type: object + type: array + extractFrom: + description: ExtractFrom defines different ways to extract + the JWT token from HTTP request. If empty, it defaults + to extract JWT token from the Authorization HTTP request + header using Bearer schema or access_token from query + parameters. + properties: + cookies: + description: Cookies represents a list of cookie names + to extract the JWT token from. If specified, Envoy + will extract the JWT token from the listed cookies + and validate each of them. If any cookie is found + to be an invalid JWT, a 401 error will be returned. + items: + type: string + type: array + type: object + issuer: + description: Issuer is the principal that issued the JWT + and takes the form of a URL or email address. For additional + details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 + for URL format and https://rfc-editor.org/rfc/rfc5322.html + for email format. If not provided, the JWT issuer is not + checked. + maxLength: 253 + type: string + name: + description: Name defines a unique name for the JWT provider. + A name can have a variety of forms, including RFC1123 + subdomains, RFC 1123 labels, or RFC 1035 labels. + maxLength: 253 + minLength: 1 + type: string + remoteJWKS: + description: RemoteJWKS defines how to fetch and cache JSON + Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. + properties: + uri: + description: URI is the HTTPS URI to fetch the JWKS. + Envoy's system trust bundle is used to validate the + server certificate. + maxLength: 253 + minLength: 1 + type: string + required: + - uri + type: object + required: + - name + - remoteJWKS + type: object + maxItems: 4 + minItems: 1 + type: array + required: + - providers + type: object + oidc: + description: OIDC defines the configuration for the OpenID Connect + (OIDC) authentication. + properties: + clientID: + description: The client ID to be used in the OIDC [Authentication + Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + minLength: 1 + type: string + clientSecret: + description: "The Kubernetes secret which contains the OIDC client + secret to be used in the [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + \n This is an Opaque secret. The client secret should be stored + in the key \"client-secret\"." + 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. + \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 + required: + - name + type: object + provider: + description: The OIDC Provider configuration. + properties: + authorizationEndpoint: + description: The OIDC Provider's [authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint). + If not provided, EG will try to discover it from the provider's + [Well-Known Configuration Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). + type: string + issuer: + description: The OIDC Provider's [issuer identifier](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery). + Issuer MUST be a URI RFC 3986 [RFC3986] with a scheme component + that MUST be https, a host component, and optionally, port + and path components and no query or fragment components. + minLength: 1 + type: string + tokenEndpoint: + description: The OIDC Provider's [token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint). + If not provided, EG will try to discover it from the provider's + [Well-Known Configuration Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). + type: string + required: + - issuer + type: object + scopes: + description: The OIDC scopes to be used in the [Authentication + Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + The "openid" scope is always added to the list of scopes if + not already specified. + items: + type: string + type: array + required: + - clientID + - clientSecret + - provider + type: object + targetRef: + description: TargetRef is the name of the Gateway resource this policy + is being attached to. This Policy and the TargetRef MUST be in the + same namespace for this Policy to have effect and be applied to + the Gateway. TargetRef + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When + unspecified, the local namespace is inferred. Even when policy + targets a resource in a different namespace, it MUST only apply + to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + description: "SectionName is the name of a section within the + target resource. When unspecified, this targetRef targets the + entire resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name * + Service: Port Name \n If a SectionName is specified, but does + not exist on the targeted object, the Policy must fail to attach, + and the policy implementation should record a `ResolvedRefs` + or similar Condition in the Policy's status." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - group + - kind + - name + type: object + x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' + - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute + rule: self.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute'] + - message: this policy does not yet support the sectionName field + rule: '!has(self.sectionName)' + required: + - targetRef + type: object + status: + description: Status defines the current status of SecurityPolicy. + properties: + conditions: + description: Conditions describe the current conditions of the SecurityPolicy. + 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 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/gateway-helm/templates/NOTES.txt b/charts/gateway-helm/templates/NOTES.txt index 6a4e80c7730..e002d40d699 100644 --- a/charts/gateway-helm/templates/NOTES.txt +++ b/charts/gateway-helm/templates/NOTES.txt @@ -15,6 +15,6 @@ To learn more about the release, try: $ helm status {{ .Release.Name }} -n {{ .Release.Namespace }} $ helm get all {{ .Release.Name }} -n {{ .Release.Namespace }} -To have a quickstart of Envoy Gateway, please refer to https://gateway.envoyproxy.io/latest/user/quickstart.html. +To have a quickstart of Envoy Gateway, please refer to https://gateway.envoyproxy.io/latest/user/quickstart. To get more details, please visit https://gateway.envoyproxy.io and https://github.com/envoyproxy/gateway. diff --git a/charts/gateway-helm/templates/_rbac.tpl b/charts/gateway-helm/templates/_rbac.tpl new file mode 100644 index 00000000000..c009b66a33b --- /dev/null +++ b/charts/gateway-helm/templates/_rbac.tpl @@ -0,0 +1,173 @@ +{{/* +All namespaced resources for Envoy Gateway RBAC. +*/}} +{{- define "eg.rbac.namespaced" -}} +- {{ include "eg.rbac.namespaced.basic" . | nindent 2 | trim }} +- {{ include "eg.rbac.namespaced.apps" . | nindent 2 | trim }} +- {{ include "eg.rbac.namespaced.discovery" . | nindent 2 | trim }} +- {{ include "eg.rbac.namespaced.gateway.envoyproxy" . | nindent 2 | trim }} +- {{ include "eg.rbac.namespaced.gateway.envoyproxy.status" . | nindent 2 | trim }} +- {{ include "eg.rbac.namespaced.gateway.networking" . | nindent 2 | trim }} +- {{ include "eg.rbac.namespaced.gateway.networking.status" . | nindent 2 | trim }} +{{- end }} + +{{/* +All cluster scoped resources for Envoy Gateway RBAC. +*/}} +{{- define "eg.rbac.cluster" -}} +- {{ include "eg.rbac.cluster.basic" . | nindent 2 | trim }} +- {{ include "eg.rbac.cluster.gateway.networking" . | nindent 2 | trim }} +- {{ include "eg.rbac.cluster.gateway.networking.status" . | nindent 2 | trim }} +- {{ include "eg.rbac.cluster.multiclusterservices" . | nindent 2 | trim }} +{{- end }} + +{{/* +Namespaced +*/}} + +{{- define "eg.rbac.namespaced.basic" -}} +apiGroups: +- "" +resources: +- secrets +- services +verbs: +- get +- list +- watch +{{- end }} + +{{- define "eg.rbac.namespaced.apps" -}} +apiGroups: +- apps +resources: +- deployments +verbs: +- get +- list +- watch +{{- end }} + +{{- define "eg.rbac.namespaced.discovery" -}} +apiGroups: +- discovery.k8s.io +resources: +- endpointslices +verbs: +- get +- list +- watch +{{- end }} + +{{- define "eg.rbac.namespaced.gateway.envoyproxy" -}} +apiGroups: +- gateway.envoyproxy.io +resources: +- envoyproxies +- envoypatchpolicies +- clienttrafficpolicies +- backendtrafficpolicies +- securitypolicies +verbs: +- get +- list +- patch +- update +- watch +{{- end }} + +{{- define "eg.rbac.namespaced.gateway.envoyproxy.status" -}} +apiGroups: +- gateway.envoyproxy.io +resources: +- envoypatchpolicies/status +- clienttrafficpolicies/status +- backendtrafficpolicies/status +- securitypolicies/status +verbs: +- update +{{- end }} + +{{- define "eg.rbac.namespaced.gateway.networking" -}} +apiGroups: +- gateway.networking.k8s.io +resources: +- gateways +- grpcroutes +- httproutes +- referencegrants +- referencepolicies +- tcproutes +- tlsroutes +- udproutes +verbs: +- get +- list +- patch +- update +- watch +{{- end }} + +{{- define "eg.rbac.namespaced.gateway.networking.status" -}} +apiGroups: +- gateway.networking.k8s.io +resources: +- gateways/status +- grpcroutes/status +- httproutes/status +- tcproutes/status +- tlsroutes/status +- udproutes/status +verbs: +- update +{{- end }} + +{{/* +Cluster scope +*/}} + +{{- define "eg.rbac.cluster.basic" -}} +apiGroups: +- "" +resources: +- nodes +- namespaces +verbs: +- get +- list +- watch +{{- end }} + +{{- define "eg.rbac.cluster.gateway.networking" -}} +apiGroups: +- gateway.networking.k8s.io +resources: +- gatewayclasses +verbs: +- get +- list +- patch +- update +- watch +{{- end }} + + +{{- define "eg.rbac.cluster.multiclusterservices" -}} +apiGroups: +- multicluster.x-k8s.io +resources: +- serviceimports +verbs: +- get +- list +- watch +{{- end }} + +{{- define "eg.rbac.cluster.gateway.networking.status" -}} +apiGroups: +- gateway.networking.k8s.io +resources: +- gatewayclasses/status +verbs: +- update +{{- end }} diff --git a/charts/gateway-helm/templates/certgen-rbac.yaml b/charts/gateway-helm/templates/certgen-rbac.yaml index ff805dad3db..81bf1a820b8 100644 --- a/charts/gateway-helm/templates/certgen-rbac.yaml +++ b/charts/gateway-helm/templates/certgen-rbac.yaml @@ -5,8 +5,14 @@ metadata: namespace: '{{ .Release.Namespace }}' labels: {{- include "eg.labels" . | nindent 4 }} + {{- if .Values.certgen.rbac.labels }} + {{- toYaml .Values.certgen.rbac.labels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-install + {{- if .Values.certgen.rbac.annotations }} + {{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}} + {{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -15,8 +21,14 @@ metadata: namespace: '{{ .Release.Namespace }}' labels: {{- include "eg.labels" . | nindent 4 }} + {{- if .Values.certgen.rbac.labels }} + {{- toYaml .Values.certgen.rbac.labels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-install + {{- if .Values.certgen.rbac.annotations }} + {{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}} + {{- end }} rules: - apiGroups: - "" @@ -34,8 +46,14 @@ metadata: namespace: '{{ .Release.Namespace }}' labels: {{- include "eg.labels" . | nindent 4 }} + {{- if .Values.certgen.rbac.labels }} + {{- toYaml .Values.certgen.rbac.labels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-install + {{- if .Values.certgen.rbac.annotations }} + {{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}} + {{- end }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role diff --git a/charts/gateway-helm/templates/certgen.yaml b/charts/gateway-helm/templates/certgen.yaml index c1e9b16fd6d..4d49597fec0 100644 --- a/charts/gateway-helm/templates/certgen.yaml +++ b/charts/gateway-helm/templates/certgen.yaml @@ -7,6 +7,9 @@ metadata: {{- include "eg.labels" . | nindent 4 }} annotations: "helm.sh/hook": pre-install + {{- if .Values.certgen.job.annotations }} + {{- toYaml .Values.certgen.job.annotations | nindent 4 -}} + {{- end }} spec: backoffLimit: 1 completions: 1 @@ -37,4 +40,6 @@ spec: runAsNonRoot: true runAsUser: 65534 serviceAccountName: {{ include "eg.fullname" . }}-certgen - ttlSecondsAfterFinished: 0 + {{- if not ( kindIs "invalid" .Values.certgen.job.ttlSecondsAfterFinished) }} + ttlSecondsAfterFinished: {{ .Values.certgen.job.ttlSecondsAfterFinished }} + {{- end }} diff --git a/charts/gateway-helm/templates/envoy-gateway-config.yaml b/charts/gateway-helm/templates/envoy-gateway-config.yaml index 518354f5900..b57b6729c2e 100644 --- a/charts/gateway-helm/templates/envoy-gateway-config.yaml +++ b/charts/gateway-helm/templates/envoy-gateway-config.yaml @@ -7,6 +7,6 @@ metadata: {{- include "eg.labels" . | nindent 4 }} data: envoy-gateway.yaml: | - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway {{- toYaml .Values.config.envoyGateway | nindent 4 }} diff --git a/charts/gateway-helm/templates/envoy-gateway-deployment.yaml b/charts/gateway-helm/templates/envoy-gateway-deployment.yaml index 6f36ef0a7c6..52e9c419fd9 100644 --- a/charts/gateway-helm/templates/envoy-gateway-deployment.yaml +++ b/charts/gateway-helm/templates/envoy-gateway-deployment.yaml @@ -71,22 +71,6 @@ spec: - mountPath: /certs name: certs readOnly: true - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: {{ .Values.kubernetesClusterDomain }} - image: {{ .Values.deployment.kubeRbacProxy.image.repository }}:{{ .Values.deployment.kubeRbacProxy.image.tag | default .Chart.AppVersion }} - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: {{- toYaml .Values.deployment.kubeRbacProxy.resources | nindent 10 - }} securityContext: runAsNonRoot: true serviceAccountName: envoy-gateway diff --git a/charts/gateway-helm/templates/envoy-gateway-rbac.yaml b/charts/gateway-helm/templates/envoy-gateway-rbac.yaml index 3fe9289c2cb..5d975b8aaac 100644 --- a/charts/gateway-helm/templates/envoy-gateway-rbac.yaml +++ b/charts/gateway-helm/templates/envoy-gateway-rbac.yaml @@ -1,3 +1,72 @@ +{{ $watchedNamespaces := list }} +{{ if .Values.config.envoyGateway.provider.kubernetes }} +{{ $kube := .Values.config.envoyGateway.provider.kubernetes }} +{{ if $kube.watch }} +{{ if $kube.watch.namespaces }} +{{ if gt (len $kube.watch.namespaces) 0 }} +{{ $watchedNamespaces = $kube.watch.namespaces }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ if gt (len $watchedNamespaces) 0 }} +{{ range $_, $ns := $watchedNamespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + creationTimestamp: null + name: {{ include "eg.fullname" $ }}-envoy-gateway-role + namespace: {{ $ns | quote }} +rules: +{{ include "eg.rbac.namespaced" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "eg.fullname" $ }}-envoy-gateway-rolebinding + namespace: {{ $ns | quote }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "eg.fullname" $ }}-envoy-gateway-role +subjects: +- kind: ServiceAccount + name: 'envoy-gateway' + namespace: '{{ $.Release.Namespace }}' +{{ end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: {{ include "eg.fullname" . }}-envoy-gateway-role +rules: +{{ include "eg.rbac.cluster" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "eg.fullname" . }}-envoy-gateway-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "eg.fullname" . }}-envoy-gateway-role +subjects: +- kind: ServiceAccount + name: 'envoy-gateway' + namespace: '{{ .Release.Namespace }}' +{{ else }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: {{ include "eg.fullname" . }}-envoy-gateway-role +rules: +{{ include "eg.rbac.cluster" . }} +{{ include "eg.rbac.namespaced" . }} +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -10,3 +79,4 @@ subjects: - kind: ServiceAccount name: 'envoy-gateway' namespace: '{{ .Release.Namespace }}' +{{ end }} diff --git a/charts/gateway-helm/templates/generated/rbac/roles.yaml b/charts/gateway-helm/templates/generated/rbac/roles.yaml deleted file mode 100644 index 602b9f4322c..00000000000 --- a/charts/gateway-helm/templates/generated/rbac/roles.yaml +++ /dev/null @@ -1,91 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: {{ include "eg.fullname" . }}-envoy-gateway-role -rules: -- apiGroups: - - "" - resources: - - namespaces - - nodes - - secrets - - services - verbs: - - get - - list - - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - get - - list - - watch -- apiGroups: - - config.gateway.envoyproxy.io - resources: - - envoyproxies - verbs: - - get - - list - - update - - watch -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - get - - list - - watch -- apiGroups: - - gateway.envoyproxy.io - resources: - - authenticationfilters - - envoypatchpolicies - - ratelimitfilters - verbs: - - get - - list - - patch - - update - - watch -- apiGroups: - - gateway.envoyproxy.io - resources: - - envoypatchpolicies/status - verbs: - - update -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses - - gateways - - grpcroutes - - httproutes - - referencegrants - - referencepolicies - - tcproutes - - tlsroutes - - udproutes - verbs: - - get - - list - - patch - - update - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses/status - - gateways/status - - grpcroutes/status - - httproutes/status - - tcproutes/status - - tlsroutes/status - - udproutes/status - verbs: - - update diff --git a/charts/gateway-helm/templates/infra-manager-rbac.yaml b/charts/gateway-helm/templates/infra-manager-rbac.yaml index 6f3e5a4677f..3929524f484 100644 --- a/charts/gateway-helm/templates/infra-manager-rbac.yaml +++ b/charts/gateway-helm/templates/infra-manager-rbac.yaml @@ -25,6 +25,15 @@ rules: - get - update - delete +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - create + - get + - update + - delete --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/gateway-helm/templates/metrics-reader-rbac.yaml b/charts/gateway-helm/templates/metrics-reader-rbac.yaml deleted file mode 100644 index 3b77e714185..00000000000 --- a/charts/gateway-helm/templates/metrics-reader-rbac.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "eg.fullname" . }}-metrics-reader - namespace: '{{ .Release.Namespace }}' - labels: - {{- include "eg.labels" . | nindent 4 }} -rules: -- nonResourceURLs: - - /metrics - verbs: - - get diff --git a/charts/gateway-helm/values.tmpl.yaml b/charts/gateway-helm/values.tmpl.yaml index 430cca41f06..a65b0233bc4 100644 --- a/charts/gateway-helm/values.tmpl.yaml +++ b/charts/gateway-helm/values.tmpl.yaml @@ -11,17 +11,6 @@ deployment: requests: cpu: 100m memory: 256Mi - kubeRbacProxy: - image: - repository: gcr.io/kubebuilder/kube-rbac-proxy - tag: v0.11.0 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi ports: - name: grpc port: 18000 @@ -46,11 +35,19 @@ config: envoyGatewayMetricsService: ports: - - name: https - port: 8443 + - name: http + port: 19001 protocol: TCP - targetPort: https + targetPort: 19001 createNamespace: false kubernetesClusterDomain: cluster.local + +certgen: + job: + annotations: {} + ttlSecondsAfterFinished: 0 + rbac: + annotations: {} + labels: {} diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 32039ae50a5..00000000000 --- a/docs/index.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/latest/about_docs.rst b/docs/latest/about_docs.rst deleted file mode 100644 index ecaa28247b9..00000000000 --- a/docs/latest/about_docs.rst +++ /dev/null @@ -1,9 +0,0 @@ -About the Documentation -======================= - -Learn how to contribute to Envoy Gateway documentation. - -.. toctree:: - :maxdepth: 1 - - dev/DOCS diff --git a/docs/latest/api/config_types.md b/docs/latest/api/config_types.md deleted file mode 100644 index 73d2ace5762..00000000000 --- a/docs/latest/api/config_types.md +++ /dev/null @@ -1,978 +0,0 @@ -# API Reference - -## Packages -- [config.gateway.envoyproxy.io/v1alpha1](#configgatewayenvoyproxyiov1alpha1) - - -## config.gateway.envoyproxy.io/v1alpha1 - -Package v1alpha1 contains API schema definitions for the config.gateway.envoyproxy.io -API group. - - -### Resource Types -- [EnvoyGateway](#envoygateway) -- [EnvoyProxy](#envoyproxy) - - - -## CustomTag - - - - - -_Appears in:_ -- [ProxyTracing](#proxytracing) - -| Field | Description | -| --- | --- | -| `type` _[CustomTagType](#customtagtype)_ | Type defines the type of custom tag. | -| `literal` _[LiteralCustomTag](#literalcustomtag)_ | Literal adds hard-coded value to each span. It's required when the type is "Literal". | -| `environment` _[EnvironmentCustomTag](#environmentcustomtag)_ | Environment adds value from environment variable to each span. It's required when the type is "Environment". | -| `requestHeader` _[RequestHeaderCustomTag](#requestheadercustomtag)_ | RequestHeader adds value from request header to each span. It's required when the type is "RequestHeader". | - - -## CustomTagType - -_Underlying type:_ `string` - - - -_Appears in:_ -- [CustomTag](#customtag) - - - -## EnvironmentCustomTag - - - -EnvironmentCustomTag adds value from environment variable to each span. - -_Appears in:_ -- [CustomTag](#customtag) - -| Field | Description | -| --- | --- | -| `name` _string_ | Name defines the name of the environment variable which to extract the value from. | -| `defaultValue` _string_ | DefaultValue defines the default value to use if the environment variable is not set. | - - -## EnvoyGateway - - - -EnvoyGateway is the schema for the envoygateways API. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `EnvoyGateway` -| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | -| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | -| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | -| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | -| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | -| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | -| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | - - -## EnvoyGatewayAdmin - - - -EnvoyGatewayAdmin defines the Envoy Gateway Admin configuration. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `address` _[EnvoyGatewayAdminAddress](#envoygatewayadminaddress)_ | Address defines the address of Envoy Gateway Admin Server. | -| `debug` _boolean_ | Debug defines if enable the /debug endpoint of Envoy Gateway. | - - -## EnvoyGatewayAdminAddress - - - -EnvoyGatewayAdminAddress defines the Envoy Gateway Admin Address configuration. - -_Appears in:_ -- [EnvoyGatewayAdmin](#envoygatewayadmin) - -| Field | Description | -| --- | --- | -| `port` _integer_ | Port defines the port the admin server is exposed on. | -| `host` _string_ | Host defines the admin server hostname. | - - -## EnvoyGatewayCustomProvider - - - -EnvoyGatewayCustomProvider defines configuration for the Custom provider. - -_Appears in:_ -- [EnvoyGatewayProvider](#envoygatewayprovider) - -| Field | Description | -| --- | --- | -| `resource` _[EnvoyGatewayResourceProvider](#envoygatewayresourceprovider)_ | Resource defines the desired resource provider. This provider is used to specify the provider to be used to retrieve the resource configurations such as Gateway API resources | -| `infrastructure` _[EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider)_ | Infrastructure defines the desired infrastructure provider. This provider is used to specify the provider to be used to provide an environment to deploy the out resources like the Envoy Proxy data plane. | - - -## EnvoyGatewayFileResourceProvider - - - -EnvoyGatewayFileResourceProvider defines configuration for the File Resource provider. - -_Appears in:_ -- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) - -| Field | Description | -| --- | --- | -| `paths` _string array_ | Paths are the paths to a directory or file containing the resource configuration. Recursive sub directories are not currently supported. | - - -## EnvoyGatewayHostInfrastructureProvider - - - -EnvoyGatewayHostInfrastructureProvider defines configuration for the Host Infrastructure provider. - -_Appears in:_ -- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) - - - -## EnvoyGatewayInfrastructureProvider - - - -EnvoyGatewayInfrastructureProvider defines configuration for the Custom Infrastructure provider. - -_Appears in:_ -- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) - -| Field | Description | -| --- | --- | -| `type` _[InfrastructureProviderType](#infrastructureprovidertype)_ | Type is the type of infrastructure providers to use. Supported types are "Host". | -| `host` _[EnvoyGatewayHostInfrastructureProvider](#envoygatewayhostinfrastructureprovider)_ | Host defines the configuration of the Host provider. Host provides runtime deployment of the data plane as a child process on the host environment. | - - -## EnvoyGatewayKubernetesProvider - - - -EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. - -_Appears in:_ -- [EnvoyGatewayProvider](#envoygatewayprovider) - -| Field | Description | -| --- | --- | -| `rateLimitDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. If unspecified, default settings for the manged Envoy ratelimit deployment resource are applied. | -| `watch` _[KubernetesWatchMode](#kuberneteswatchmode)_ | Watch holds configuration of which input resources should be watched and reconciled. | -| `deploy` _[KubernetesDeployMode](#kubernetesdeploymode)_ | Deploy holds configuration of how output managed resources such as the Envoy Proxy data plane should be deployed | -| `overwrite_control_plane_certs` _boolean_ | OverwriteControlPlaneCerts updates the secrets containing the control plane certs, when set. | - - -## EnvoyGatewayLogComponent - -_Underlying type:_ `string` - -EnvoyGatewayLogComponent defines a component that supports a configured logging level. - -_Appears in:_ -- [EnvoyGatewayLogging](#envoygatewaylogging) - - - -## EnvoyGatewayLogging - - - -EnvoyGatewayLogging defines logging for Envoy Gateway. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `level` _object (keys:[EnvoyGatewayLogComponent](#envoygatewaylogcomponent), values:[LogLevel](#loglevel))_ | Level is the logging level. If unspecified, defaults to "info". EnvoyGatewayLogComponent options: default/provider/gateway-api/xds-translator/xds-server/infrastructure/global-ratelimit. LogLevel options: debug/info/error/warn. | - - -## EnvoyGatewayProvider - - - -EnvoyGatewayProvider defines the desired configuration of a provider. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `type` _[ProviderType](#providertype)_ | Type is the type of provider to use. Supported types are "Kubernetes". | -| `kubernetes` _[EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider)_ | Kubernetes defines the configuration of the Kubernetes provider. Kubernetes provides runtime configuration via the Kubernetes API. | -| `custom` _[EnvoyGatewayCustomProvider](#envoygatewaycustomprovider)_ | Custom defines the configuration for the Custom provider. This provider allows you to define a specific resource provider and a infrastructure provider. | - - -## EnvoyGatewayResourceProvider - - - -EnvoyGatewayResourceProvider defines configuration for the Custom Resource provider. - -_Appears in:_ -- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) - -| Field | Description | -| --- | --- | -| `type` _[ResourceProviderType](#resourceprovidertype)_ | Type is the type of resource provider to use. Supported types are "File". | -| `file` _[EnvoyGatewayFileResourceProvider](#envoygatewayfileresourceprovider)_ | File defines the configuration of the File provider. File provides runtime configuration defined by one or more files. | - - -## EnvoyGatewaySpec - - - -EnvoyGatewaySpec defines the desired state of Envoy Gateway. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) - -| Field | Description | -| --- | --- | -| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | -| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | -| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | -| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | -| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | -| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | -| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | - - -## EnvoyProxy - - - -EnvoyProxy is the schema for the envoyproxies API. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `EnvoyProxy` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[EnvoyProxySpec](#envoyproxyspec)_ | EnvoyProxySpec defines the desired state of EnvoyProxy. | - - -## EnvoyProxyKubernetesProvider - - - -EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource provider. - -_Appears in:_ -- [EnvoyProxyProvider](#envoyproxyprovider) - -| Field | Description | -| --- | --- | -| `envoyDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | EnvoyDeployment defines the desired state of the Envoy deployment resource. If unspecified, default settings for the manged Envoy deployment resource are applied. | -| `envoyService` _[KubernetesServiceSpec](#kubernetesservicespec)_ | EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings for the manged Envoy service resource are applied. | - - -## EnvoyProxyProvider - - - -EnvoyProxyProvider defines the desired state of a resource provider. - -_Appears in:_ -- [EnvoyProxySpec](#envoyproxyspec) - -| Field | Description | -| --- | --- | -| `type` _[ProviderType](#providertype)_ | Type is the type of resource provider to use. A resource provider provides infrastructure resources for running the data plane, e.g. Envoy proxy, and optional auxiliary control planes. Supported types are "Kubernetes". | -| `kubernetes` _[EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider)_ | Kubernetes defines the desired state of the Kubernetes resource provider. Kubernetes provides infrastructure resources for running the data plane, e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings for managed Kubernetes resources are applied. | - - -## EnvoyProxySpec - - - -EnvoyProxySpec defines the desired state of EnvoyProxy. - -_Appears in:_ -- [EnvoyProxy](#envoyproxy) - -| Field | Description | -| --- | --- | -| `provider` _[EnvoyProxyProvider](#envoyproxyprovider)_ | Provider defines the desired resource provider and provider-specific configuration. If unspecified, the "Kubernetes" resource provider is used with default configuration parameters. | -| `logging` _[ProxyLogging](#proxylogging)_ | Logging defines logging parameters for managed proxies. | -| `telemetry` _[ProxyTelemetry](#proxytelemetry)_ | Telemetry defines telemetry parameters for managed proxies. | -| `bootstrap` _string_ | Bootstrap defines the Envoy Bootstrap as a YAML string. Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap to learn more about the syntax. If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration set by Envoy Gateway. Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources from it are not configurable and will result in the `EnvoyProxy` resource being rejected. Backward compatibility across minor versions is not guaranteed. We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. | - - - - -## ExtensionAPISettings - - - -ExtensionAPISettings defines the settings specific to Gateway API Extensions. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `enableEnvoyPatchPolicy` _boolean_ | EnableEnvoyPatchPolicy enables Envoy Gateway to reconcile and implement the EnvoyPatchPolicy resources. | - - -## ExtensionHooks - - - -ExtensionHooks defines extension hooks across all supported runners - -_Appears in:_ -- [ExtensionManager](#extensionmanager) - -| Field | Description | -| --- | --- | -| `xdsTranslator` _[XDSTranslatorHooks](#xdstranslatorhooks)_ | XDSTranslator defines all the supported extension hooks for the xds-translator runner | - - -## ExtensionManager - - - -ExtensionManager defines the configuration for registering an extension manager to the Envoy Gateway control plane. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `resources` _[GroupVersionKind](#groupversionkind) array_ | Resources defines the set of K8s resources the extension will handle. | -| `hooks` _[ExtensionHooks](#extensionhooks)_ | Hooks defines the set of hooks the extension supports | -| `service` _[ExtensionService](#extensionservice)_ | Service defines the configuration of the extension service that the Envoy Gateway Control Plane will call through extension hooks. | - - -## ExtensionService - - - -ExtensionService defines the configuration for connecting to a registered extension service. - -_Appears in:_ -- [ExtensionManager](#extensionmanager) - -| Field | Description | -| --- | --- | -| `host` _string_ | Host define the extension service hostname. | -| `port` _integer_ | Port defines the port the extension service is exposed on. | -| `tls` _[ExtensionTLS](#extensiontls)_ | TLS defines TLS configuration for communication between Envoy Gateway and the extension service. | - - -## ExtensionTLS - - - -ExtensionTLS defines the TLS configuration when connecting to an extension service - -_Appears in:_ -- [ExtensionService](#extensionservice) - -| Field | Description | -| --- | --- | -| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef contains a references to objects (Kubernetes objects or otherwise) that contains a TLS certificate and private keys. These certificates are used to establish a TLS handshake to the extension server. - CertificateRef can only reference a Kubernetes Secret at this time. | - - -## FileEnvoyProxyAccessLog - - - - - -_Appears in:_ -- [ProxyAccessLogSink](#proxyaccesslogsink) - -| Field | Description | -| --- | --- | -| `path` _string_ | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). Empty value disables accesslog. | - - -## Gateway - - - -Gateway defines the desired Gateway API configuration of Envoy Gateway. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `controllerName` _string_ | ControllerName defines the name of the Gateway API controller. If unspecified, defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following for additional details: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass | - - -## GroupVersionKind - - - -GroupVersionKind unambiguously identifies a Kind. It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind - -_Appears in:_ -- [ExtensionManager](#extensionmanager) - -| Field | Description | -| --- | --- | -| `group` _string_ | | -| `version` _string_ | | -| `kind` _string_ | | - - -## InfrastructureProviderType - -_Underlying type:_ `string` - -InfrastructureProviderType defines the types of custom infrastructure providers supported by Envoy Gateway. - -_Appears in:_ -- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) - - - -## KubernetesContainerSpec - - - -KubernetesContainerSpec defines the desired state of the Kubernetes container resource. - -_Appears in:_ -- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) - -| Field | Description | -| --- | --- | -| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#envvar-v1-core) array_ | List of environment variables to set in the container. | -| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources required by this container. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | -| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#securitycontext-v1-core)_ | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | -| `image` _string_ | Image specifies the EnvoyProxy container image to be used, instead of the default image. | -| `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volumemount-v1-core) array_ | VolumeMounts are volumes to mount into the container's filesystem. Cannot be updated. | - - -## KubernetesDeployMode - - - -KubernetesDeployMode holds configuration for how to deploy managed resources such as the Envoy Proxy data plane fleet. - -_Appears in:_ -- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) - - - -## KubernetesDeploymentSpec - - - -KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. - -_Appears in:_ -- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) -- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) - -| Field | Description | -| --- | --- | -| `replicas` _integer_ | Replicas is the number of desired pods. Defaults to 1. | -| `strategy` _[DeploymentStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#deploymentstrategy-v1-apps)_ | The deployment strategy to use to replace existing pods with new ones. | -| `pod` _[KubernetesPodSpec](#kubernetespodspec)_ | Pod defines the desired annotations and securityContext of container. | -| `container` _[KubernetesContainerSpec](#kubernetescontainerspec)_ | Container defines the resources and securityContext of container. | - - -## KubernetesPodSpec - - - -KubernetesPodSpec defines the desired state of the Kubernetes pod resource. - -_Appears in:_ -- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) - -| Field | Description | -| --- | --- | -| `annotations` _object (keys:string, values:string)_ | Annotations are the annotations that should be appended to the pods. By default, no pod annotations are appended. | -| `labels` _object (keys:string, values:string)_ | Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. | -| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)_ | SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. | -| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#affinity-v1-core)_ | If specified, the pod's scheduling constraints. | -| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#toleration-v1-core) array_ | If specified, the pod's tolerations. | -| `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volume-v1-core) array_ | Volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes | - - -## KubernetesServiceSpec - - - -KubernetesServiceSpec defines the desired state of the Kubernetes service resource. - -_Appears in:_ -- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) - -| Field | Description | -| --- | --- | -| `annotations` _object (keys:string, values:string)_ | Annotations that should be appended to the service. By default, no annotations are appended. | -| `type` _[ServiceType](#servicetype)_ | Type determines how the Service is exposed. Defaults to LoadBalancer. Valid options are ClusterIP, LoadBalancer and NodePort. "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | - - -## KubernetesWatchMode - - - -KubernetesWatchMode holds the configuration for which input resources to watch and reconcile. - -_Appears in:_ -- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) - -| Field | Description | -| --- | --- | -| `Namespaces` _string array_ | Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped resources such as Gateway, HTTPRoute and Service. Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as GatewayClass that it is linked to. By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources from all namespaces. | - - -## LiteralCustomTag - - - -LiteralCustomTag adds hard-coded value to each span. - -_Appears in:_ -- [CustomTag](#customtag) - -| Field | Description | -| --- | --- | -| `value` _string_ | Value defines the hard-coded value to add to each span. | - - -## LogComponent - -_Underlying type:_ `string` - -LogComponent defines a component that supports a configured logging level. - -_Appears in:_ -- [ProxyLogging](#proxylogging) - - - -## LogLevel - -_Underlying type:_ `string` - -LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. This type is not implemented for EnvoyProxy until https://github.com/envoyproxy/gateway/issues/280 is fixed. - -_Appears in:_ -- [EnvoyGatewayLogging](#envoygatewaylogging) -- [ProxyLogging](#proxylogging) - - - -## MetricSink - - - - - -_Appears in:_ -- [ProxyMetrics](#proxymetrics) - -| Field | Description | -| --- | --- | -| `type` _[MetricSinkType](#metricsinktype)_ | Type defines the metric sink type. EG currently only supports OpenTelemetry. | -| `openTelemetry` _[OpenTelemetrySink](#opentelemetrysink)_ | OpenTelemetry defines the configuration for OpenTelemetry sink. It's required if the sink type is OpenTelemetry. | - - -## MetricSinkType - -_Underlying type:_ `string` - - - -_Appears in:_ -- [MetricSink](#metricsink) - - - -## OpenTelemetryEnvoyProxyAccessLog - - - -TODO: consider reuse ExtensionService? - -_Appears in:_ -- [ProxyAccessLogSink](#proxyaccesslogsink) - -| Field | Description | -| --- | --- | -| `host` _string_ | Host define the extension service hostname. | -| `port` _integer_ | Port defines the port the extension service is exposed on. | -| `resources` _object (keys:string, values:string)_ | Resources is a set of labels that describe the source of a log entry, including envoy node info. It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). | - - -## OpenTelemetrySink - - - - - -_Appears in:_ -- [MetricSink](#metricsink) - -| Field | Description | -| --- | --- | -| `host` _string_ | Host define the service hostname. | -| `port` _integer_ | Port defines the port the service is exposed on. | - - -## PrometheusProvider - - - - - -_Appears in:_ -- [ProxyMetrics](#proxymetrics) - - - -## ProviderType - -_Underlying type:_ `string` - -ProviderType defines the types of providers supported by Envoy Gateway. - -_Appears in:_ -- [EnvoyGatewayProvider](#envoygatewayprovider) -- [EnvoyProxyProvider](#envoyproxyprovider) - - - -## ProxyAccessLog - - - - - -_Appears in:_ -- [ProxyTelemetry](#proxytelemetry) - -| Field | Description | -| --- | --- | -| `disable` _boolean_ | Disable disables access logging for managed proxies if set to true. | -| `settings` _[ProxyAccessLogSetting](#proxyaccesslogsetting) array_ | Settings defines accesslog settings for managed proxies. If unspecified, will send default format to stdout. | - - -## ProxyAccessLogFormat - - - -ProxyAccessLogFormat defines the format of accesslog. - -_Appears in:_ -- [ProxyAccessLogSetting](#proxyaccesslogsetting) - -| Field | Description | -| --- | --- | -| `type` _[ProxyAccessLogFormatType](#proxyaccesslogformattype)_ | Type defines the type of accesslog format. | -| `text` _string_ | Text defines the text accesslog format, following Envoy accesslog formatting, empty value results in proxy's default access log format. It's required when the format type is "Text". Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. | -| `json` _object (keys:string, values:string)_ | JSON is additional attributes that describe the specific event occurrence. Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) can be used as values for fields within the Struct. It's required when the format type is "JSON". | - - -## ProxyAccessLogFormatType - -_Underlying type:_ `string` - - - -_Appears in:_ -- [ProxyAccessLogFormat](#proxyaccesslogformat) - - - -## ProxyAccessLogSetting - - - - - -_Appears in:_ -- [ProxyAccessLog](#proxyaccesslog) - -| Field | Description | -| --- | --- | -| `format` _[ProxyAccessLogFormat](#proxyaccesslogformat)_ | Format defines the format of accesslog. | -| `sinks` _[ProxyAccessLogSink](#proxyaccesslogsink) array_ | Sinks defines the sinks of accesslog. | - - -## ProxyAccessLogSink - - - - - -_Appears in:_ -- [ProxyAccessLogSetting](#proxyaccesslogsetting) - -| Field | Description | -| --- | --- | -| `type` _[ProxyAccessLogSinkType](#proxyaccesslogsinktype)_ | Type defines the type of accesslog sink. | -| `file` _[FileEnvoyProxyAccessLog](#fileenvoyproxyaccesslog)_ | File defines the file accesslog sink. | -| `openTelemetry` _[OpenTelemetryEnvoyProxyAccessLog](#opentelemetryenvoyproxyaccesslog)_ | OpenTelemetry defines the OpenTelemetry accesslog sink. | - - -## ProxyAccessLogSinkType - -_Underlying type:_ `string` - - - -_Appears in:_ -- [ProxyAccessLogSink](#proxyaccesslogsink) - - - -## ProxyLogging - - - -ProxyLogging defines logging parameters for managed proxies. - -_Appears in:_ -- [EnvoyProxySpec](#envoyproxyspec) - -| Field | Description | -| --- | --- | -| `level` _object (keys:[LogComponent](#logcomponent), values:[LogLevel](#loglevel))_ | Level is a map of logging level per component, where the component is the key and the log level is the value. If unspecified, defaults to "default: warn". | - - -## ProxyMetrics - - - - - -_Appears in:_ -- [ProxyTelemetry](#proxytelemetry) - -| Field | Description | -| --- | --- | -| `prometheus` _[PrometheusProvider](#prometheusprovider)_ | Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. | -| `sinks` _[MetricSink](#metricsink) array_ | Sinks defines the metric sinks where metrics are sent to. | - - -## ProxyTelemetry - - - - - -_Appears in:_ -- [EnvoyProxySpec](#envoyproxyspec) - -| Field | Description | -| --- | --- | -| `accessLog` _[ProxyAccessLog](#proxyaccesslog)_ | AccessLogs defines accesslog parameters for managed proxies. If unspecified, will send default format to stdout. | -| `tracing` _[ProxyTracing](#proxytracing)_ | Tracing defines tracing configuration for managed proxies. If unspecified, will not send tracing data. | -| `metrics` _[ProxyMetrics](#proxymetrics)_ | Metrics defines metrics configuration for managed proxies. | - - -## ProxyTracing - - - - - -_Appears in:_ -- [ProxyTelemetry](#proxytelemetry) - -| Field | Description | -| --- | --- | -| `samplingRate` _integer_ | SamplingRate controls the rate at which traffic will be selected for tracing if no prior sampling decision has been made. Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. | -| `customTags` _object (keys:string, values:[CustomTag](#customtag))_ | CustomTags defines the custom tags to add to each span. If provider is kubernetes, pod name and namespace are added by default. | -| `provider` _[TracingProvider](#tracingprovider)_ | Provider defines the tracing provider. Only OpenTelemetry is supported currently. | - - -## RateLimit - - - -RateLimit defines the configuration associated with the Rate Limit Service used for Global Rate Limiting. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `backend` _[RateLimitDatabaseBackend](#ratelimitdatabasebackend)_ | Backend holds the configuration associated with the database backend used by the rate limit service to store state associated with global ratelimiting. | - - -## RateLimitDatabaseBackend - - - -RateLimitDatabaseBackend defines the configuration associated with the database backend used by the rate limit service. - -_Appears in:_ -- [RateLimit](#ratelimit) - -| Field | Description | -| --- | --- | -| `type` _[RateLimitDatabaseBackendType](#ratelimitdatabasebackendtype)_ | Type is the type of database backend to use. Supported types are: * Redis: Connects to a Redis database. | -| `redis` _[RateLimitRedisSettings](#ratelimitredissettings)_ | Redis defines the settings needed to connect to a Redis database. | - - -## RateLimitDatabaseBackendType - -_Underlying type:_ `string` - -RateLimitDatabaseBackendType specifies the types of database backend to be used by the rate limit service. - -_Appears in:_ -- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) - - - -## RateLimitRedisSettings - - - -RateLimitRedisSettings defines the configuration for connecting to redis database. - -_Appears in:_ -- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) - -| Field | Description | -| --- | --- | -| `url` _string_ | URL of the Redis Database. | -| `tls` _[RedisTLSSettings](#redistlssettings)_ | TLS defines TLS configuration for connecting to redis database. | - - -## RedisTLSSettings - - - -RedisTLSSettings defines the TLS configuration for connecting to redis database. - -_Appears in:_ -- [RateLimitRedisSettings](#ratelimitredissettings) - -| Field | Description | -| --- | --- | -| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef defines the client certificate reference for TLS connections. Currently only a Kubernetes Secret of type TLS is supported. | - - -## RequestHeaderCustomTag - - - -RequestHeaderCustomTag adds value from request header to each span. - -_Appears in:_ -- [CustomTag](#customtag) - -| Field | Description | -| --- | --- | -| `name` _string_ | Name defines the name of the request header which to extract the value from. | -| `defaultValue` _string_ | DefaultValue defines the default value to use if the request header is not set. | - - -## ResourceProviderType - -_Underlying type:_ `string` - -ResourceProviderType defines the types of custom resource providers supported by Envoy Gateway. - -_Appears in:_ -- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) - - - -## ServiceType - -_Underlying type:_ `string` - -ServiceType string describes ingress methods for a service - -_Appears in:_ -- [KubernetesServiceSpec](#kubernetesservicespec) - - - -## TracingProvider - - - - - -_Appears in:_ -- [ProxyTracing](#proxytracing) - -| Field | Description | -| --- | --- | -| `type` _[TracingProviderType](#tracingprovidertype)_ | Type defines the tracing provider type. EG currently only supports OpenTelemetry. | -| `host` _string_ | Host define the provider service hostname. | -| `port` _integer_ | Port defines the port the provider service is exposed on. | - - -## TracingProviderType - -_Underlying type:_ `string` - - - -_Appears in:_ -- [TracingProvider](#tracingprovider) - - - -## XDSTranslatorHook - -_Underlying type:_ `string` - -XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support for the xds-translator - -_Appears in:_ -- [XDSTranslatorHooks](#xdstranslatorhooks) - - - -## XDSTranslatorHooks - - - -XDSTranslatorHooks contains all the pre and post hooks for the xds-translator runner. - -_Appears in:_ -- [ExtensionHooks](#extensionhooks) - -| Field | Description | -| --- | --- | -| `pre` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | -| `post` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | - - diff --git a/docs/latest/api/extension_types.md b/docs/latest/api/extension_types.md deleted file mode 100644 index 977cd2ac861..00000000000 --- a/docs/latest/api/extension_types.md +++ /dev/null @@ -1,391 +0,0 @@ -# API Reference - -## Packages -- [gateway.envoyproxy.io/v1alpha1](#gatewayenvoyproxyiov1alpha1) - - -## gateway.envoyproxy.io/v1alpha1 - -Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io API group. - - -### Resource Types -- [AuthenticationFilter](#authenticationfilter) -- [EnvoyPatchPolicy](#envoypatchpolicy) -- [EnvoyPatchPolicyList](#envoypatchpolicylist) -- [RateLimitFilter](#ratelimitfilter) - - - -## AuthenticationFilter - - - - - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `AuthenticationFilter` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[AuthenticationFilterSpec](#authenticationfilterspec)_ | Spec defines the desired state of the AuthenticationFilter type. | - - -## AuthenticationFilterSpec - - - -AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. - -_Appears in:_ -- [AuthenticationFilter](#authenticationfilter) - -| Field | Description | -| --- | --- | -| `type` _[AuthenticationFilterType](#authenticationfiltertype)_ | Type defines the type of authentication provider to use. Supported provider types are "JWT". | -| `jwtProviders` _[JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) array_ | JWT defines the JSON Web Token (JWT) authentication provider type. When multiple jwtProviders are specified, the JWT is considered valid if any of the providers successfully validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | - - -## AuthenticationFilterType - -_Underlying type:_ `string` - -AuthenticationFilterType is a type of authentication provider. - -_Appears in:_ -- [AuthenticationFilterSpec](#authenticationfilterspec) - - - -## ClaimToHeader - - - -ClaimToHeader defines a configuration to convert JWT claims into HTTP headers - -_Appears in:_ -- [JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) - -| Field | Description | -| --- | --- | -| `header` _string_ | Header defines the name of the HTTP request header that the JWT Claim will be saved into. | -| `claim` _string_ | Claim is the JWT Claim that should be saved into the header : it can be a nested claim of type (eg. "claim.nested.key", "sub"). The nested claim name must use dot "." to separate the JSON name path. | - - -## EnvoyJSONPatchConfig - - - -EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource using JSONPatch semantic - -_Appears in:_ -- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) - -| Field | Description | -| --- | --- | -| `type` _[EnvoyResourceType](#envoyresourcetype)_ | Type is the typed URL of the Envoy xDS Resource | -| `name` _string_ | Name is the name of the resource | -| `operation` _[JSONPatchOperation](#jsonpatchoperation)_ | Patch defines the JSON Patch Operation | - - -## EnvoyPatchPolicy - - - -EnvoyPatchPolicy allows the user to modify the generated Envoy xDS resources by Envoy Gateway using this patch API - -_Appears in:_ -- [EnvoyPatchPolicyList](#envoypatchpolicylist) - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `EnvoyPatchPolicy` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[EnvoyPatchPolicySpec](#envoypatchpolicyspec)_ | Spec defines the desired state of EnvoyPatchPolicy. | - - -## EnvoyPatchPolicyList - - - -EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `EnvoyPatchPolicyList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `items` _[EnvoyPatchPolicy](#envoypatchpolicy) array_ | | - - -## EnvoyPatchPolicySpec - - - -EnvoyPatchPolicySpec defines the desired state of EnvoyPatchPolicy. - -_Appears in:_ -- [EnvoyPatchPolicy](#envoypatchpolicy) - -| Field | Description | -| --- | --- | -| `type` _[EnvoyPatchType](#envoypatchtype)_ | Type decides the type of patch. Valid EnvoyPatchType values are "JSONPatch". | -| `jsonPatches` _[EnvoyJSONPatchConfig](#envoyjsonpatchconfig) array_ | JSONPatch defines the JSONPatch configuration. | -| `targetRef` _[PolicyTargetReference](#policytargetreference)_ | TargetRef is the name of the Gateway API resource this policy is being attached to. Currently only attaching to Gateway is supported This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway TargetRef | -| `priority` _integer_ | Priority of the EnvoyPatchPolicy. If multiple EnvoyPatchPolicies are applied to the same TargetRef, they will be applied in the ascending order of the priority i.e. int32.min has the highest priority and int32.max has the lowest priority. Defaults to 0. | - - - - -## EnvoyPatchType - -_Underlying type:_ `string` - -EnvoyPatchType specifies the types of Envoy patching mechanisms. - -_Appears in:_ -- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) - - - -## EnvoyResourceType - -_Underlying type:_ `string` - -EnvoyResourceType specifies the type URL of the Envoy resource. - -_Appears in:_ -- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) - - - -## GlobalRateLimit - - - -GlobalRateLimit defines global rate limit configuration. - -_Appears in:_ -- [RateLimitFilterSpec](#ratelimitfilterspec) - -| Field | Description | -| --- | --- | -| `rules` _[RateLimitRule](#ratelimitrule) array_ | Rules are a list of RateLimit selectors and limits. Each rule and its associated limit is applied in a mutually exclusive way i.e. if multiple rules get selected, each of their associated limits get applied, so a single traffic request might increase the rate limit counters for multiple rules if selected. | - - -## HeaderMatch - - - -HeaderMatch defines the match attributes within the HTTP Headers of the request. - -_Appears in:_ -- [RateLimitSelectCondition](#ratelimitselectcondition) - -| Field | Description | -| --- | --- | -| `type` _[HeaderMatchType](#headermatchtype)_ | Type specifies how to match against the value of the header. | -| `name` _string_ | Name of the HTTP header. | -| `value` _string_ | Value within the HTTP header. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. Do not set this field when Type="Distinct", implying matching on any/all unique values within the header. | - - -## HeaderMatchType - -_Underlying type:_ `string` - -HeaderMatchType specifies the semantics of how HTTP header values should be compared. Valid HeaderMatchType values are "Exact", "RegularExpression", and "Distinct". - -_Appears in:_ -- [HeaderMatch](#headermatch) - - - -## JSONPatchOperation - - - -JSONPatchOperation defines the JSON Patch Operation as defined in https://datatracker.ietf.org/doc/html/rfc6902 - -_Appears in:_ -- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) - -| Field | Description | -| --- | --- | -| `op` _[JSONPatchOperationType](#jsonpatchoperationtype)_ | Op is the type of operation to perform | -| `path` _string_ | Path is the location of the target document/field where the operation will be performed Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. | -| `value` _[JSON](#json)_ | Value is the new value of the path location. | - - -## JSONPatchOperationType - -_Underlying type:_ `string` - -JSONPatchOperationType specifies the JSON Patch operations that can be performed. - -_Appears in:_ -- [JSONPatchOperation](#jsonpatchoperation) - - - -## JwtAuthenticationFilterProvider - - - -JwtAuthenticationFilterProvider defines the JSON Web Token (JWT) authentication provider type and how JWTs should be verified: - -_Appears in:_ -- [AuthenticationFilterSpec](#authenticationfilterspec) - -| Field | Description | -| --- | --- | -| `name` _string_ | Name defines a unique name for the JWT provider. A name can have a variety of forms, including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. | -| `issuer` _string_ | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. | -| `audiences` _string array_ | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. | -| `remoteJWKS` _[RemoteJWKS](#remotejwks)_ | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. | -| `claimToHeaders` _[ClaimToHeader](#claimtoheader) array_ | ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers For examples, following config: The claim must be of type; string, int, double, bool. Array type claims are not supported | - - -## RateLimitFilter - - - -RateLimitFilter allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `RateLimitFilter` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[RateLimitFilterSpec](#ratelimitfilterspec)_ | Spec defines the desired state of RateLimitFilter. | - - -## RateLimitFilterSpec - - - -RateLimitFilterSpec defines the desired state of RateLimitFilter. - -_Appears in:_ -- [RateLimitFilter](#ratelimitfilter) - -| Field | Description | -| --- | --- | -| `type` _[RateLimitType](#ratelimittype)_ | Type decides the scope for the RateLimits. Valid RateLimitType values are "Global". | -| `global` _[GlobalRateLimit](#globalratelimit)_ | Global defines global rate limit configuration. | - - -## RateLimitRule - - - -RateLimitRule defines the semantics for matching attributes from the incoming requests, and setting limits for them. - -_Appears in:_ -- [GlobalRateLimit](#globalratelimit) - -| Field | Description | -| --- | --- | -| `clientSelectors` _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | ClientSelectors holds the list of select conditions to select specific clients using attributes from the traffic flow. All individual select conditions must hold True for this rule and its limit to be applied. If this field is empty, it is equivalent to True, and the limit is applied. | -| `limit` _[RateLimitValue](#ratelimitvalue)_ | Limit holds the rate limit values. This limit is applied for traffic flows when the selectors compute to True, causing the request to be counted towards the limit. The limit is enforced and the request is ratelimited, i.e. a response with 429 HTTP status code is sent back to the client when the selected requests have reached the limit. | - - -## RateLimitSelectCondition - - - -RateLimitSelectCondition specifies the attributes within the traffic flow that can be used to select a subset of clients to be ratelimited. All the individual conditions must hold True for the overall condition to hold True. - -_Appears in:_ -- [RateLimitRule](#ratelimitrule) - -| Field | Description | -| --- | --- | -| `headers` _[HeaderMatch](#headermatch) array_ | Headers is a list of request headers to match. Multiple header values are ANDed together, meaning, a request MUST match all the specified headers. | -| `sourceIP` _string_ | Deprecated: Use SourceCIDR instead. | -| `sourceCIDR` _[SourceMatch](#sourcematch)_ | SourceCIDR is the client IP Address range to match on. | - - -## RateLimitType - -_Underlying type:_ `string` - -RateLimitType specifies the types of RateLimiting. - -_Appears in:_ -- [RateLimitFilterSpec](#ratelimitfilterspec) - - - -## RateLimitUnit - -_Underlying type:_ `string` - -RateLimitUnit specifies the intervals for setting rate limits. Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". - -_Appears in:_ -- [RateLimitValue](#ratelimitvalue) - - - -## RateLimitValue - - - -RateLimitValue defines the limits for rate limiting. - -_Appears in:_ -- [RateLimitRule](#ratelimitrule) - -| Field | Description | -| --- | --- | -| `requests` _integer_ | | -| `unit` _[RateLimitUnit](#ratelimitunit)_ | | - - -## RemoteJWKS - - - -RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. - -_Appears in:_ -- [JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) - -| Field | Description | -| --- | --- | -| `uri` _string_ | URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to validate the server certificate. | - - -## SourceMatch - - - - - -_Appears in:_ -- [RateLimitSelectCondition](#ratelimitselectcondition) - -| Field | Description | -| --- | --- | -| `type` _[SourceMatchType](#sourcematchtype)_ | | -| `value` _string_ | Value is the IP CIDR that represents the range of Source IP Addresses of the client. These could also be the intermediate addresses through which the request has flown through and is part of the `X-Forwarded-For` header. For example, `192.168.0.1/32`, `192.168.0.0/24`, `001:db8::/64`. | - - -## SourceMatchType - -_Underlying type:_ `string` - - - -_Appears in:_ -- [SourceMatch](#sourcematch) - - - diff --git a/docs/latest/api_docs.rst b/docs/latest/api_docs.rst deleted file mode 100644 index b470527c50c..00000000000 --- a/docs/latest/api_docs.rst +++ /dev/null @@ -1,10 +0,0 @@ -API Docs -============== - -API reference documentation for Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - Config APIs - Extension APIs diff --git a/docs/latest/conf.py b/docs/latest/conf.py deleted file mode 100644 index 76ef5717548..00000000000 --- a/docs/latest/conf.py +++ /dev/null @@ -1,43 +0,0 @@ -import os - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.duration', - 'sphinx.ext.autosectionlabel', - 'myst_parser', -] - -autosectionlabel_prefix_document = True -myst_heading_anchors = 3 - -html_theme = 'alabaster' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -version = os.environ["BUILD_VERSION"] -envoyVersion = os.environ["ENVOY_PROXY_VERSION"] -gatewayAPIVersion = os.environ["GATEWAYAPI_VERSION"] - -project = 'Envoy Gateway' -author = 'Envoy Gateway Project Authors' - -copyright = 'Envoy Gateway Project Authors | GitHub | Latest Docs' - -source_suffix = { - '.rst': 'restructuredtext', - '.md': 'markdown', -} - -variables_to_export = [ - "version", - "envoyVersion", - "gatewayAPIVersion", -] - -frozen_locals = dict(locals()) -rst_epilog = '\n'.join(map(lambda x: f".. |{x}| replace:: {frozen_locals[x]}", variables_to_export)) -del frozen_locals diff --git a/docs/latest/design/accesslog.md b/docs/latest/design/accesslog.md deleted file mode 100644 index e8ab51b751f..00000000000 --- a/docs/latest/design/accesslog.md +++ /dev/null @@ -1,235 +0,0 @@ -# Observability: Accesslog - -## Overview - -Envoy supports extensible accesslog to different sinks, File, gRPC etc. Envoy supports customizable access log formats using predefined fields as well as arbitrary HTTP request and response headers. Envoy supports several built-in access log filters and extension filters that are registered at runtime. - -Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementors such as Envoy Gateway to expose features. Since accesslog is not covered by `Core` or `Extended` APIs, EG should provide an easy to config access log formats and sinks per `EnvoyProxy`. - -## Goals - -- Support send accesslog to `File` or `OpenTelemetry` backend -- TODO: Support access log filters base on [CEL](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/filters/cel/v3/cel.proto#extension-envoy-access-loggers-extension-filters-cel) expression - -## Non-Goals - -- Support non-CEL filters, e.g. `status_code_filter`, `response_flag_filter` -- Support [HttpGrpcAccessLogConfig](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-httpgrpcaccesslogconfig) or [TcpGrpcAccessLogConfig](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-tcpgrpcaccesslogconfig) - -## Use-Cases - -- Configure accesslog for a `EnvoyProxy` to `File` -- Configure accesslog for a `EnvoyProxy` to `OpenTelemetry` backend -- Configure multi accesslog providers for a `EnvoyProxy` - -### ProxyAccessLog API Type - -```golang mdox-exec="sed '1,7d' api/config/v1alpha1/accesslogging_types.go" -type ProxyAccessLog struct { - // Disable disables access logging for managed proxies if set to true. - Disable bool `json:"disable,omitempty"` - // Settings defines accesslog settings for managed proxies. - // If unspecified, will send default format to stdout. - // +optional - Settings []ProxyAccessLogSetting `json:"settings,omitempty"` -} - -type ProxyAccessLogSetting struct { - // Format defines the format of accesslog. - Format ProxyAccessLogFormat `json:"format"` - // Sinks defines the sinks of accesslog. - // +kubebuilder:validation:MinItems=1 - Sinks []ProxyAccessLogSink `json:"sinks"` -} - -type ProxyAccessLogFormatType string - -const ( - // ProxyAccessLogFormatTypeText defines the text accesslog format. - ProxyAccessLogFormatTypeText ProxyAccessLogFormatType = "Text" - // ProxyAccessLogFormatTypeJSON defines the JSON accesslog format. - ProxyAccessLogFormatTypeJSON ProxyAccessLogFormatType = "JSON" - // TODO: support format type "mix" in the future. -) - -// ProxyAccessLogFormat defines the format of accesslog. -// +union -type ProxyAccessLogFormat struct { - // Type defines the type of accesslog format. - // +kubebuilder:validation:Enum=Text;JSON - // +unionDiscriminator - Type ProxyAccessLogFormatType `json:"type,omitempty"` - // Text defines the text accesslog format, following Envoy accesslog formatting, - // empty value results in proxy's default access log format. - // It's required when the format type is "Text". - // Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. - // The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. - // +optional - Text *string `json:"text,omitempty"` - // JSON is additional attributes that describe the specific event occurrence. - // Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) - // can be used as values for fields within the Struct. - // It's required when the format type is "JSON". - // +optional - JSON map[string]string `json:"json,omitempty"` -} - -type ProxyAccessLogSinkType string - -const ( - // ProxyAccessLogSinkTypeFile defines the file accesslog sink. - ProxyAccessLogSinkTypeFile ProxyAccessLogSinkType = "File" - // ProxyAccessLogSinkTypeOpenTelemetry defines the OpenTelemetry accesslog sink. - ProxyAccessLogSinkTypeOpenTelemetry ProxyAccessLogSinkType = "OpenTelemetry" -) - -type ProxyAccessLogSink struct { - // Type defines the type of accesslog sink. - // +kubebuilder:validation:Enum=File;OpenTelemetry - Type ProxyAccessLogSinkType `json:"type,omitempty"` - // File defines the file accesslog sink. - // +optional - File *FileEnvoyProxyAccessLog `json:"file,omitempty"` - // OpenTelemetry defines the OpenTelemetry accesslog sink. - // +optional - OpenTelemetry *OpenTelemetryEnvoyProxyAccessLog `json:"openTelemetry,omitempty"` -} - -type FileEnvoyProxyAccessLog struct { - // Path defines the file path used to expose envoy access log(e.g. /dev/stdout). - // Empty value disables accesslog. - Path string `json:"path,omitempty"` -} - -// TODO: consider reuse ExtensionService? -type OpenTelemetryEnvoyProxyAccessLog struct { - // Host define the extension service hostname. - Host string `json:"host"` - // Port defines the port the extension service is exposed on. - // - // +optional - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:default=4317 - Port int32 `json:"port,omitempty"` - // Resources is a set of labels that describe the source of a log entry, including envoy node info. - // It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). - // +optional - Resources map[string]string `json:"resources,omitempty"` - - // TODO: support more OpenTelemetry accesslog options(e.g. TLS, auth etc.) in the future. -} -``` - -### Example - -1. The following is an example to disable access log. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/disable-accesslog.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: disable-accesslog - namespace: envoy-gateway-system -spec: - telemetry: - accessLog: - disabled: true -``` - -2. The following is an example with text format access log. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/text-accesslog.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: text-access-logging - namespace: envoy-gateway-system -spec: - telemetry: - accessLog: - settings: - - format: - type: Text - text: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - sinks: - - type: File - file: - path: /dev/stdout -``` - -1. The following is an example with json format access log. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/json-accesslog.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: json-access-logging - namespace: envoy-gateway-system -spec: - telemetry: - accessLog: - settings: - - format: - type: JSON - json: - status: "%RESPONSE_CODE%" - message: "%LOCAL_REPLY_BODY%" - sinks: - - type: File - file: - path: /dev/stdout -``` - -1. The following is an example with OpenTelemetry format access log. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/otel-accesslog.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: otel-access-logging - namespace: envoy-gateway-system -spec: - telemetry: - accessLog: - settings: - - format: - type: Text - text: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - sinks: - - type: OpenTelemetry - openTelemetry: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 - resources: - k8s.cluster.name: "cluster-1" -``` - -1. The following is an example of sending same format to different sinks. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/multi-sinks.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: multi-sinks - namespace: envoy-gateway-system -spec: - telemetry: - accessLog: - settings: - - format: - type: Text - text: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - sinks: - - type: File - file: - path: /dev/stdout - - type: OpenTelemetry - openTelemetry: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 - resources: - k8s.cluster.name: "cluster-1" -``` diff --git a/docs/latest/design/bootstrap.md b/docs/latest/design/bootstrap.md deleted file mode 100644 index 30f1b52116f..00000000000 --- a/docs/latest/design/bootstrap.md +++ /dev/null @@ -1,379 +0,0 @@ -# Bootstrap Design - -## Overview - -[Issue 31][] specifies the need for allowing advanced users to specify their custom -Envoy Bootstrap configuration rather than using the default Bootstrap configuration -defined in Envoy Gateway. This allows advanced users to extend Envoy Gateway and -support their custom use cases such setting up tracing and stats configuration -that is not supported by Envoy Gateway. - -## Goals - -* Define an API field to allow a user to specify a custom Bootstrap -* Provide tooling to allow the user to generate the default Bootstrap configuration - as well as validate their custom Bootstrap. - -## Non Goals - -* Allow user to configure only a section of the Bootstrap - -## API - -Leverage the existing [EnvoyProxy][] resource which can be attached to the [GatewayClass][] using -the [parametersRef][] field, and define a `Bootstrap` field within the resource. If this field is set, -the value is used as the Bootstrap configuration for all managed Envoy Proxies created by Envoy Gateway. - -```go -// EnvoyProxySpec defines the desired state of EnvoyProxy. -type EnvoyProxySpec struct { - ...... - // Bootstrap defines the Envoy Bootstrap as a YAML string. - // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap - // to learn more about the syntax. - // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration - // set by Envoy Gateway. - // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources - // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. - // Backward compatibility across minor versions is not guaranteed. - // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default - // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. - // - // +optional - Bootstrap *string `json:"bootstrap,omitempty"` -} -``` - -## Tooling - -A CLI tool `egctl x translate` will be provided to the user to help generate a working Bootstrap configuration. -Here is an example where a user inputs a `GatewayClass` and the CLI generates the `EnvoyProxy` resource with the `Bootstrap` field populated. - -``` -cat < /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: {} -EOF -``` - -This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. - -The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: - foo: bar -EOF -``` - -__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for -illustration purposes only. - -The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the -File provider: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: File - file: - foo: bar -EOF -``` - -__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration -purposes only. - -Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use -default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to -manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -gateway: - controllerName: foo -``` - -With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: - -```shell -$ ./envoy-gateway -``` - -## Data Plane API - -The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. -Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through -`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure -configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: - -* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane - infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. -* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: - > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the - > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are - > not propagated down to existing Gateways. - -The initial `EnvoyProxy` API: - -```go -// gateway/api/config/v1alpha1/envoyproxy.go - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EnvoyProxy is the Schema for the envoyproxies API. -type EnvoyProxy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EnvoyProxySpec `json:"spec,omitempty"` - Status EnvoyProxyStatus `json:"status,omitempty"` -} - -// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure -// configuration. -type EnvoyProxySpec struct { - // Undefined by this design spec. -} - -// EnvoyProxyStatus defines the observed state of EnvoyProxy. -type EnvoyProxyStatus struct { - // Undefined by this design spec. -} -``` - -The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use -cases are better understood. - -### Data Plane Configuration - -GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is -running with the Kubernetes provider. - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 -``` - -Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration -parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening -on port 80. - -The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer -service: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parametersRef: - name: example-config - group: config.gateway.envoyproxy.io - kind: EnvoyProxy ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: example-config -spec: - networkPublishing: - type: ClusterIPService -``` - -__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. - -[issue_51]: https://github.com/envoyproxy/gateway/issues/51 -[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md -[gw_api]: https://gateway-api.sigs.k8s.io/ -[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/docs/latest/design/egctl.md b/docs/latest/design/egctl.md deleted file mode 100644 index 7989ff49e5e..00000000000 --- a/docs/latest/design/egctl.md +++ /dev/null @@ -1,57 +0,0 @@ -# egctl Design - -## Motivation - -EG should provide a command line tool with following capabilities: - -- Collect configuration from envoy proxy and gateway -- Analyse system configuration to diagnose any issues in envoy gateway - -This tool is named `egctl`. - -## Syntax - -Use the following syntax to run `egctl` commands from your terminal window: - -```console -egctl [command] [entity] [name] [flags] -``` - -where `command`, `name`, and `flags` are: - -* `command`: Specifies the operation that you want to perform on one or more resources, - for example `config`, `version`. - -* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. - -* `name`: Specifies the name of the specified instance. - -* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. - -If you need help, run `egctl help` from the terminal window. - -## Operation - -The following table includes short descriptions and the general syntax for all the `egctl` operations: - -| Operation | Syntax | Description | -| --------------| -------------------------------- | -------------------------------------------------------------------------------------| -| `version` | `egctl version` | Prints out build version information. | -| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | -| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | -| `experimental`| `egctl experimental` | Subcommand for experimental features. These do not guarantee backwards compatibility | - -## Examples - -Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: - -```console -# Retrieve all information about proxy configuration from envoy -egctl config envoy-proxy all - -# Retrieve listener information about proxy configuration from envoy -egctl config envoy-proxy listener - -# Retrieve information about envoy gateway -egctl config envoy-gateway -``` diff --git a/docs/latest/design/envoy-patch-policy.md b/docs/latest/design/envoy-patch-policy.md deleted file mode 100644 index 0116c8e6336..00000000000 --- a/docs/latest/design/envoy-patch-policy.md +++ /dev/null @@ -1,174 +0,0 @@ -# EnvoyPatchPolicy - -## Overview - -This design introduces the `EnvoyPatchPolicy` API allowing users to modify the generated Envoy xDS Configuration -that Envoy Gateway generates before sending it to Envoy Proxy. - -Envoy Gateway allows users to configure networking and security intent using the -upstream [Gateway API][] as well as implementation specific [Extension APIs][] defined in this project -to provide a more batteries included experience for application developers. -* These APIs are an abstracted version of the underlying Envoy xDS API to provide a better user experience for the application developer, exposing and setting only a subset of the fields for a specific feature, sometimes in a opinionated way (e.g [RateLimit][]) -* These APIs do not expose all the features capabilities that Envoy has either because these features are desired but the API -is not defined yet or the project cannot support such an extensive list of features. -To alleviate this problem, and provide an interim solution for a small section of advanced users who are well versed in -Envoy xDS API and its capabilities, this API is being introduced. - -## Goals -* Add an API allowing users to modify the generated xDS Configuration - -## Non Goals -* Support multiple patch mechanisims - -## Implementation -`EnvoyPatchPolicy` is a [Direct Policy Attachment][] type API that can be used to extend [Gateway API][] -Modifications to the generated xDS configuration can be provided as a JSON Patch which is defined in -[RFC 6902][]. This patching mechanism has been adopted in [Kubernetes][] as well as [Kustomize][] to update -resource objects. - -### Example -Here is an example highlighting how a user can configure global ratelimiting using an external rate limit service using this API. - -``` -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: eg -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: eg - namespace: default -spec: - gatewayClassName: eg - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: backend - namespace: default -spec: - parentRefs: - - name: eg - hostnames: - - "www.example.com" - rules: - - backendRefs: - - group: "" - kind: Service - name: backend - port: 3000 - weight: 1 - matches: - - path: - type: PathPrefix - value: / ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: EnvoyPatchPolicy -metadata: - name: ratelimit-patch-policy - namespace: default -spec: - targetRef: - group: gateway.networking.k8s.io - kind: Gateway - name: eg - namespace: default - type: JSONPatch - jsonPatches: - - type: "type.googleapis.com/envoy.config.listener.v3.Listener" - # The listener name is of the form // - name: default/eg/http - operation: - op: add - path: "/default_filter_chain/filters/0/typed_config/http_filters/0" - value: - name: "envoy.filters.http.ratelimit" - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" - domain: "eag-ratelimit" - failure_mode_deny: true - timeout: 1s - rate_limit_service: - grpc_service: - envoy_grpc: - cluster_name: rate-limit-cluster - transport_api_version: V3 - - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" - # The route name is of the form // - name: default/eg/http - operation: - op: add - path: "/virtual_hosts/0/rate_limits" - value: - - actions: - - remote_address: {} - - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" - name: rate-limit-cluster - operation: - op: add - path: "" - value: - name: rate-limit-cluster - type: STRICT_DNS - connect_timeout: 10s - lb_policy: ROUND_ROBIN - http2_protocol_options: {} - load_assignment: - cluster_name: rate-limit-cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ratelimit.svc.cluster.local - port_value: 8081 -``` - - -## Verification -* Offline - Leverage [egctl x translate][] to ensure that the `EnvoyPatchPolicy` can be successfully applied and the desired -output xDS is created. -* Runtime - Use the `Status` field within `EnvoyPatchPolicy` to highlight whether the patch was applied successfully or not. - -## State of the World -* Istio - Supports the [EnvoyFilter][] API which allows users to customize the output xDS using patches and proto based merge -semantics. - -## Design Decisions -* This API will only support a single `targetRef` and can bind to only a `Gateway` resource. This simplifies reasoning of how -patches will work. -* This API will always be an experimental API and cannot be graduated into a stable API because Envoy Gateway cannot garuntee - * that the naming scheme for the generated resources names will not change across releases - * that the underlying Envoy Proxy API will not change across releases -* This API needs to be explicitly enabled using the [EnvoyGateway][] API - -## Open Questions -* Should the value only support JSON or YAML as well (which is a JSON superset) ? - -## Alternatives -* Users can customize the Envoy [Bootstrap configuration using EnvoyProxy API][] and provide static xDS configuration. -* Users can extend functionality by [Extending the Control Plane][] and adding gRPC hooks to modify the generated xDS configuration. - - - -[Direct Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/#direct-policy-attachment -[RFC 6902]: https://datatracker.ietf.org/doc/html/rfc6902 -[Gateway API]: https://gateway-api.sigs.k8s.io/ -[Kubernetes]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ -[Kustomize]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md -[Extension APIs]: https://gateway.envoyproxy.io/latest/api/extension_types.html -[RateLimit]: https://gateway.envoyproxy.io/latest/user/rate-limit.html -[EnvoyGateway]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoygateway -[Extending the Control Plane]: https://gateway.envoyproxy.io/latest/design/extending-envoy-gateway.html -[EnvoyFilter]: https://istio.io/latest/docs/reference/config/networking/envoy-filter -[egctl x translate]: https://gateway.envoyproxy.io/latest/user/egctl.html#egctl-experimental-translate -[Bootstrap configuration using EnvoyProxy API]: https://gateway.envoyproxy.io/latest/user/customize-envoyproxy.html#customize-envoyproxy-bootstrap-config diff --git a/docs/latest/design/extending-envoy-gateway.md b/docs/latest/design/extending-envoy-gateway.md deleted file mode 100644 index df56964a573..00000000000 --- a/docs/latest/design/extending-envoy-gateway.md +++ /dev/null @@ -1,325 +0,0 @@ -# Envoy Gateway Extensions Design - -As outlined in the [official goals][] for the Envoy Gateway project, one of the main goals is to "provide a common foundation for vendors to build value-added products -without having to re-engineer fundamental interactions". Development of the Envoy Gateway project has been focused on developing the core features for the project and -Kubernetes Gateway API conformance. This system focuses on the “common foundation for vendors” component by introducing a way for vendors to extend Envoy Gateway. - -To meaningfully extend Envoy Gateway and provide additional features, Extensions need to be able to introduce their own custom resources and have a high level of control -over the configuration generated by Envoy Gateway. Simply applying some static xDS configuration patches or relying on the existing Gateway API resources are both insufficient on their own -as means to add larger features that require dynamic user-configuration. - -As an example, an extension developer may wish to provide their own out-of-the-box authentication filters that require configuration from the end-user. This is a scenario where the ability to introduce -custom resources and attach them to [HTTPRoute][]s as an [ExtensionRef][] is necessary. Providing the same feature through a series of xDS patch resources would be too cumbersome for many end-users that want to avoid -that level of complexity when managing their clusters. - -## Goals - -- Provide a foundation for extending the Envoy Gateway control plane -- Allow Extension Developers to introduce their own custom resources for extending the Gateway-API via [ExtensionRefs][], [policyAttachments][] (future) and [backendRefs][] (future). -- Extension developers should **NOT** have to maintain a custom fork of Envoy Gateway -- Provide a system for extending Envoy Gateway which allows extension projects to ship updates independent of Envoy Gateway's release schedule -- Modify the generated Envoy xDS config -- Setup a foundation for the initial iteration of Extending Envoy Gateway -- Allow an Extension to hook into the infra manager pipeline (future) - -## Non-Goals - -- The initial design does not capture every hook that Envoy Gateway will eventually support. -- Extend [Gateway API Policy Attachments][]. At some point, these will be addressed using this extension system, but the initial implementation omits these. -- Support multiple extensions at the same time. Due to the fact that extensions will be modifying xDS resources after they are generated, handling the order of extension execution for each individual hook point is a challenge. Additionally, there is no -real way to prevent one extension from overwriting or breaking modifications to xDS resources that were made by another extension that was executed first. - -## Overview - -Envoy Gateway can be extended by vendors by means of an extension server developed by the vendor and deployed alongside Envoy Gateway. -An extension server can make use of one or more pre/post hooks inside Envoy Gateway before and after its major components (translator, etc.) to allow the extension to modify the data going into or coming out of these components. -An extension can be created external to Envoy Gateway as its own Kubernetes deployment or loaded as a sidecar. gRPC is used for the calls between Envoy Gateway and an extension. In the hook call, Envoy Gateway sends data as well -as context information to the extension and expects a reply with a modified version of the data that was sent to the extension. Since extensions fundamentally alter the logic and data that Envoy Gateway provides, Extension projects assume responsibility for any bugs and issues -they create as a direct result of their modification of Envoy Gateway. - -## Diagram - -![Architecture](../images/extension-example.png) - -## Registering Extensions in Envoy Gateway - -Information about the extension that Envoy Gateway needs to load is configured in the Envoy Gateway config. - -An example configuration: - -```yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -extensionManager: - resources: - - group: example.myextension.io - version: v2 - kind: OAuth2Filter - hooks: - xdsTranslator: - post: - - Route - - VirtualHost - - HTTPListener - - Translation - service: - host: my-extension.example - port: 443 - tls: - certificateRef: - name: my-secret - namespace: default -``` - -An extension must supply connection information in the `extension.service` field so that Envoy Gateway can communicate with the extension. The `tls` configuration is optional. - -If the extension wants Envoy Gateway to watch resources for it then the extension must configure the optional `extension.resources` field and supply a list of: - -- `group`: the API group of the resource -- `version`: the API version of the resource -- `kind`: the Kind of resource - -The extension can configure the `extensionManager.hooks` field to specify which hook points it would like to support. If a given hook is not listed here then it will not be executed even -if the extension is configured properly. This allows extension developers to only opt-in to the hook points they want to make use of. - -This configuration is required to be provided at bootstrap and modifying the registered extension during runtime is not currently supported. -Envoy Gateway will keep track of the registered extension and its API `groups` and `kinds` when processing Gateway API resources. - -## Extending Gateway API and the Data Plane - -Envoy Gateway manages [Envoy][] deployments, which act as the data plane that handles actual user traffic. Users configure the data plane using the K8s Gateway API resources which Envoy -Gateway converts into [Envoy specific configuration (xDS)][] to send over to Envoy. - -Gateway API offers [ExtensionRef filters][] and [Policy Attachments][] as extension points for implementers to use. Envoy Gateway extends the Gateway API using these extension points to provide support for [rate limiting][] -and [authentication][] native to the project. The initial design of Envoy Gateway extensions will primarily focus on ExtensionRef filters so that extension developers can reference their own resources as HTTP Filters in the same way -that Envoy Gateway has native support for rate limiting and authentication filters. - -When Envoy Gateway encounters an [HTTPRoute][] or [GRPCRoute][] that has an `ExtensionRef` `filter` with a `group` and `kind` that Envoy Gateway does not support, it will first -check the registered extension to determine if it supports the referenced object before considering it a configuration error. - -This allows users to be able to reference additional filters provided by their Envoy Gateway Extension, in their `HTTPRoute`s / `GRPCRoute`s: - -```yaml -apiVersion: example.myextension.io/v1alpha1 -kind: OAuth2Filter -metadata: - name: oauth2-filter -spec: - ... - ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - clientSelectors: - - path: - type: PathPrefix - value: / - filters: - - type: ExtensionRef - extensionRef: - group: example.myextension.io - kind: OAuth2Filter - name: oauth2-filter - backendRefs: - - name: backend - port: 3000 -``` - -In order to enable the usage of new resources introduced by an extension for translation and xDS modification, Envoy Gateway provides hook points within the translation pipeline, where it calls out to the extension service registered in the [EnvoyGateway config][] -if they specify an `group` that matches the `group` of an `ExtensionRef` filter. The extension will then be able to modify the xDS that Envoy Gateway generated and send back the -modified configuration. If an extension is not registered or if the registered extension does not specify support for the `group` of an `ExtensionRef` filter then Envoy Gateway will treat it as an unknown resource -and provide an error to the user. - -**Note:** Currently (as of [v1beta1][]) Gateway API does not provide a means to specify the namespace or version of an object referenced as an `ExtensionRef`. The extension mechanism will assume that -the namespace of any `ExtensionRef` is the same as the namespace of the `HTTPRoute` or `GRPCRoute` it is attached to rather than treating the `name` field of an `ExtensionRef` as a `name.namespace` string. -If Gateway API adds support for these fields then the design of the Envoy Gateway extensions will be updated to support them. - -## Watching New Resources - -Envoy Gateway will dynamically create new watches on resources introduced by the registered Extension. It does so by using the [controller-runtime][] to create new watches on [Unstructured][] resources that match the `version`s, `group`s, and `kind`s that the -registered extension configured. When communicating with an extension, Envoy Gateway sends these Unstructured resources over to the extension. This eliminates the need for the extension to create its own watches which would have a strong chance of creating race conditions and reconciliation loops when resources change. When an extension receives the Unstructured resources from Envoy Gateway it can perform its own type validation on them. Currently we make the simplifying assumption that the registered extension's `Kinds` are filters referenced by `extensionRef` in `HTTPRouteFilter`s . Support for Policy attachments will be introduced at a later time. - -## xDS Hooks API - -Envoy Gateway supports the following hooks as the initial foundation of the Extension system. Additional hooks can be developed using this extension system at a later point as new use-cases and needs are discovered. The primary iteration of the extension hooks -focuses solely on the modification of xDS resources. - -### Route Modification Hook - -The [Route][] level Hook provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized. -Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the -Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to ext_authz filters. -The Post Route Modify hook also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that created this xDS route -This hook is always executed when an extension is loaded that has added `Route` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`, and only on Routes which were generated from an HTTPRoute that uses extension resources as externalRef filters. - -```go -// PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified -message PostRouteModifyRequest { - envoy.config.route.v3.Route route = 1; - PostRouteExtensionContext post_route_context = 2; -} - -// RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway -// additional context information can be added to this message as more use-cases are discovered -message PostRouteExtensionContext { - // Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute - repeated ExtensionResource extension_resources = 1; - - // hostnames are the fully qualified domain names attached to the HTTPRoute - repeated string hostnames = 2; -} - -// ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter -// extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension -// can marshal the bytes from this resource back into an unstructured.Unstructured and then -// perform type checking to obtain the resource. -message ExtensionResource { - bytes unstructured_bytes = 1; -} - -// PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent -// If an extension returns a nil Route then it will not be modified -message PostRouteModifyResponse { - envoy.config.route.v3.Route route = 1; -} -``` - -### VirtualHost Modification Hook - -The [VirtualHost][] Hook provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized. -An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway. -This hook is always executed when an extension is loaded that has added `VirtualHost` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. -An extension may return nil to not make any changes to the VirtualHost. - -```protobuf -// PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified -message PostVirtualHostModifyRequest { - envoy.config.route.v3.VirtualHost virtual_host = 1; - PostVirtualHostExtensionContext post_virtual_host_context = 2; -} - -// Empty for now but we can add fields to the context as use-cases are discovered without -// breaking any clients that use the API -// additional context information can be added to this message as more use-cases are discovered -message PostVirtualHostExtensionContext {} - -// PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent -// If an extension returns a nil Virtual Host then it will not be modified -message PostVirtualHostModifyResponse { - envoy.config.route.v3.VirtualHost virtual_host = 1; -} -``` - -### HTTP Listener Modification Hook - -The HTTP [Listener][] modification hook is the broadest xDS modification Hook available and allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized. -This hook is always executed when an extension is loaded that has added `HTTPListener` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. An extension may return nil -in order to not make any changes to the Listener. - -```protobuf -// PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified -message PostHTTPListenerModifyRequest { - envoy.config.listener.v3.Listener listener = 1; - PostHTTPListenerExtensionContext post_listener_context = 2; -} - -// Empty for now but we can add fields to the context as use-cases are discovered without -// breaking any clients that use the API -// additional context information can be added to this message as more use-cases are discovered -message PostHTTPListenerExtensionContext {} - -// PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent -// If an extension returns a nil Listener then it will not be modified -message PostHTTPListenerModifyResponse { - envoy.config.listener.v3.Listener listener = 1; -} -``` - -### Post xDS Translation Modify Hook - -The Post Translate Modify hook allows an extension to modify the clusters and secrets in the xDS config. -This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than -using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed. -An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension. -The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets -This hook is always executed when an extension is loaded that has added `Translation` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. - -```protobuf -// PostTranslateModifyRequest currently sends only clusters and secrets to an extension. -// The extension is free to add/modify/remove the resources it received. -message PostTranslateModifyRequest { - PostTranslateExtensionContext post_translate_context = 1; - repeated envoy.config.cluster.v3.Cluster clusters = 2; - repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3; -} - -// PostTranslateModifyResponse is the expected response from an extension and contains -// the full list of xDS clusters and secrets to be used for the xDS config. -message PostTranslateModifyResponse { - repeated envoy.config.cluster.v3.Cluster clusters = 1; - repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 2; -} -``` - -### Extension Service - -Currently, an extension must implement all of the following hooks although it may return the input(s) it received -if no modification of the resource is desired. A future expansion of the extension hooks will allow an Extension to specify -with config which Hooks it would like to "subscribe" to and which Hooks it does not wish to support. These specific Hooks were chosen -in order to provide extensions with the ability to have both broad and specific control over xDS resources and to minimize the amount of data being sent. - -```protobuf -service EnvoyGatewayExtension { - rpc PostRouteModify (PostRouteModifyRequest) returns (PostRouteModifyResponse) {}; - rpc PostVirtualHostModify(PostVirtualHostModifyRequest) returns (PostVirtualHostModifyResponse) {}; - rpc PostHTTPListenerModify(PostHTTPListenerModifyRequest) returns (PostHTTPListenerModifyResponse) {}; - rpc PostTranslateModify(PostTranslateModifyRequest) returns (PostTranslateModifyResponse) {}; -} -``` - -## Design Decisions - -- Envoy Gateway watches new custom resources introduced by a loaded extension and passes the resources back to the extension when they are used. - - This decision was made to solve the problem about how resources introduced by an extension get watched. If an extension server watches its own resources then it would need some way to trigger an Envoy Gateway reconfigure when a resource that Envoy Gateway is not watching gets updated. Having Envoy Gateway watch all resources removes any concern about creating race confitions or reconcile loops that would result from Envoy Gateway and the extension server both having so much separate state that needs to be synchronized. -- The Extension Server takes ownership of producing the correct xDS configuration in the hook responses -- The Extension Server will be responsible for ensuring the performance of the hook processing time -- The Post xDS level gRPC hooks all currently send a context field even though it contains nothing for several hooks. These fields exist so that they can be updadated in the future to pass -additional information to extensions as new use-cases and needs are discovered. -- The initial design supplies the scaffolding for both "pre xDS" and "post xDS" hooks. Only the post hooks are currently implemented which operate on xDS resources after they have been generated. -The pre hooks will be implemented at a later date along with one or more hooks in the infra manager. The infra manager level hook(s) will exist to power use-cases such as dynamically creating Deployments/Services for the extension the -whenever Envoy Gateway creates an instance of Envoy Proxy. An extension developer might want to take advantage of this functionality to inject a new authorization service as a sidecar on the Envoy Proxy deployment for reduced latency. -- Multiple extensions are not be supported at the same time. Preventing conflict between multiple extensions that are mangling xDS resources is too difficult to ensure compatibility with and is likely to only generate issues. - -## Known Challenges - -Extending Envoy Gateway by using an external extension server which makes use of hook points in Envoy Gateway does comes with a few trade-offs. One known trade-off is the impact of the time that it takes for the hook calls to be executed. Since an extension would make use of hook points in Envoy Gateway that use gRPC for communication, the time it takes to perform these requests could become a concern for some extension developers. One way to minimize the request time of the hook calls is to load the extension server as a sidecar to Envoy Gateway to minimize the impact of networking on the hook calls. - -[official goals]: https://github.com/envoyproxy/gateway/blob/main/GOALS.md#extensibility -[ExtensionRef filters]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference -[ExtensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference -[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference -[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendObjectReference -[Gateway API Policy attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy -[Policy Attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy -[policyAttachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy -[Envoy]: https://www.envoyproxy.io/ -[Envoy specific configuration (xDS)]: https://www.envoyproxy.io/docs/envoy/v1.25.1/configuration/configuration -[v1beta1]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1 -[rate limiting]: https://gateway.envoyproxy.io/v0.3.0/user/rate-limit.html -[authentication]: https://gateway.envoyproxy.io/v0.3.0/user/authn.html -[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute -[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute -[EnvoyGateway config]: https://gateway.envoyproxy.io/v0.3.0/api/config_types.html#envoygateway -[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime -[Unstructured]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured -[Listener]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener -[VirtualHost]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost -[Route]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-route diff --git a/docs/latest/design/gatewayapi-translator.md b/docs/latest/design/gatewayapi-translator.md deleted file mode 100644 index 3cf0da94f5a..00000000000 --- a/docs/latest/design/gatewayapi-translator.md +++ /dev/null @@ -1,250 +0,0 @@ -# Gateway API Translator Design - -The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate -Representation (IR). - -## Assumptions - -Initially target core conformance features only, to be followed by extended conformance features. - -## Inputs and Outputs - -The main inputs to the Gateway API translator are: - -- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. - -__Note:__ ReferenceGrant is not fully implemented as of v0.2. - -The outputs of the Gateway API translator are: - -- Xds and Infra Internal Representations (IRs). -- Status updates for GatewayClass, Gateways, HTTPRoutes - -## Listener Compatibility - -Envoy Gateway follows Gateway API listener compatibility spec: -> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. - -__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. - -### Listener Compatibility Examples - -#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -## Computing Status - -Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway -must compute the appropriate status fields and conditions for managed resources. - -Status is computed and set for: - -- The managed GatewayClass (`gatewayclass.status.conditions`). -- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the - Envoy Deployment and Service status are also included to calculate Gateway status. -- Listeners for each Gateway (`gateway.status.listeners`). -- The ParentRef for each Route (`route.status.parents`). - -The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to -the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and -updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to -update resource status on the Kubernetes API server. - -## Outline - -The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway -API resources. - -1. Process Gateway Listeners - - Validate unique hostnames, ports, and protocols. - - Validate and compute supported kinds. - - Validate allowed namespaces (validate selector if specified). - - Validate TLS fields if specified, including resolving referenced Secrets. - -2. Process HTTPRoutes - - foreach route rule: - - compute matches - - [core] path exact, path prefix - - [core] header exact - - [extended] query param exact - - [extended] HTTP method - - compute filters - - [core] request header modifier (set/add/remove) - - [core] request redirect (hostname, statuscode) - - [extended] request mirror - - compute backends - - [core] Kubernetes services - - foreach route parent ref: - - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) - - foreach matching listener: - - foreach hostname intersection with route: - - add each computed route rule to host - -## Context Structs - -To help store, access and manipulate information as it's processed during the translation process, a set of context -structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support -processing. - -`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. - -```go -type GatewayContext struct { - // The managed Gateway - *v1beta1.Gateway - - // A list of Gateway ListenerContexts. - listeners []*ListenerContext -} -``` - -`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on -the associated Gateway. - -```go -type ListenerContext struct { - // The Gateway listener. - *v1beta1.Listener - - // The Gateway this Listener belongs to. - gateway *v1beta1.Gateway - - // An index used for managing this listener in the list of Gateway listeners. - listenerStatusIdx int - - // Only Routes in namespaces selected by the selector may be attached - // to the Gateway this listener belongs to. - namespaceSelector labels.Selector - - // The TLS Secret for this Listener, if applicable. - tlsSecret *v1.Secret -} -``` - -`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. - -```go -type RouteContext interface { - client.Object - - // GetRouteType returns the Kind of the Route object, HTTPRoute, - // TLSRoute, TCPRoute, UDPRoute etc. - GetRouteType() string - - // GetHostnames returns the hosts targeted by the Route object. - GetHostnames() []string - - // GetParentReferences returns the ParentReference of the Route object. - GetParentReferences() []v1beta1.ParentReference - - // GetRouteParentContext returns RouteParentContext by using the Route - // objects' ParentReference. - GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext -} -``` - -[message bus]: watching.md diff --git a/docs/latest/design/local-envoy-gateway.md b/docs/latest/design/local-envoy-gateway.md deleted file mode 100644 index d382b8cfff8..00000000000 --- a/docs/latest/design/local-envoy-gateway.md +++ /dev/null @@ -1,50 +0,0 @@ -# Running Envoy Gateway locally - -## Overview - -Today, Envoy Gateway runs only on Kubernetes. This is an ideal solution -when the applications are running in Kubernetes. -However there might be cases when the applications are running on the host which would -require Envoy Gateway to run locally. - -## Goals - -* Define an API to allow Envoy Gateway to retrieve configuration while running locally. -* Define an API to allow Envoy Gateway to deploy the managed Envoy Proxy fleet on the host -machine. - -## Non Goals - -* Support multiple ways to retrieve configuration while running locally. -* Support multiple ways to deploy the Envoy Proxy fleet locally on the host. - -## API - -* The `provider` field within the `EnvoyGateway` configuration only supports -`Kubernetes` today which provides two features - the ability to retrieve -resources from the Kubernetes API Server as well as deploy the managed -Envoy Proxy fleet on Kubernetes. -* This document proposes adding a new top level `provider` type called `Custom` -with two fields called `resource` and `infrastructure` to allow the user to configure -the sub providers for providing resource configuration and an infrastructure to deploy -the Envoy Proxy data plane in. -* A `File` resource provider will be introduced to enable retrieveing configuration locally -by reading from the configuration from a file. -* A `Host` infrastructure provider will be introduced to allow Envoy Gateway to spawn a -Envoy Proxy child process on the host. - -Here is an example configuration - -``` -provider: - type: Custom - custom: - resource: - type: File - file: - paths: - - "config.yaml" - infrastructure: - type: Host - host: {} -``` diff --git a/docs/latest/design/metrics.md b/docs/latest/design/metrics.md deleted file mode 100644 index e01260bcd19..00000000000 --- a/docs/latest/design/metrics.md +++ /dev/null @@ -1,106 +0,0 @@ -# Observability: Metrics - -## Overview - -Envoy provide robust platform for metrics, Envoy support three different kinds of stats: counter, gauges, histograms. - -Envoy enables prometheus format output via the `/stats/prometheus` [admin endpoint](https://www.envoyproxy.io/docs/envoy/latest/operations/admin). - -Envoy support different kinds of sinks, but EG will only support [Open Telemetry sink](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto). - -Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementors such as Envoy Gateway to expose features. Since metrics is not covered by `Core` or `Extended` APIs, EG should provide an easy to config metrics per `EnvoyProxy`. - -## Goals - -- Support expose metrics in prometheus way(reuse probe port). -- Support Open Telemetry stats sink. - -## Non-Goals - -- Support other stats sink. - -## Use-Cases - -- Enable prometheus metric -- Push metrics via Open Telemetry Sink -- TODO: Customize histogram buckets of target metric -- TODO: Support stats matcher - -### ProxyMetric API Type - -```golang mdox-exec="sed '1,7d' api/config/v1alpha1/metric_types.go" -type ProxyMetrics struct { - // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. - Prometheus *PrometheusProvider `json:"prometheus,omitempty"` - // Sinks defines the metric sinks where metrics are sent to. - Sinks []MetricSink `json:"sinks,omitempty"` -} - -type MetricSinkType string - -const ( - MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" -) - -type MetricSink struct { - // Type defines the metric sink type. - // EG currently only supports OpenTelemetry. - // +kubebuilder:validation:Enum=OpenTelemetry - // +kubebuilder:default=OpenTelemetry - Type MetricSinkType `json:"type"` - // OpenTelemetry defines the configuration for OpenTelemetry sink. - // It's required if the sink type is OpenTelemetry. - OpenTelemetry *OpenTelemetrySink `json:"openTelemetry,omitempty"` -} - -type OpenTelemetrySink struct { - // Host define the service hostname. - Host string `json:"host"` - // Port defines the port the service is exposed on. - // - // +optional - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:Maximum=65535 - // +kubebuilder:default=4317 - Port int32 `json:"port,omitempty"` - - // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig -} - -type PrometheusProvider struct { -} -``` - -### Example - -1. The following is an example to enable prometheus metric. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/prometheus.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: prometheus - namespace: envoy-gateway-system -spec: - telemetry: - metrics: - prometheus: {} -``` - -1. The following is an example to send metric via Open Telemetry sink. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/otel-sink.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: otel-sink - namespace: envoy-gateway-system -spec: - telemetry: - metrics: - sinks: - - type: OpenTelemetry - openTelemetry: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 -``` diff --git a/docs/latest/design/pprof.md b/docs/latest/design/pprof.md deleted file mode 100644 index 0e0f3bdb64a..00000000000 --- a/docs/latest/design/pprof.md +++ /dev/null @@ -1,49 +0,0 @@ -# Add Pprof support in Envoy Gateway - -## Overview - -Envoy Gateway exposes endpoints at `localhost:8899/debug/pprof` to run Golang profiles to aid in live debugging. The endpoints are equivalent to those found in the http/pprof package. `/debug/pprof/` returns an HTML page listing the available profiles. - -## Goals - -* Add Debug Pprof support to Envoy Gateway control plane. -* Define an API to allow Envoy Gateway to custom debug server configuration. - -The following are the different types of profiles end-user can run: - -PROFILE | FUNCTION --- | -- -/debug/pprof/allocs | Returns a sampling of all past memory allocations. -/debug/pprof/block | Returns stack traces of goroutines that led to blocking on synchronization primitives. -/debug/pprof/cmdline | Returns the command line that was invoked by the current program. -/debug/pprof/goroutine | Returns stack traces of all current goroutines. -/debug/pprof/heap | Returns a sampling of memory allocations of live objects. -/debug/pprof/mutex | Returns stack traces of goroutines holding contended mutexes. -/debug/pprof/profile | Returns pprof-formatted cpu profile. You can specify the duration using the seconds GET parameter. The default duration is 30 seconds. -/debug/pprof/symbol | Returns the program counters listed in the request. -/debug/pprof/threadcreate | Returns stack traces that led to creation of new OS threads. -/debug/pprof/trace | Returns the execution trace in binary form. You can specify the duration using the seconds GET parameter. The default duration is 1 second. - -## Non Goals - -## API - -* Add `admin` field in EnvoyGateway config. -* Add `debug` field under `admin` field. -* Add `enable`, `port` and `host` under `address` field. - -Here is an example configuration - -``` yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -gateway: - controllerName: "gateway.envoyproxy.io/gatewayclass-controller" -kind: EnvoyGateway -provider: - type: "Kubernetes" -admin: - debug: true - address: - port: 8899 - host: "127.0.0.1" -``` diff --git a/docs/latest/design/rate-limit.md b/docs/latest/design/rate-limit.md deleted file mode 100644 index 10ef7a3fc93..00000000000 --- a/docs/latest/design/rate-limit.md +++ /dev/null @@ -1,417 +0,0 @@ -# Rate Limit Design - -## Overview - -Rate limit is a feature that allows the user to limit the number of incoming requests -to a predefined value based on attributes within the traffic flow. - -Here are some reasons why a user may want to implements Rate limits - -* To prevent malicious activity such as DDoS attacks. -* To prevent applications and its resources (such as a database) from getting overloaded. -* To create API limits based on user entitlements. - -## Scope Types - -The rate limit type here describes the scope of rate limits. - -* Global - In this case, the rate limit is common across all the instances of Envoy proxies -where its applied i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is -10 requests/second, this limit is common and will be hit if 5 requests pass through the first replica -and 5 requests pass through the second replica within the same second. - -* Local - In this case, the rate limits are specific to each instance/replica of Envoy running. -Note - This is not part of the initial design and will be added as a future enhancement. - -## Match Types - -### Rate limit a specific traffic flow - -* Here is an example of a ratelimit implemented by the application developer to limit a specific user -by matching on a custom `x-user-id` header with a value set to `one` - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-specific-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - name: x-user-id - value: one - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-specific-user - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit all traffic flows - -* Here is an example of a rate limit implemented by the application developer that limits the total requests made -to a specific route to safeguard health of internal application components. In this case, no specific `headers` match -is specified, and the rate limit is applied to all traffic flows accepted by this `HTTPRoute`. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-all-requests -spec: - type: Global - global: - rules: - - limit: - requests: 1000 - unit: Second ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-all-requests - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit per distinct value - -* Here is an example of a rate limit implemented by the application developer to limit any unique user -by matching on a custom `x-user-id` header. Here, user A (recognised from the traffic flow using the header -`x-user-id` and value `a`) will be rate limited at 10 requests/hour and so will user B -(recognised from the traffic flow using the header `x-user-id` and value `b`). - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-per-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - type: Distinct - name: x-user-id - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-per-user - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit per source IP - -* Here is an example of a rate limit implemented by the application developer that limits the total requests made -to a specific route by matching on source IP. In this case, requests from `x.x.x.x` will be rate limited at 10 requests/hour. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-per-ip -spec: - type: Global - global: - rules: - - clientSelectors: - - sourceIP: x.x.x.x/32 - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-per-user - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit based on JWT claims - -* Here is an example of rate limit implemented by the application developer that limits the total requests made -to a specific route by matching on the jwt claim. In this case, requests with jwt claim information of `{"name":"John Doe"}` -will be rate limited at 10 requests/hour. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: jwt-example -spec: - type: JWT - jwtProviders: - - name: example - remoteJWKS: - uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json - claimToHeaders: - - claim: name - header: custom-request-header ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-specific-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - name: custom-request-header - value: John Doe - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - "www.example.com" - rules: - - backendRefs: - - group: "" - kind: Service - name: backend - port: 3000 - weight: 1 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: jwt-example - type: ExtensionRef - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-specific-user - matches: - - path: - type: PathPrefix - value: /foo -``` - - -## Multiple RateLimitFilters, rules and clientSelectors -* Users can create multiple `RateLimitFilter`s and apply it to the same `HTTPRoute`. In such a case each -`RateLimitFilter` will be applied to the route and matched (and limited) in a mutually exclusive way, independent of each other. -* Rate limits are applied for each `RateLimitFilter` `rule` when ALL the conditions under `clientSelectors` hold true. - -Here's an example highlighting this - - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-all-safeguard-app -spec: - type: Global - global: - rules: - - limit: - requests: 100 - unit: Second ---- - -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-per-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - type: Distinct - name: x-user-id - limit: - requests: 1000 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-per-user - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-all-safeguard-app - backendRefs: - - name: backend - port: 3000 -``` - -* The user has created two `RateLimitFilter`s and has attached it to a `HTTPRoute` - one(`ratelimit-all-safeguard-app`) to -ensure that the backend does not get overwhelmed with requests, any excess requests are rate limited irrespective of -the attributes within the traffic flow, and another(`ratelimit-per-user`) to rate limit each distinct user client -who can be differentiated using the `x-user-id` header, to ensure that each client does not make exessive requests to the backend. -* If user `baz` (identified with the header and value of `x-user-id: baz`) sends 90 requests within the first second, and -user `bar` sends 11 more requests during that same interval of 1 second, and user `bar` sends the 101th request within that second, -the rule defined in `ratelimit-all-safeguard-app` gets activated and Envoy Gateway will ratelimit the request sent by `bar` (and any other -request sent within that 1 second). After 1 second, the rate limit counter associated with the `ratelimit-all-safeguard-app` rule -is reset and again evaluated. -* If user `bar` also ends up sending 90 more requests within the hour, summing up `bar`'s total request count to 101, the rate limit rule -defined within `ratelimit-per-user` will get activated, and `bar`'s requests will be rate limited again until the hour interval ends. -* Within the same above hour, if `baz` sends 991 more requests, summing up `baz`'s total request count to 1001, the rate limit rule defined -within `ratelimit-per-user` will get activated for `baz`, and `baz`'s requests will also be rate limited until the hour interval ends. - -## Design Decisions - -* The initial design uses an Extension filter to apply the Rate Limit functionality on a specific [HTTPRoute][]. -This was preferred over the [PolicyAttachment][] extension mechanism, because it is unclear whether Rate Limit -will be required to be enforced or overridden by the platform administrator or not. -* The RateLimitFilter can only be applied as a filter to a [HTTPRouteRule[], applying it across all backends within a [HTTPRoute][] -and cannot be applied a filter within a [HTTPBackendRef][] for a specific backend. -* The [HTTPRoute][] API has a [matches][] field within each [rule][] to select a specific traffic flow to be routed to -the destination backend. The RateLimitFilter API that can be attached to an HTTPRoute via an [extensionRef][] filter, -also has a `clientSelectors` field within each `rule` to select attributes within the traffic flow to rate limit specific clients. -The two levels of selectors/matches allow for flexibility and aim to hold match information specific to its use, allowing the author/owner -of each configuration to be different. It also allows the `clientSelectors` field within the RateLimitFilter to be enhanced with other matchable -attribute such as [IP subnet][] in the future that are not relevant in the [HTTPRoute][] API. - -## Implementation Details - -### Global Rate limiting - -* [Global rate limiting][] in Envoy Proxy can be achieved using the following - - * [Actions][] can be conifgured per [xDS Route][]. - * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] - defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined - using a [rate limit filter][]. - * Based on information received by the rate limit service and its programmed configuration, a decision is computed, whether to rate limit - the HTTP Request or not, and is sent back to Envoy, which enforces this decision on the data plane. -* Envoy Gateway will leverage this Envoy Proxy feature by - - * Translating the user facing RateLimitFilter API into Rate limit [Actions][] as well as Rate limit service configuration to implement - the desired API intent. - * Envoy Gateway will use the existing [reference implementation][] of the rate limit service. - * The Infrastructure administrator will need to enable the rate limit service using new settings that will be defined in the [EnvoyGateway][] config API. - * The xDS IR will be enhanced to hold the user facing rate limit intent. - * The xDS Translator will be enhanced to translate the rate limit field within the xDS IR into Rate limit [Actions][] as well as instantiate the [rate limit filter][]. - * A new runner called `rate-limit` will be added that subscribes to the xDS IR messages and translates it into a new Rate Limit Infra IR which contains - the [rate limit service configuration][] as well as other information needed to deploy the rate limit service. - * The infrastructure service will be enhanced to subscribe to the Rate Limit Infra IR and deploy a provider specific rate limit service runnable entity. - * A Status field within the RateLimitFilter API will be added to reflect whether the specific configuration was programmed correctly in these multiple locations or not. - -[PolicyAttachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/ -[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute -[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPBackendRef -[matches]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch -[rule]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch -[extensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType -[IP subnet]: https://en.wikipedia.org/wiki/Subnetwork -[Actions]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit-action -[descriptors]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter.html?highlight=descriptor#example-1 -[Global rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting -[xDS Route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-routeaction -[rate limit filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit -[rate limit service]: https://www.envoyproxy.io/docs/envoy/latest/configuration/other_features/rate_limit#config-rate-limit-service -[reference implementation]: https://github.com/envoyproxy/ratelimit -[EnvoyGateway]: https://github.com/envoyproxy/gateway/blob/main/api/config/v1alpha1/envoygateway_types.go -[rate limit service configuration]: https://github.com/envoyproxy/ratelimit#configuration diff --git a/docs/latest/design/request-authentication.md b/docs/latest/design/request-authentication.md deleted file mode 100644 index 7f75fa07291..00000000000 --- a/docs/latest/design/request-authentication.md +++ /dev/null @@ -1,513 +0,0 @@ -# Request Authentication Design - -## Overview - -[Issue 336][] specifies the need for exposing a user-facing API to configure request authentication. Request -authentication is defined as an authentication mechanism to be enforced by Envoy on a per-request basis. A connection -will be rejected if it contains invalid authentication information, based on the `AuthenticationFilter` API type -proposed in this design document. - -Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and -implementation-specific API [support levels][] for implementors such as Envoy Gateway to expose features. Since -implementing request authentication is not covered by `Core` or `Extended` APIs, an `Implementation-specific` API will -be created for this purpose. - -## Goals - -* Define an API for configuring request authentication. -* Implement [JWT] as the first supported authentication type. -* Allow users that manage routes, e.g. [HTTPRoute][], to authenticate matching requests before forwarding to a backend - service. -* Support HTTPRoutes as an Authentication API referent. HTTPRoute provides multiple [extension points][]. The - [HTTPRouteFilter][] is the extension point supported by the Authentication API. - -## Non-Goals - -* Allow infrastructure administrators to override or establish default authentication policies. -* Support referents other than HTTPRoute. -* Support Gateway API extension points other than HTTPRouteFilter. - -## Use-Cases - -These use-cases are presented as an aid for how users may attempt to utilize the outputs of the design. They are not an -exhaustive list of features for authentication support in Envoy Gateway. - -As a Service Producer, I need the ability to: -* Authenticate a request before forwarding it to a backend service. -* Have different authentication mechanisms per route rule. -* Choose from different authentication mechanisms supported by Envoy, e.g. OIDC. - -### Authentication API Type - -The Authentication API type defines authentication configuration for authenticating requests through managed Envoy -proxies. - -```go -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - -) - -type AuthenticationFilter struct { - metav1.TypeMeta - metav1.ObjectMeta - - // Spec defines the desired state of the Authentication type. - Spec AuthenticationFilterSpec - - // Note: The status sub-resource has been excluded but may be added in the future. -} - -// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. -// +union -type AuthenticationFilterSpec struct { - // Type defines the type of authentication provider to use. Supported provider types are: - // - // * JWT: A provider that uses JSON Web Token (JWT) for authenticating requests. - // - // +unionDiscriminator - Type AuthenticationFilterType - - // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple - // jwtProviders are specified, the JWT is considered valid if any of the providers - // successfully validate the JWT. - JwtProviders []JwtAuthenticationFilterProvider -} - -... -``` - -Refer to [PR 773][] for the detailed AuthenticationFilter API spec. - -The status subresource is not included in the AuthenticationFilter API. Status will be surfaced by an HTTPRoute that -references an AuthenticationFilter. For example, an HTTPRoute will surface the `ResolvedRefs=False` status condition if it -references an AuthenticationFilter that does not exist. It may be beneficial to add AuthenticationFilter status fields in the future -based on defined use-cases. For example, a remote [JWKS][] can be validated based on the specified URI and have an -appropriate status condition surfaced. - -#### AuthenticationFilter Example - -The following is an AuthenticationFilter example with one JWT authentication provider: - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example -spec: - type: JWT - jwtProviders: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json - -``` - -__Note:__ `type` is a union type, allowing only one of any supported provider type such as `jwtProviders` to be -specified. - -The following is an example HTTPRoute configured to use the above JWT authentication provider: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example - backendRefs: - - name: backend - port: 3000 -``` - -Requests for `www.example.com/foo` will be authenticated using the referenced JWT provider before being forwarded to the -backend service named "backend". - -## Implementation Details - -The JWT authentication type is translated to an Envoy [JWT authentication filter][] and a cluster is created for each -remote [JWKS][]. The following examples provide additional details on how Gateway API and AuthenticationFilter resources are -translated into Envoy configuration. - -### Example 1: One Route with One JWT Provider - -The following cluster is created from the above HTTPRoute and AuthenticationFilter: - -```yaml -dynamic_clusters: - - name: foo.com|443 - load_assignment: - cluster_name: foo.com|443 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: foo.com - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: foo.com - common_tls_context: - validation_context: - match_subject_alt_names: - - exact: "*.foo.com" - trusted_ca: - filename: /etc/ssl/certs/ca-certificates.crt -``` - -A JWT authentication HTTP filter is added to the HTTP Connection Manager. For example: - -```yaml -dynamic_resources: - dynamic_listeners: - - name: example_listener - address: - socket_address: - address: 1.2.3.4 - port_value: 80 - filter_chains: - - filters: - - name: envoy.http_connection_manager - http_filters: - - name: envoy.filters.http.jwt_authn - typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication -``` - -This JWT authentication HTTP filter contains two fields: -* The `providers` field specifies how a JWT should be verified, such as where to extract the token, where to fetch the - public key ([JWKS][]) and where to output its payload. This field is built from the source resource `namespace-name`, and - the JWT provider name of an AuthenticationFilter. -* The `rules` field specifies matching rules and their requirements. If a request matches a rule, its requirement - applies. The requirement specifies which JWT providers should be used. This field is built from a HTTPRoute - `matches` rule that references the AuthenticationFilter. When a referenced Authentication specifies multiple - `jwtProviders`, the JWT is considered valid if __any__ of the providers successfully validate the JWT. - -The following JWT authentication HTTP filter `providers` configuration is created from the above AuthenticationFilter. - -```yaml -providers: - example: - issuer: https://www.example.com - audiences: - - foo.com - remote_jwks: - http_uri: - uri: https://foo.com/jwt/public-key/jwks.json - cluster: example_jwks_cluster - timeout: 1s -``` - -The following JWT authentication HTTP filter `rules` configuration is created from the above HTTPRoute. - -```yaml -rules: - - match: - prefix: /foo - requires: - provider_name: default-example-example -``` - -### Example 2: Two HTTPRoutes with Different AuthenticationFilters - -The following example contains: -* Two HTTPRoutes with different hostnames. -* Each HTTPRoute references a different AuthenticationFilter. -* Each AuthenticationFilter contains a different JWT provider. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example1 -spec: - type: JWT - jwtProviders: - - name: example1 - issuer: https://www.example1.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example2 -spec: - type: JWT - jwtProviders: - - name: example2 - issuer: https://www.example2.com - audiences: - - bar.com - remoteJwks: - uri: https://bar.com/jwt/public-key/jwks.json ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example1 -spec: - hostnames: - - www.example1.com - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: eg - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example1 - backendRefs: - - name: backend - port: 3000 ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example2 -spec: - hostnames: - - www.example2.com - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: eg - rules: - - matches: - - path: - type: PathPrefix - value: /bar - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example2 - backendRefs: - - name: backend2 - port: 3000 -``` - -The following xDS configuration is created from the above example resources: - -```yaml -configs: -... -dynamic_listeners: - - name: default-eg-http - ... - default_filter_chain: - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - '@type': >- - type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: http - rds: - config_source: - ... - route_config_name: default-eg-http - http_filters: - - name: envoy.filters.http.jwt_authn - typed_config: - '@type': >- - type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication - providers: - default-example1-example1: - issuer: https://www.example1.com - audiences: - - foo.com - remote_jwks: - http_uri: - uri: https://foo.com/jwt/public-key/jwks.json - cluster: default-example1-example1-jwt - default-example2-example2: - issuer: https://www.example2.com - audiences: - - bar.com - remote_jwks: - http_uri: - uri: https://bar.com/jwt/public-key/jwks.json - cluster: default-example2-example2-jwt - rules: - - match: - exact: /foo - requires: - provider_name: default-example1-example1 - - match: - exact: /bar - requires: - provider_name: default-example2-example2 - - name: envoy.filters.http.router - typed_config: - '@type': >- - type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -dynamic_route_configs: - - route_config: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - name: default-eg-http - virtual_hosts: - - name: default-eg-http - domains: - - '*' - routes: - - match: - prefix: /foo - headers: - - name: ':authority' - string_match: - exact: www.example1.com - route: - cluster: default-backend-rule-0-match-0-www.example1.com - - match: - prefix: /bar - headers: - - name: ':authority' - string_match: - exact: www.example2.com - route: - cluster: default-backend2-rule-0-match-0-www.example2.com -dynamic_active_clusters: - - cluster: - name: default-backend-rule-0-match-0-www.example.com - ... - endpoints: - - locality: {} - lb_endpoints: - - endpoint: - address: - socket_address: - address: $BACKEND_SERVICE1_IP - port_value: 3000 - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - name: default-backend-rule-1-match-0-www.example.com - ... - endpoints: - - locality: {} - lb_endpoints: - - endpoint: - address: - socket_address: - address: $BACKEND_SERVICE2_IP - port_value: 3000 -... -``` - -__Note:__ The JWT provider cluster and route is omitted from the above example for brevity. - -### Implementation Outline - -* Update the Kubernetes provider to get/watch AuthenticationFilter resources that are referenced by managed HTTPRoutes. - Add the referenced AuthenticationFilter object to the resource map and publish it. -* Update the resource translator to include the AuthenticationFilter API in HTTPRoute processing. -* Update the xDS translator to translate an AuthenticationFilter into xDS resources. The translator should perform the - following: - * Convert a list of JWT rules from the xds IR into an Envoy JWT filter config. - * Create a JWT authentication HTTP filter. - * Build the HTTP Connection Manager (HCM) HTTP filters. - * Build the HCM. - * When building the Listener, create an HCM for each filter-chain. - -## Adding Authentication Types - -Additional authentication types can be added in the future through the `AuthenticationFilterType` API. For -example, to add the `Foo` authentication type: - -Define the `Foo` authentication provider: - -```go -package v1alpha1 - -// FooAuthenticationFilterProvider defines the "Foo" authentication filter provider type. -type FooAuthenticationFilterProvider struct { - // TODO: Define fields of the Foo authentication filter provider type. -} -``` - -Add the `FooAuthenticationFilterProvider` type to `AuthenticationFilterSpec`: - -```go -package v1alpha1 - -type AuthenticationFilterSpec struct { - ... - - // Foo defines the Foo authentication type. For additional - // details, see: - // - // - // - // +optional - Foo *FooAuthenticationFilterProvider -} -``` - -Lastly, add the type to the `AuthenticationType` enum: - -```go -// AuthenticationType is a type of authentication provider. -// +kubebuilder:validation:Enum=JWT,FOO -type AuthenticationFilterType string - -const ( - // JwtAuthenticationProviderType is the JWT authentication provider type. - FooAuthenticationFilterProviderType AuthenticationFilterType = "FOO" -) -``` - -The AuthenticationFilter API should support additional authentication types in the future, for example: -- OAuth2 -- OIDC - -## Outstanding Questions - -- If Envoy Gateway owns the AuthenticationFilter API, is an xDS IR equivalent needed? -- Should local [JWKS][] be implemented before remote [JWKS][]? -- How should Envoy obtain the trusted CA for a remote [JWKS][]? -- Should HTTPS be the only supported scheme for remote [JWKS][]? -- Should OR'ing JWT providers be supported? -- Should Authentication provide status? -- Are the API field validation rules acceptable? - -[Issue 336]: https://github.com/envoyproxy/gateway/issues/336 -[Gateway API]: https://gateway-api.sigs.k8s.io/ -[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels -[JWT]: https://jwt.io/ -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[extension points]: https://gateway-api.sigs.k8s.io/concepts/api-overview/?h=extension#extension-points -[HTTPRouteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter -[JWKS]: https://www.rfc-editor.org/rfc/rfc7517 -[JWT authentication filter]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn -[PR 773]: https://github.com/envoyproxy/gateway/pull/733 diff --git a/docs/latest/design/roadmap.md b/docs/latest/design/roadmap.md deleted file mode 100644 index 65f88de1214..00000000000 --- a/docs/latest/design/roadmap.md +++ /dev/null @@ -1,88 +0,0 @@ -# Roadmap - -This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of -the project. - -## Contributing to the Roadmap - -- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use - case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update - this document accordingly. -- To help with an existing roadmap item, comment on or assign yourself to the associated issue. -- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A - maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be - discussed in an issue or a community meeting before implementing it. - -If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. -Look for issues with the `help wanted` label to get started. - -## Details - -Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific -roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by -contributing to the project. - -`Last Updated: April 2023` - -### [v0.2.0][v0.2.0]: Establish a Solid Foundation - -- Complete the core Envoy Gateway implementation- [Issue #60][60]. -- Establish initial testing, e2e, integration, etc- [Issue #64][64]. -- Establish user and developer project documentation- [Issue #17][17]. -- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. -- Setup a CI/CD pipeline- [Issue #63][63]. - -### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms - -- Support extended Gateway API fields [Issue #707][707]. -- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. -- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. -- Rate Limiting [Issue #670][670]. -- Authentication [Issue #336][336]. - -### [v0.4.0][v0.4.0]: Customizing Envoy Gateway - -- Extending Envoy Gateway control plane [Issue #20][20] -- Helm based installation for Envoy Gateway [Issue #650][650] -- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] -- Configuring xDS Bootstrap [Issue #31][31] - -### [v0.5.0][v0.5.0]: Observability and Scale - -- Observability for control plane and data plane [Issue #701][701]. -- Compute and document Envoy Gateway performance [Issue #1365][1365]. -- Allow users to configure xDS Resources [Issue #24][24]. - -### [v0.6.0][v0.6.0]: Preparation for GA - -- Envoy Gateway meets readiness criteria [Issue #1160][1160]. - -[issue]: https://github.com/envoyproxy/gateway/issues -[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing -[pr]: https://github.com/envoyproxy/gateway/compare -[milestones]: https://github.com/envoyproxy/gateway/milestones -[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 -[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 -[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 -[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 -[v0.6.0]: https://github.com/envoyproxy/gateway/milestone/15 -[17]: https://github.com/envoyproxy/gateway/issues/17 -[20]: https://github.com/envoyproxy/gateway/issues/20 -[24]: https://github.com/envoyproxy/gateway/issues/24 -[31]: https://github.com/envoyproxy/gateway/issues/31 -[60]: https://github.com/envoyproxy/gateway/issues/60 -[63]: https://github.com/envoyproxy/gateway/issues/63 -[64]: https://github.com/envoyproxy/gateway/issues/64 -[65]: https://github.com/envoyproxy/gateway/issues/65 -[336]: https://github.com/envoyproxy/gateway/issues/336 -[641]: https://github.com/envoyproxy/gateway/issues/641 -[642]: https://github.com/envoyproxy/gateway/issues/642 -[648]: https://github.com/envoyproxy/gateway/issues/648 -[650]: https://github.com/envoyproxy/gateway/issues/650 -[643]: https://github.com/envoyproxy/gateway/issues/643 -[670]: https://github.com/envoyproxy/gateway/issues/670 -[675]: https://github.com/envoyproxy/gateway/issues/675 -[701]: https://github.com/envoyproxy/gateway/issues/701 -[707]: https://github.com/envoyproxy/gateway/issues/707 -[1160]: https://github.com/envoyproxy/gateway/issues/1160 -[1365]: https://github.com/envoyproxy/gateway/issues/1365 diff --git a/docs/latest/design/system-design.md b/docs/latest/design/system-design.md deleted file mode 100644 index 86114be37fa..00000000000 --- a/docs/latest/design/system-design.md +++ /dev/null @@ -1,171 +0,0 @@ -# System Design - -## Goals - -* Define the system components needed to satisfy the requirements of Envoy Gateway. - -## Non-Goals - -* Create a detailed design and interface specification for each system component. - -## Terminology - -* Control Plane- A collection of inter-related software components for providing application gateway and routing - functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. - These services are detailed in the [components](#components) section. -* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. - -## Architecture - -![Architecture](../images/architecture.png) - -## Configuration - -Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through -Kubernetes resources, primarily [Gateway API][gw_api] objects. - -### Static Configuration - -Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, -configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the -configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. - -### Dynamic Configuration - -Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using -reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is -defined as Kubernetes resources that provide the following services: - -* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is - expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be - referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, - e.g. expose Envoy network endpoints using a `ClusterIP` service instead of a `LoadBalancer` service. -* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP - requests for "www.example.com" to a backend service running a web server. This configuration is expressed through - [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. - Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] - reference. - -## Components - -Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described -in the [Watching Components Design][wcd]. - -### Provider - -A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve -services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap -via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A -provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). - -#### Kubernetes Provider - -* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the - [dynamic configuration](#dynamic-configuration). -* Manages the data plane through Kubernetes API CRUD operations. -* Uses Kubernetes for Service discovery. -* Uses etcd (via Kubernetes API) to persist data. - -#### File Provider - -* Uses a file watcher to watch files in a directory that define the data plane configuration. -* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. -* Uses the host's DNS for Service discovery. -* If needed, the local filesystem is used to persist data. - -### Resource Watcher - -The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The -mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes -provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator -as output. - -### Resource Translator - -The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate -Representation (IR). It is responsible for: - -* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. -* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. - -__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. - -### Intermediate Representation (IR) - -The Intermediate Representation defines internal data models that external resources are translated into. This allows -Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR -used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. - -* Infra IR- Used as the internal definition of the managed data plane infrastructure. -* xDS IR- Used as the internal definition of the managed data plane xDS configuration. - -### xDS Translator - -The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. - -### xDS Server - -The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server -Protocol and is responsible for using xDS to configure the data plane. - -### Infra Manager - -The Infra Manager is a provider-specific component responsible for managing the following infrastructure: - -* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, - Service, etc. resources to run Envoy in a Kubernetes cluster. -* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require - external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning - and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to - users through the [Custom Route Filters][crf] extension. - -The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. - -## Design Decisions - -* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with - `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy - Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. - `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy - infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. -* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. - * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. - * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners - in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are - met: - * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies - either the “HTTPS” or “TLS” Protocol. - * Each Listener within the group specifies a unique "Hostname". - * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no - other Listener matches. - * Envoy Gateway does __not__ merge listeners across multiple Gateways. -* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. - * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. -* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. - * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. -* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. - -The draft for this document is [here][draft_design]. - -[gw_api]: https://gateway-api.sigs.k8s.io -[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass -[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway -[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute -[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute -[go_cp]: https://github.com/envoyproxy/go-control-plane -[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting -[rls]: https://github.com/envoyproxy/ratelimit -[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit -[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional -[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts -[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners -[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route -[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional -[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster -[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference -[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ -[ wcd ]: ./watching.md -[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 -[roadmap]: roadmap.md diff --git a/docs/latest/design/tcp-udp-design.md b/docs/latest/design/tcp-udp-design.md deleted file mode 100644 index 276221b897b..00000000000 --- a/docs/latest/design/tcp-udp-design.md +++ /dev/null @@ -1,47 +0,0 @@ -# TCP and UDP Proxy Design - -Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP -and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the -design decision. - -Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) - and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) -, so ideally, Envoy Gateway should also be able to work in these two modes: - -## Non-transparent Proxy Mode -For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the -TCP traffic from the downstream to the upstream. - -For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when -proxying the UDP datagrams to the upstream. - -In this mode, the upstream will see Envoy's IP address and port. - -## Transparent Proxy Mode -For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies -the TCP traffic from the downstream to the upstream. - -For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address -when proxying the UDP datagrams to the upstream. - -In this mode, the upstream will see the original downstream IP address and Envoy's mac address. - -Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward -the port number. - -## The Implications of Transparent Proxy Mode - -### Escalated Privilege -Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated -CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. - -### Routing -The upstream can see the original source IP, but the original port number won't be passed, so the return -traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back -to the right port number of the downstream, which requires routing at the upstream side to be set up. -In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. - -## The Design Decision (For Now) - -The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and -port of the deployed Envoy instance instead of the client. diff --git a/docs/latest/design/tracing.md b/docs/latest/design/tracing.md deleted file mode 100644 index 4b85cff2287..00000000000 --- a/docs/latest/design/tracing.md +++ /dev/null @@ -1,159 +0,0 @@ -# Observability: Accesslog - -## Overview - -Envoy supports extensible tracing to different sinks, Zipkin, OpenTelemetry etc. Overview of Envoy tracing can be found [here](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/observability/tracing). - -Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementors such as Envoy Gateway to expose features. Since tracing is not covered by `Core` or `Extended` APIs, EG should provide an easy to config tracing per `EnvoyProxy`. - -Only OpenTelemetry sink can be configured currently, you can use [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) to export to other tracing backends. - -## Goals - -- Support send tracing to `OpenTelemetry` backend -- Support configurable sampling rate -- Support propagate tag from `Literal`, `Environment` and `Request Header` - -## Non-Goals - -- Support other tracing backend, e.g. `Zipkin`, `Jaeger` - -## Use-Cases - -- Configure accesslog for a `EnvoyProxy` to `File` - -### ProxyAccessLog API Type - -```golang mdox-exec="sed '1,7d' api/config/v1alpha1/tracing_types.go" -type ProxyTracing struct { - // SamplingRate controls the rate at which traffic will be - // selected for tracing if no prior sampling decision has been made. - // Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:Maximum=100 - // +kubebuilder:default=100 - // +optional - SamplingRate *uint32 `json:"samplingRate,omitempty"` - // CustomTags defines the custom tags to add to each span. - // If provider is kubernetes, pod name and namespace are added by default. - CustomTags map[string]CustomTag `json:"customTags,omitempty"` - // Provider defines the tracing provider. - // Only OpenTelemetry is supported currently. - Provider TracingProvider `json:"provider"` -} - -type TracingProviderType string - -const ( - TracingProviderTypeOpenTelemetry TracingProviderType = "OpenTelemetry" -) - -type TracingProvider struct { - // Type defines the tracing provider type. - // EG currently only supports OpenTelemetry. - // +kubebuilder:validation:Enum=OpenTelemetry - // +kubebuilder:default=OpenTelemetry - Type TracingProviderType `json:"type"` - // Host define the provider service hostname. - Host string `json:"host"` - // Port defines the port the provider service is exposed on. - // - // +optional - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:default=4317 - Port int32 `json:"port,omitempty"` -} - -type CustomTagType string - -const ( - // CustomTagTypeLiteral adds hard-coded value to each span. - CustomTagTypeLiteral CustomTagType = "Literal" - // CustomTagTypeEnvironment adds value from environment variable to each span. - CustomTagTypeEnvironment CustomTagType = "Environment" - // CustomTagTypeRequestHeader adds value from request header to each span. - CustomTagTypeRequestHeader CustomTagType = "RequestHeader" -) - -type CustomTag struct { - // Type defines the type of custom tag. - // +kubebuilder:validation:Enum=Literal;Environment;RequestHeader - // +unionDiscriminator - // +kubebuilder:default=Literal - Type CustomTagType `json:"type"` - // Literal adds hard-coded value to each span. - // It's required when the type is "Literal". - Literal *LiteralCustomTag `json:"literal,omitempty"` - // Environment adds value from environment variable to each span. - // It's required when the type is "Environment". - Environment *EnvironmentCustomTag `json:"environment,omitempty"` - // RequestHeader adds value from request header to each span. - // It's required when the type is "RequestHeader". - RequestHeader *RequestHeaderCustomTag `json:"requestHeader,omitempty"` - - // TODO: add support for Metadata tags in the future. - // EG currently doesn't support metadata for route or cluster. -} - -// LiteralCustomTag adds hard-coded value to each span. -type LiteralCustomTag struct { - // Value defines the hard-coded value to add to each span. - Value string `json:"value"` -} - -// EnvironmentCustomTag adds value from environment variable to each span. -type EnvironmentCustomTag struct { - // Name defines the name of the environment variable which to extract the value from. - Name string `json:"name"` - // DefaultValue defines the default value to use if the environment variable is not set. - // +optional - DefaultValue *string `json:"defaultValue,omitempty"` -} - -// RequestHeaderCustomTag adds value from request header to each span. -type RequestHeaderCustomTag struct { - // Name defines the name of the request header which to extract the value from. - Name string `json:"name"` - // DefaultValue defines the default value to use if the request header is not set. - // +optional - DefaultValue *string `json:"defaultValue,omitempty"` -} -``` - -### Example - -1. The following is an example to config tracing. - -```yaml mdox-exec="sed '1,12d' examples/kubernetes/tracing/default.yaml" -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: tracing - namespace: envoy-gateway-system -spec: - telemetry: - tracing: - # sample 100% of requests - samplingRate: 100 - provider: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 - customTags: - # This is an example of using a literal as a tag value - key1: - type: Literal - literal: - value: "val1" - # This is an example of using an environment variable as a tag value - env1: - type: Environment - environment: - name: ENV1 - defaultValue: "-" - # This is an example of using a header value as a tag value - header1: - type: RequestHeader - requestHeader: - name: X-Header-1 - defaultValue: "-" -``` diff --git a/docs/latest/design/watching.md b/docs/latest/design/watching.md deleted file mode 100644 index b8477a30e2d..00000000000 --- a/docs/latest/design/watching.md +++ /dev/null @@ -1,117 +0,0 @@ -# Watching Components Design - -Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch -external resources, and "publish" what they see for other components to consume; others watch what another publishes and -act on it (such as the resource translator watches what the providers publish, and then publishes its own results that -are watched by another component). Some of these internally published results are consumed by multiple components. - -To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the -standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub -pattern. - -## Pub - -Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" -tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if -we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by -the IR translator: - - ```go - type ResourceTable struct { - // gateway classes are cluster-scoped; no namespace - GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] - - // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. - Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] - - HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] - } - ``` - -The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; -updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` -method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher -doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just -`.Store` its data and `watchable` will do the right thing. - -## Sub - -Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or -`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: - - ```go - func(ctx context.Context) error { - for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { - fullState := irInput{ - GatewayClasses: k8sTable.GatewayClasses.LoadAll(), - Gateways: k8sTable.Gateways.LoadAll(), - HTTPRoutes: snapshot.State, - } - translate(irInput) - } - } - ``` - -Or, to watch multiple maps in the same loop: - - ```go - func worker(ctx context.Context) error { - classCh := k8sTable.GatewayClasses.Subscribe(ctx) - gwCh := k8sTable.Gateways.Subscribe(ctx) - routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) - for ctx.Err() == nil { - var arg irInput - select { - case snapshot := <-classCh: - arg.GatewayClasses = snapshot.State - case snapshot := <-gwCh: - arg.Gateways = snapshot.State - case snapshot := <-routeCh: - arg.Routes = snapshot.State - } - if arg.GateWayClasses == nil { - arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() - } - if arg.GateWays == nil { - arg.Gateways = k8sTable.GateWays.LoadAll() - } - if arg.HTTPRoutes == nil { - arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() - } - translate(irInput) - } - } - ``` - -From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; -but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a -handy way to know when to run, `.Load` and friends can be used without subscribing. - -There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but -it's probably wise to just have one publisher for each map. - -The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when -`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple -mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in -to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of -each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need -to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. - -If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` -entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you -must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to -help with this. - -## Other Notes - -The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the -map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb -`chan`. - -A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types -be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and -so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can -generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate -methods for. - -[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/docs/latest/design_docs.rst b/docs/latest/design_docs.rst deleted file mode 100644 index a0b82b578f8..00000000000 --- a/docs/latest/design_docs.rst +++ /dev/null @@ -1,20 +0,0 @@ -Design Docs -=========== - -Learn about the internal details of Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - design/system-design - design/gatewayapi-translator - design/watching - design/config-api - design/tcp-udp-design - design/egctl - design/rate-limit - design/request-authentication - design/bootstrap - design/extending-envoy-gateway - design/local-envoy-gateway - design/accesslog diff --git a/docs/latest/dev/CODEOWNERS.md b/docs/latest/dev/CODEOWNERS.md deleted file mode 100644 index d4229b6b23f..00000000000 --- a/docs/latest/dev/CODEOWNERS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Maintainers - -## The following maintainers, listed in alphabetical order, own everything - -- @AliceProxy -- @arkodg -- @skriss -- @Xunzhuo -- @youngnick -- @zirain - -## Emeritus Maintainers - -- @danehans -- @alexgervais diff --git a/docs/latest/dev/CODE_OF_CONDUCT.md b/docs/latest/dev/CODE_OF_CONDUCT.md deleted file mode 100644 index a0a295770f3..00000000000 --- a/docs/latest/dev/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,3 +0,0 @@ -# Community Code of Conduct - -Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/docs/latest/dev/CONTRIBUTING.md b/docs/latest/dev/CONTRIBUTING.md deleted file mode 100644 index 04d95bb675d..00000000000 --- a/docs/latest/dev/CONTRIBUTING.md +++ /dev/null @@ -1,186 +0,0 @@ -# Contributing - -We welcome contributions from the community. Please carefully review the [project goals](GOALS.md) -and following guidelines to streamline your contributions. - -## Communication - -* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no - one else is working on it and ask you to open a GitHub issue. -* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or - changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to - agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process - for major features is also important so that [affiliations with commit access](CODEOWNERS.md) can - come to agreement on the design. If it's appropriate to write a design document, the document must - be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable - location. -* Small patches and bug fixes don't need prior communication. - -## Inclusivity - -The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere -to the following guidelines for all code, APIs, and documentation: - -* The following words and phrases are not allowed: - * *Whitelist*: use allowlist instead. - * *Blacklist*: use denylist or blocklist instead. - * *Master*: use primary instead. - * *Slave*: use secondary or replica instead. -* Documentation should be written in an inclusive style. The [Google developer - documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent - reference on this topic. -* The above policy is not considered definitive and may be amended in the future as industry best - practices evolve. Additional comments on this topic may be provided by maintainers during code - review. - -## Submitting a PR - -* Fork the repo. -* Hack -* DCO sign-off each commit. This can be done with `git commit -s`. -* Submit your PR. -* Tests will automatically run for you. -* We will **not** merge any PR that is not passing tests. -* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage - build. If your PR cannot have 100% coverage for some reason please clearly explain why when you - open it. -* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/docs) folder of the repo as - well as the [changelog](../releases). -* All code comments and documentation are expected to have proper English grammar and punctuation. - If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try - to find some help but there are no guarantees. -* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary - and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. - Examples: - * "docs: fix grammar error" - * "feat(translator): add new feature" - * "fix: fix xx bug" - * "chore: change ci & build tools etc" -* Your PR commit message will be used as the commit message when your PR is merged. You should - update this field if your PR diverges during review. -* Your PR description should have details on what the PR does. If it fixes an existing issue it - should end with "Fixes #XXX". -* If your PR is co-authored or based on an earlier PR from another contributor, - please attribute them with `Co-authored-by: name `. See - GitHub's [multiple author - guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) - for further details. -* When all tests are passing and all other conditions described herein are satisfied, a maintainer - will be assigned to review and merge the PR. -* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits - are new commits and/or merges. We squash and merge so the number of commits you have in the PR - doesn't matter. -* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. - We reserve the right to close PRs that are not making progress. This is generally defined as no - changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. - Closing stale PRs helps us to keep on top of all the work currently in flight. - -## Maintainer PR Review Policy - -* See [CODEOWNERS.md](CODEOWNERS.md) for the current list of maintainers. -* A maintainer representing a different affiliation from the PR owner is required to review and - approve the PR. -* When the project matures, it is expected that a "domain expert" for the code the PR touches should - review the PR. This person does not require commit access, just domain knowledge. -* The above rules may be waived for PRs which only update docs or comments, or trivial changes to - tests and tools (where trivial is decided by the maintainer in question). -* If there is a question on who should review a PR please discuss in Slack. -* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. -* Please make sure that the PR title, commit message, and description are updated if the PR changes - significantly during review. -* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge - title with the original title, and the commit body with every individual commit from the PR. - The maintainer doing the merge should make sure the title follows the guidelines above and should - overwrite the body with the original commit message from the PR (cleaning it up if necessary) - while preserving the PR author's final DCO sign-off. - -## Decision making - -This is a new and complex project, and we need to make a lot of decisions very quickly. -To this end, we've settled on this process for making (possibly contentious) decisions: - -* For decisions that need a record, we create an issue. -* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. -* Maintainers can cast binding votes on that comment by reacting or replying in another comment. -* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. -* Voting will be resolved by simple majority. -* In the event of deadlocks, the question will be put to steering instead. - -## DCO: Sign your work - -The sign-off is a simple line at the end of the explanation for the -patch, which certifies that you wrote it or otherwise have the right to -pass it on as an open-source patch. The rules are pretty simple: if you -can certify the below (from -[developercertificate.org](https://developercertificate.org/)): - -``` -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -using your real name (sorry, no pseudonyms or anonymous contributions.) - -You can add the sign-off when creating the git commit via `git commit -s`. - -If you want this to be automatic you can set up some aliases: - -```bash -git config --add alias.amend "commit -s --amend" -git config --add alias.c "commit -s" -``` - -## Fixing DCO - -If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best -practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) -the commit history to a single commit, append the DCO sign-off as described above, and [force -push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in -your history: - -```bash -git rebase -i HEAD^^ -(interactive squash + DCO append) -git push origin -f -``` - -Note, that in general rewriting history in this way is a hindrance to the review process and this -should only be done to correct a DCO mistake. diff --git a/docs/latest/dev/DOCS.md b/docs/latest/dev/DOCS.md deleted file mode 100644 index fb49b9d55dd..00000000000 --- a/docs/latest/dev/DOCS.md +++ /dev/null @@ -1,63 +0,0 @@ -# Working on the Envoy Gateway Docs - -The documentation for the Envoy Gateway lives in the `docs/` directory. Any -individual document can be written using either [reStructuredText] or [Markdown], -you can choose the format that you're most comfortable with when working on the -documentation. - -## Documentation Structure - -We supported the versioned Docs now, the directory name under docs represents -the version of docs. The root of the latest site is in `docs/latest/index.rst`. -This is probably where to start if you're trying to understand how things fit together. - -Note that the new contents should be added to `docs/latest` and will be cut off at -the next release. The contents under `docs/v0.2.0` are auto-generated, -and usually do not need to make changes to them, unless if you find the current release pages have -some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` -and `docs/v0.2.0`. - -It's important to note that a given document _must_ have a reference in some -`.. toctree::` section for the document to be reachable. Not everything needs -to be in `docs/index.rst`'s `toctree` though. - -You can access the website which represents the current release in default, -and you can access the website which contains the latest version changes in -[Here][latest-website] or at the footer of the pages. - -## Documentation Workflow - -To work with the docs, just edit reStructuredText or Markdown files in `docs`, -then run - -```bash -make docs -``` - -This will create `docs/html` with the built HTML pages. You can view the docs -either simply by pointing a web browser at the `file://` path to your -`docs/html`, or by firing up a static webserver from that directory, e.g. - -``` shell -make docs-serve -``` - -If you want to generate a new release version of the docs, like `v0.3.0`, then run - -```bash -make docs-release TAG=v0.3.0 -``` - -This will update the VERSION file at the project root, which records current release version, -and it will be used in the pages version context and binary version output. Also, this will generate -new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` -in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. - -## Publishing Docs - -Whenever docs are pushed to `main`, CI will publish the built docs to GitHub -Pages. For more details, see `.github/workflows/docs.yaml`. - -[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -[Markdown]: https://daringfireball.net/projects/markdown/syntax -[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/docs/latest/dev/GOALS.md b/docs/latest/dev/GOALS.md deleted file mode 120000 index f2174592394..00000000000 --- a/docs/latest/dev/GOALS.md +++ /dev/null @@ -1 +0,0 @@ -../../../GOALS.md \ No newline at end of file diff --git a/docs/latest/dev/README.md b/docs/latest/dev/README.md deleted file mode 100644 index cf25992e5b3..00000000000 --- a/docs/latest/dev/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# Developer Guide - -Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. - -## Prerequisites - -### go - -* Version: 1.20 -* Installation Guide: https://go.dev/doc/install - -### make - -* Recommended Version: 4.0 or later -* Installation Guide: https://www.gnu.org/software/make - -### docker - -* Optional when you want to build a Docker image or run `make` inside Docker. -* Recommended Version: 20.10.16 -* Installation Guide: https://docs.docker.com/engine/install - -### python3 - -* Need a `python3` program -* Must have a functioning `venv` module; this is part of the standard - library, but some distributions (such as Debian and Ubuntu) replace - it with a stub and require you to install a `python3-venv` package - separately. - -## Quickstart - -* Run `make help` to see all the available targets to build, test and run Envoy Gateway. - -### Building - -* Run `make build` to build all the binaries. -* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. -* Run `make build BINS="egctl"` to build the egctl binary. - -__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. - -### Testing - -* Run `make test` to run the golang tests. - -* Run `make testdata` to generate the golden YAML testdata files. - -### Running Linters - -* Run `make lint` to make sure your code passes all the linter checks. -__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). - -### Building and Pushing the Image - -* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. -* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. - -__Note:__ Replace `IMAGE` with your registry's image name. - -### Deploying Envoy Gateway for Test/Dev - -* Run `make create-cluster` to create a [Kind][] cluster. - -#### Option 1: Use the Latest [gateway-dev][] Image - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` - to use a different image tag. - -#### Option 2: Use a Custom Image - -* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. -* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. - -### Deploying Envoy Gateway in Kubernetes - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to - the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or - tag. -* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. - -__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. - -### Demo Setup - -* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource -(similar to steps outlined in the [Quickstart][] docs) and test the configuration. -* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. - -### Run Gateway API Conformance Tests - -The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the -Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run -`TAG=latest make run-conformance` to run the conformance tests. - -#### On a Linux Host - -* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] - image, and run Gateway API conformance tests. - -#### On a Mac Host - -Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following -workarounds to run conformance tests: - -* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run - `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image - to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to - uninstall Envoy Gateway. -* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. - -__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` -is unspecified, the short SHA of your current branch is used. - -### Debugging the Envoy Config - -An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port -(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. - -Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: - -```shell -export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward the admin interface port: - -```shell -kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 -``` - -Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. - -There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. - -### JWT Testing - -An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] -user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs -verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching -settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure -and is hosted in the repo. - -[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md -[make]: https://www.gnu.org/software/make/ -[Github Actions]: https://docs.github.com/en/actions -[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows -[Kind]: https://kind.sigs.k8s.io/ -[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ -[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ -[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ -[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags -[mac_connect]: https://github.com/chipmk/docker-mac-net-connect -[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface -[jwt]: https://tools.ietf.org/html/rfc7519 -[jwks]: https://tools.ietf.org/html/rfc7517 -[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html -[JWT Debugger]: https://jwt.io/ -[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/docs/latest/dev/releasing.md b/docs/latest/dev/releasing.md deleted file mode 100644 index 81625698cfa..00000000000 --- a/docs/latest/dev/releasing.md +++ /dev/null @@ -1,228 +0,0 @@ -# Release Process - -This document guides maintainers through the process of creating an Envoy Gateway release. - -- [Release Candidate](#release-candidate) -- [Minor Release](#minor-release) -- [Announce the Release](#announce-the-release) - -## Release Candidate - -The following steps should be used for creating a release candidate. - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export RELEASE_CANDIDATE_NUMBER=1 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. -3. Sign, commit, and push your changes to your fork. -4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and - the [Build and Test][] has successfully completed. -5. Create a new release branch from `main`. The release branch should be named - `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. Push the branch to the Envoy Gateway repo. - - ```shell - git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] - for additional details on updating the image tag. -8. Sign, commit, and push your changes to your fork. -9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not - proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. -10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' - ``` - -11. Push the tag to the Envoy Gateway repository. - - ```shell - git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} - ``` - -12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -13. Confirm that the [release workflow][] completed successfully. -14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -15. Confirm that the [release][] was created. -16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test - the quickstart steps using the release candidate by manually updating the links. -17. [Generate][] the GitHub changelog. -18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. -19. If you find any bugs in this process, please create an issue. - -### Setup cherry picker action - -After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. - -Configuration looks like following: - -```yaml - cherry_pick_release_v0_4: - runs-on: ubuntu-latest - name: Cherry pick into release-v0.4 - if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Cherry pick into release/v0.4 - uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 - with: - branch: release/v0.4 - title: "[release/v0.4] {old_title}" - body: "Cherry picking #{old_pull_request_id} onto release/v0.4" - labels: | - cherrypick/release-v0.4 - # put release manager here - reviewers: | - AliceProxy -``` - -Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. - -## Minor Release - -The following steps should be used for creating a minor release. - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. -- A release branch that has been cut from the corresponding release candidate. Refer to the - [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. - - 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release - notes should be an accumulation of the release candidate release notes and any changes since the release - candidate. - 2. Create a release announcement. Refer to [PR #635] as an example release announcement. - 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. - 4. Generate the versioned release docs: - - ``` shell - make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -3. Sign, commit, and push your changes to your fork. -4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged - and the [Build and Test][] has completed for your final PR. - -5. Checkout the release branch. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. If the tip of the release branch does not match the tip of `main`, perform the following: - - 1. Create a topic branch from the release branch. - 2. Cherry-pick the commits from `main` that differ from the release branch. - 3. Run tests locally, e.g. `make lint`. - 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. - 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. - 6. Do not proceed until the PR has merged and CI passes for the merged PR. - 7. If you are still on your topic branch, change to the release branch: - - ```shell - git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - - 8. Ensure your local release branch is up-to-date: - - ```shell - git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -7. Tag the head of your release branch with the release tag. For example: - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' - ``` - - __Note:__ The tag version differs from the release branch by including the `.0` patch version. - -8. Push the tag to the Envoy Gateway repository. - - ```shell - git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -10. Confirm that the [release workflow][] completed successfully. -11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -12. Confirm that the [release][] was created. -13. Confirm that the steps in the [Quickstart Guide][] work as expected. -14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: - - ```console - # Release Announcement - - Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] - (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. - ``` - -If you find any bugs in this process, please create an issue. - -## Announce the Release - -It's important that the world knows about the release. Use the following steps to announce the release. - -1. Set the release information in the Envoy Gateway Slack channel. For example: - - ```shell - Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -2. Send a message to the Envoy Gateway Slack channel. For example: - - ```shell - On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway - v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. - Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs - to start using Envoy Gateway. - ... - ``` - - Link to the GitHub release and release announcement page that highlights the release. - -[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes -[Pull Request]: https://github.com/envoyproxy/gateway/pulls -[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml -[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml -[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml -[image]: https://hub.docker.com/r/envoyproxy/gateway/tags -[release]: https://github.com/envoyproxy/gateway/releases -[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes -[PR #635]: https://github.com/envoyproxy/gateway/pull/635 -[PR #958]: https://github.com/envoyproxy/gateway/pull/958 -[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 -[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/docs/latest/dev_docs.rst b/docs/latest/dev_docs.rst deleted file mode 100644 index e546e14c1ad..00000000000 --- a/docs/latest/dev_docs.rst +++ /dev/null @@ -1,15 +0,0 @@ -Developer Docs -============== - -Learn how to contribute to Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - dev/GOALS - dev/CODE_OF_CONDUCT - dev/CODEOWNERS - dev/CONTRIBUTING - dev/README - dev/DOCS - dev/releasing diff --git a/docs/latest/get_involved.rst b/docs/latest/get_involved.rst deleted file mode 100644 index f17febd5651..00000000000 --- a/docs/latest/get_involved.rst +++ /dev/null @@ -1,9 +0,0 @@ -Getting Involved -================ - -We welcome contributions from the community. Please carefully review the -`project goals `_ -and the -`code of conduct `_ -before diving in. - diff --git a/docs/latest/helm.rst b/docs/latest/helm.rst deleted file mode 100644 index 853e26a141b..00000000000 --- a/docs/latest/helm.rst +++ /dev/null @@ -1,9 +0,0 @@ -Helm Guides -=========== - -Learn how to deploy, use the helm chart for Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - helm/api.md \ No newline at end of file diff --git a/docs/latest/helm/api.md b/docs/latest/helm/api.md deleted file mode 100644 index c046b66e222..00000000000 --- a/docs/latest/helm/api.md +++ /dev/null @@ -1,53 +0,0 @@ -# gateway-helm - -![Version: v0.0.0-latest](https://img.shields.io/badge/Version-v0.0.0--latest-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square) - -The Helm chart for Envoy Gateway - -**Homepage:** - -## Maintainers - -| Name | Email | Url | -| ---- | ------ | --- | -| envoy-gateway-steering-committee | | | -| envoy-gateway-maintainers | | | - -## Source Code - -* - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| config.envoyGateway.gateway.controllerName | string | `"gateway.envoyproxy.io/gatewayclass-controller"` | | -| config.envoyGateway.logging.level.default | string | `"info"` | | -| config.envoyGateway.provider.type | string | `"Kubernetes"` | | -| createNamespace | bool | `false` | | -| deployment.envoyGateway.image.repository | string | `"${ImageRepository}"` | | -| deployment.envoyGateway.image.tag | string | `"${ImageTag}"` | | -| deployment.envoyGateway.imagePullPolicy | string | `"Always"` | | -| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | -| deployment.envoyGateway.resources.limits.memory | string | `"128Mi"` | | -| deployment.envoyGateway.resources.requests.cpu | string | `"10m"` | | -| deployment.envoyGateway.resources.requests.memory | string | `"64Mi"` | | -| deployment.kubeRbacProxy.image.repository | string | `"gcr.io/kubebuilder/kube-rbac-proxy"` | | -| deployment.kubeRbacProxy.image.tag | string | `"v0.11.0"` | | -| deployment.kubeRbacProxy.resources.limits.cpu | string | `"500m"` | | -| deployment.kubeRbacProxy.resources.limits.memory | string | `"128Mi"` | | -| deployment.kubeRbacProxy.resources.requests.cpu | string | `"5m"` | | -| deployment.kubeRbacProxy.resources.requests.memory | string | `"64Mi"` | | -| deployment.ports[0].name | string | `"grpc"` | | -| deployment.ports[0].port | int | `18000` | | -| deployment.ports[0].targetPort | int | `18000` | | -| deployment.ports[1].name | string | `"ratelimit"` | | -| deployment.ports[1].port | int | `18001` | | -| deployment.ports[1].targetPort | int | `18001` | | -| deployment.replicas | int | `1` | | -| envoyGatewayMetricsService.ports[0].name | string | `"https"` | | -| envoyGatewayMetricsService.ports[0].port | int | `8443` | | -| envoyGatewayMetricsService.ports[0].protocol | string | `"TCP"` | | -| envoyGatewayMetricsService.ports[0].targetPort | string | `"https"` | | -| kubernetesClusterDomain | string | `"cluster.local"` | | - diff --git a/docs/latest/index.rst b/docs/latest/index.rst deleted file mode 100644 index 81957cef394..00000000000 --- a/docs/latest/index.rst +++ /dev/null @@ -1,35 +0,0 @@ -`Envoy Gateway `_ -==================== - -Release: |version| - -.. image:: https://img.shields.io/badge/slack-join-orange.svg - :target: https://envoyproxy.slack.com/archives/C03E6NHLESV - :alt: Join the Envoy Slack - -Envoy Gateway is an open source project for managing `Envoy Proxy`_ as a standalone or Kubernetes-based application -gateway. `Gateway API`_ resources are used to dynamically provision and configure the managed Envoy Proxies. Whether -you are interested in using or contributing to Envoy Gateway, the following resources will help you get started: - -.. toctree:: - :maxdepth: 1 - - intro/compatibility - user_docs - design_docs - dev_docs - api_docs - helm - releases - roadmap - about_docs - get_involved - presentations - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved`. - -.. _Envoy Proxy: https://www.envoyproxy.io/ -.. _Gateway API: https://gateway-api.sigs.k8s.io/ diff --git a/docs/latest/intro/compatibility.rst b/docs/latest/intro/compatibility.rst deleted file mode 100644 index 68722f8a762..00000000000 --- a/docs/latest/intro/compatibility.rst +++ /dev/null @@ -1,25 +0,0 @@ -Compatibility Matrix -==================== - -Envoy Gateway relies on the Envoy Proxy and the Gateway API, and runs -within a Kubernetes cluster. Not all versions of each of these products -can function together for Envoy Gateway. Supported version combinations -are listed below; **bold** type indicates the versions of the Envoy Proxy -and the Gateway API actually compiled into each Envoy Gateway release. - -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| Envoy Gateway version | Envoy Proxy version | Rate Limit version | Gateway API version | Kubernetes version | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| v0.4.0 | **v1.26-latest** | **542a6047** | **v0.6.2** | v1.25, v1.26, v1.27 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| v0.3.0 | **v1.25-latest** | **f28024e3** | **v0.6.1** | v1.24, v1.25, v1.26 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| v0.2.0 | **v1.23-latest** | | **v0.5.1** | v1.24 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| latest | **dev-latest** | **master** | **v0.6.2** | v1.25, v1.26, v1.27 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved<../get_involved>`. diff --git a/docs/latest/presentations.md b/docs/latest/presentations.md deleted file mode 100644 index bc681b8c268..00000000000 --- a/docs/latest/presentations.md +++ /dev/null @@ -1,12 +0,0 @@ -# Presentations - -This page contains a list of presentations about the Envoy Proxy project. - -| Conference | Title | Speaker | Recording | Slides | -|-----------------|------------------------------|------------------------------|--------------------------------------------------------|--------| -| KubeCon NA 2022 | Envoy Gateway Project Update | Daneyon Hansen & Alice Wasko | [YouTube](https://www.youtube.com/watch?v=3MUOZc8XNCc) | [Slides](https://static.sched.com/hosted_files/envoyconna22/2f/Envoy_Gateway_Project_Update_EnvoyCon_NA_2022.pptx) | -| KubeCon EU 2023 | Envoy Gateway Project Update | Alice Wasko | [YouTube](https://www.youtube.com/watch?v=4vnJxt9sVho) | [Slides](https://static.sched.com/hosted_files/kccnceu2023/58/Kubecon_EU_2023_Envoy_Gateway_Update.pptx) | - - - - diff --git a/docs/latest/releases.rst b/docs/latest/releases.rst deleted file mode 100644 index 42ce2b7d7b8..00000000000 --- a/docs/latest/releases.rst +++ /dev/null @@ -1,12 +0,0 @@ -Releases -======== - -Learn more about Envoy Gateway releases. - -.. toctree:: - :maxdepth: 1 - - releases/README - releases/v0.2 - releases/v0.3 - releases/v0.4 \ No newline at end of file diff --git a/docs/latest/releases/README.md b/docs/latest/releases/README.md deleted file mode 100644 index 2ca374ca69d..00000000000 --- a/docs/latest/releases/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Release Details - -This document provides details for Envoy Gateway releases. Envoy Gateway follows the Semantic Versioning [v2.0.0 spec][] -for release versioning. Since Envoy Gateway is a new project, minor releases are the only defined releases. Envoy -Gateway maintainers will establish additional release details, e.g. patch releases, at a future date. - -## Stable Releases - -Stable releases of Envoy Gateway include: - -* Minor Releases- A new release branch and corresponding tag are created from the `main` branch. A minor release - is supported for 6 months following the release date. As the project matures, Envoy Gateway maintainers will reassess - the support timeframe. - -Minor releases happen quarterly and follow the schedule below. - -## Release Management - -Minor releases are handled by a designated Envoy Gateway maintainer. This maintainer is considered the Release Manager -for the release. The details for creating a release are outlined in the [release guide][]. The Release Manager is -responsible for coordinating the overall release. This includes identifying issues to be fixed in the release, -communications with the Envoy Gateway community, and the mechanics of the release. - -| Quarter | Release Manager | -|:-------:|:--------------------------------------------------------------:| -| 2022 Q4 | Daneyon Hansen ([danehans](https://github.com/danehans)) | -| 2023 Q1 | Xunzhuo Liu ([Xunzhuo](https://github.com/Xunzhuo)) | -| 2023 Q2 | Alice Wasko ([AliceProxy](https://github.com/AliceProxy)) | - -## Release Schedule - -In order to align with the Envoy Proxy [release schedule][], Envoy Gateway releases are produced on a fixed schedule -(the 22nd day of each quarter), with an acceptable delay of up to 2 weeks, and a hard deadline of 3 weeks. - -| Version | Expected | Actual | Difference | End of Life | -|:-------:|:-----------:|:-----------:|:----------:|:-----------:| -| 0.2.0 | 2022/10/22 | 2022/10/20 | -2 day | 2023/4/20 | -| 0.3.0 | 2023/01/22 | 2023/02/09 | +17 day | 2023/08/09 | -| 0.3.0 | 2023/04/22 | 2023/04/24 | +2 day | 2023/10/24 | - -[v2.0.0 spec]: https://semver.org/spec/v2.0.0.html -[release guide]: ../dev/releasing.md -[release schedule]: https://github.com/envoyproxy/envoy/blob/main/RELEASES.md#major-release-schedule diff --git a/docs/latest/releases/v0.2.md b/docs/latest/releases/v0.2.md deleted file mode 100644 index a0dc0e885de..00000000000 --- a/docs/latest/releases/v0.2.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.2 -linktitle: v0.2 -subtitle: Major Update -description: Envoy Gateway v0.2 release announcement. -publishdate: 2022-10-20 -release: v0.2.0 -skip_list: true -aliases: -- /releases/v0.2 -- /releases/v0.2.0 ---- -# Envoy Gateway Release v0.2 - -We are pleased to announce the release of Envoy Gateway v0.2! - -This is the first functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Kubernetes Support - -Run Envoy Gateway in a Kubernetes cluster. Checkout the [quickstart guide][] to get started with Envoy Gateway in a few -simple steps. - -### Gateway API Support - -Envoy Gateway supports Gateway API resources for running and configuring a managed fleet of Envoy proxies. Envoy Gateway -passes Gateway API core [conformance tests][] and supports GatewayClass, Gateway, HTTPRoute, and TLSRoute resources. See -the [documentation][docs] for additional details on how to use Envoy Gateway for your edge proxy and API gateway needs. - -## Envoy Gateway at EnvoyCon NA - -Envoy Gateway will be at [EnvoyCon NA][] this October in Detroit. Don't miss [our talk][] to learn more about the -release and future direction of the project. - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.2.0.yaml -[matrix]: https://gateway.envoyproxy.io/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.2.0 -[conformance tests]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=conformance -[quickstart guide]: https://gateway.envoyproxy.io/user/quickstart.html -[EnvoyCon NA]: https://events.linuxfoundation.org/envoycon-north-america/program/schedule/ -[our talk]: https://sched.co/1AO5S diff --git a/docs/latest/releases/v0.3.md b/docs/latest/releases/v0.3.md deleted file mode 100644 index 96d6d6d49eb..00000000000 --- a/docs/latest/releases/v0.3.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.3 -linktitle: v0.3 -subtitle: Major Update -description: Envoy Gateway v0.3 release announcement. -publishdate: 2023-02-09 -release: v0.3.0 -skip_list: true -aliases: -- /releases/v0.3 -- /releases/v0.3.0 ---- -# Envoy Gateway Release v0.3 - -We are pleased to announce the release of Envoy Gateway v0.3! - -This is the second functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Add Support for extended Gateway API fields - -+ Added Support for HTTPRoute URLRewrite Filter -+ Added Support for HTTPRoute RequestMirror Filter -+ Added Support for HTTPRoute ResponseHeaderModifier Filter - -### Add Support for experimental Gateway APIs - -+ Added Support for the TCPRoute API -+ Added Support for the UDPRoute API -+ Added Support for the GRPCRoute API - -### Add Support for Rate Limiting - -+ Added Support for Global Rate Limiting - -### Add Support for Authentication - -+ Added Support for Request Authentication - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.3.0.yaml -[matrix]: https://gateway.envoyproxy.io/v0.3.0/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/v0.3.0/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.3.0 diff --git a/docs/latest/releases/v0.4.md b/docs/latest/releases/v0.4.md deleted file mode 100644 index 81a9bc3a0ed..00000000000 --- a/docs/latest/releases/v0.4.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.4 -linktitle: v0.4 -subtitle: Major Update -description: Envoy Gateway v0.4 release announcement. -publishdate: 2023-04-24 -release: v0.4.0 -skip_list: true -aliases: -- /releases/v0.4 -- /releases/v0.4.0 ---- -# Envoy Gateway Release v0.4 - -We are pleased to announce the release of Envoy Gateway v0.4! - -This is the third functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Upgrade Gateway API Dependency - -+ Upgraded to Gateway API v0.6.2 - -### Add Helm Support - -+ Installation of Envoy Gateway can now be done through helm - -### Add egctl CLI Tool - -+ Added egctl Support for Dry Runs of Gateway API Config -+ Added egctl Support for Dumping Envoy Proxy xDS Resources - -### Add Support for extending Envoy Gateway - -+ Added Initial Framework for Building an Extension on top of Envoy Gateway - -### Ratelimiting - -+ Added Support for Ratelimiting Based On IP Subnet - -### API Updates - -+ Added Support for Custom Envoy Proxy Bootstrap Config -+ Added Support for Configuring the Envoy Proxy Image and Service -+ Added Support for Configuring Annotations, Resources, and Securitycontext Settings on Ratelimit Infra and Envoy Proxy -+ Added Support for Using Multiple Certificates on a Single Fully Qualified Domain Name -+ Envoy Proxy Pod and Container SecurityContext is now Configurable -+ Added Support for Service Method Match in GRPCRoute -+ Added EDS Support - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.4.0.yaml -[matrix]: https://gateway.envoyproxy.io/v0.4.0/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/v0.4.0/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.4.0 diff --git a/docs/latest/roadmap.rst b/docs/latest/roadmap.rst deleted file mode 100644 index 711b6245503..00000000000 --- a/docs/latest/roadmap.rst +++ /dev/null @@ -1,9 +0,0 @@ -Roadmap -======= - -Learn about the future direction of Envoy Gateway. - -.. toctree:: - :maxdepth: 2 - - design/roadmap diff --git a/docs/latest/user/authn.md b/docs/latest/user/authn.md deleted file mode 100644 index f7405906203..00000000000 --- a/docs/latest/user/authn.md +++ /dev/null @@ -1,94 +0,0 @@ -# Request Authentication - -This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks -if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only -supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. - -## Installation - -Follow the steps from the [Quickstart](quickstart.md) guide to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Configuration - -Allow requests with a valid JWT by creating an [AuthenticationFilter][] and referencing it from the example HTTPRoute. - -```shell -kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/authn/jwt.yaml -``` - -The HTTPRoute is now updated to authenticate requests for `/foo` and allow unauthenticated requests to `/bar`. The -`/foo` route rule references an AuthenticationFilter that provides the JWT authentication configuration. - -Verify the HTTPRoute configuration and status: - -```shell -kubectl get httproute/backend -o yaml -``` - -The AuthenticationFilter is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] -provider for authenticating the JWT. - -Verify the AuthenticationFilter configuration: - -```shell -kubectl get authenticationfilter/jwt-example -o yaml -``` - -## Testing - -Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](quickstart.md) guide is set. If not, follow the -Quickstart instructions to set the variable. - -```shell -echo $GATEWAY_HOST -``` - -Verify that requests to `/foo` are denied without a JWT: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo -``` - -A `401` HTTP response code should be returned. - -Get the JWT used for testing request authentication: - -```shell -TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - -``` - -__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's -header. - -Verify that a request to `/foo` with a valid JWT is allowed: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo -``` - -A `200` HTTP response code should be returned. - -Verify that requests to `/bar` are allowed __without__ a JWT: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar -``` - -## Clean-Up - -Follow the steps from the [Quickstart](quickstart.md) guide to uninstall Envoy Gateway and the example manifest. - -Delete the AuthenticationFilter: - -```shell -kubectl delete authenticationfilter/jwt-example -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. - -[jwt]: https://tools.ietf.org/html/rfc7519 -[AuthenticationFilter]: https://gateway.envoyproxy.io/latest/api/extension_types.html#authenticationfilter -[jwks]: https://tools.ietf.org/html/rfc7517 diff --git a/docs/latest/user/customize-envoyproxy.md b/docs/latest/user/customize-envoyproxy.md deleted file mode 100644 index ac9d4d61230..00000000000 --- a/docs/latest/user/customize-envoyproxy.md +++ /dev/null @@ -1,313 +0,0 @@ -# Customize EnvoyProxy - -Envoy Gateway provides an [EnvoyProxy][] CRD that can be linked to the ParametersRef -in GatewayClass, allowing cluster admins to customize the managed EnvoyProxy Deployment and -Service. To learn more about GatewayClass and ParametersRef, please refer to [Gateway API documentation][]. - -## Installation - -Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Add GatewayClass ParametersRef - -First, you need to add ParametersRef in GatewayClass, and refer to EnvoyProxy Config: - -```shell -cat < Envoy Gateway has provided two initial `env` `ENVOY_GATEWAY_NAMESPACE` and `ENVOY_POD_NAME` for envoyproxy container. - -After applying the config, you can get the envoyproxy deployment, and see resources has been changed. - -## Customize EnvoyProxy Deployment Volumes or VolumeMounts - -You can customize the EnvoyProxy Deployment Volumes or VolumeMounts via EnvoyProxy Config like: - -```shell -cat < GET /get HTTP/1.1 -> Host: www.marketing.example.com -> User-Agent: curl/7.86.0 -> Accept: */* -> -Handling connection for 8888 -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Thu, 20 Apr 2023 19:19:42 GMT -< content-length: 521 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "www.marketing.example.com", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.86.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Internal": [ - "true" - ], - "X-Forwarded-For": [ - "10.1.0.157" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "c637977c-458a-48ae-92b3-f8c429849322" - ] - }, - "namespace": "marketing", - "ingress": "", - "service": "", - "pod": "backend-74888f465f-bcs8f" -* Connection #0 to host localhost left intact -``` - -* Lets deploy Envoy Gateway in the `product` namespace - -``` -helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n product --create-namespace -``` - -Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. - -```shell -cat < GET /get HTTP/1.1 -> Host: www.product.example.com -> User-Agent: curl/7.86.0 -> Accept: */* -> -Handling connection for 8889 -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Thu, 20 Apr 2023 19:20:17 GMT -< content-length: 517 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "www.product.example.com", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.86.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Internal": [ - "true" - ], - "X-Forwarded-For": [ - "10.1.0.156" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "39196453-2250-4331-b756-54003b2853c2" - ] - }, - "namespace": "product", - "ingress": "", - "service": "", - "pod": "backend-74888f465f-64fjs" -* Connection #0 to host localhost left intact -``` - -With the below command you can ensure that you are no able to access the marketing team's backend exposed using the `www.marketing.example.com` hostname -and the product team's data plane. - -```shell -curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get -``` - -``` -* Trying 127.0.0.1:8889... -* Connected to localhost (127.0.0.1) port 8889 (#0) -> GET /get HTTP/1.1 -> Host: www.marketing.example.com -> User-Agent: curl/7.86.0 -> Accept: */* -> -Handling connection for 8889 -* Mark bundle as not supporting multiuse -< HTTP/1.1 404 Not Found -< date: Thu, 20 Apr 2023 19:22:13 GMT -< server: envoy -< content-length: 0 -< -* Connection #0 to host localhost left intact -``` diff --git a/docs/latest/user/egctl.md b/docs/latest/user/egctl.md deleted file mode 100644 index 4ec79648f7b..00000000000 --- a/docs/latest/user/egctl.md +++ /dev/null @@ -1,788 +0,0 @@ -# egctl - -`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. - -## Installing egctl - -This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. - -### From The Envoy Gateway Project - -The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. - -### From the Binary Releases - -Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. - -1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) -2. Unpack it (tar -zxvf egctl_latest_linux_amd64.tar.gz) -3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) - -From there, you should be able to run: `egctl help`. - -### From Script - -`egctl` now has an installer script that will automatically grab the latest release version of egctl and install it locally. - -You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. - -```shell -curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh - -chmod +x get-egctl.sh - -# get help info of the -bash get-egctl.sh --help - -# install the latest development version of egctl -bash VERSION=latest get-egctl.sh -``` - -Yes, you can just use the below command if you want to live on the edge. - -```shell -curl https://gateway.envoyproxy.io/get-egctl.sh | VERSION=latest bash -``` - -## egctl experimental translate - -This subcommand allows users to translate from an input configuration type to an output configuration type. - -In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS -resources. - -```shell -cat <// - name: default/eg/http - operation: - op: add - path: "/default_filter_chain/filters/0/typed_config/local_reply_config" - value: - mappers: - - filter: - status_code_filter: - comparison: - op: EQ - value: - default_value: 404 - runtime_key: key_b - status_code: 406 - body: - inline_string: "could not find what you are looking for" -EOF -``` - -* Lets edit the HTTPRoute resource from the Quickstart to only match on paths with value `/get` - -``` -kubectl patch httproute backend --type=json --patch '[{ - "op": "add", - "path": "/spec/rules/0/matches/0/path/value", - "value": "/get", -}]' -``` - -* Lets test it out by specifying a path apart from `/get` - -``` -$ curl --header "Host: www.example.com" http://localhost:8888/find -Handling connection for 8888 -could not find what you are looking for -``` - -## Debugging - -### Runtime - -* The `Status` subresource should have information about the status of the resource. Make sure -`Accepted=True` and `Programmed=True` conditions are set to ensure that the policy has been -applied to Envoy Proxy. - -``` -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: EnvoyPatchPolicy -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"gateway.envoyproxy.io/v1alpha1","kind":"EnvoyPatchPolicy","metadata":{"annotations":{},"name":"custom-response-patch-policy","namespace":"default"},"spec":{"jsonPatches":[{"name":"default/eg/http","operation":{"op":"add","path":"/default_filter_chain/filters/0/typed_config/local_reply_config","value":{"mappers":[{"body":{"inline_string":"could not find what you are looking for"},"filter":{"status_code_filter":{"comparison":{"op":"EQ","value":{"default_value":404}}}}}]}},"type":"type.googleapis.com/envoy.config.listener.v3.Listener"}],"priority":0,"targetRef":{"group":"gateway.networking.k8s.io","kind":"Gateway","name":"eg","namespace":"default"},"type":"JSONPatch"}} - creationTimestamp: "2023-07-31T21:47:53Z" - generation: 1 - name: custom-response-patch-policy - namespace: default - resourceVersion: "10265" - uid: a35bda6e-a0cc-46d7-a63a-cee765174bc3 -spec: - jsonPatches: - - name: default/eg/http - operation: - op: add - path: /default_filter_chain/filters/0/typed_config/local_reply_config - value: - mappers: - - body: - inline_string: could not find what you are looking for - filter: - status_code_filter: - comparison: - op: EQ - value: - default_value: 404 - type: type.googleapis.com/envoy.config.listener.v3.Listener - priority: 0 - targetRef: - group: gateway.networking.k8s.io - kind: Gateway - name: eg - namespace: default - type: JSONPatch -status: - conditions: - - lastTransitionTime: "2023-07-31T21:48:19Z" - message: EnvoyPatchPolicy has been accepted. - observedGeneration: 1 - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: "2023-07-31T21:48:19Z" - message: successfully applied patches. - reason: Programmed - status: "True" - type: Programmed -``` - -### Offline - -* You can use [egctl x translate][] to validate the translated xds output. - -## Caveats - -This API will always be an unstable API and the same outcome cannot be garunteed -across versions for these reasons -* The Envoy Proxy API might deprecate and remove API fields -* Envoy Gateway might alter the xDS translation creating a different xDS output -such as changing the `name` field of resources. - -[EnvoyPatchPolicy]: https://gateway.envoyproxy.io/latest/api/extension_types.html#envoypatchpolicy -[EnvoyGateway]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoygateway -[JSON Patch]: https://datatracker.ietf.org/doc/html/rfc6902 -[xDS]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/dynamic_configuration -[Local Reply Modification]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/local_reply -[egctl x translate]: https://gateway.envoyproxy.io/latest/user/egctl.html#egctl-experimental-translate diff --git a/docs/latest/user/gateway-address.md b/docs/latest/user/gateway-address.md deleted file mode 100644 index a28ad62640a..00000000000 --- a/docs/latest/user/gateway-address.md +++ /dev/null @@ -1,67 +0,0 @@ -# Gateway Address - -The Gateway API provides an optional [Addresses][] field through which Envoy Gateway can set addresses for Envoy Proxy Service. The currently supported addresses are: - -- [External IPs](#External-IPs) - -## Installation - -Install Envoy Gateway: - -```shell -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -## External IPs - -Using the addresses in `Gateway.Spec.Addresses` as the [External IPs][] of Envoy Proxy Service, this will __require__ the address to be of type `IPAddress`. - -Install the GatewayClass, Gateway from quickstart: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default -``` - -Set the address of the Gateway, the address settings here are for reference only: - -```shell -kubectl patch gateway eg --type=json --patch '[{ - "op": "add", - "path": "/spec/addresses", - "value": [{ - "type": "IPAddress", - "value": "1.2.3.4" - }] -}]' -``` - -Verify the Gateway status: - -```shell -kubectl get gateway - -NAME CLASS ADDRESS PROGRAMMED AGE -eg eg 1.2.3.4 True 14m -``` - -Verify the Envoy Proxy Service status: - -```shell -kubectl get service -n envoy-gateway-system - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -envoy-default-eg-64656661 LoadBalancer 10.96.236.219 1.2.3.4 80:31017/TCP 15m -envoy-gateway ClusterIP 10.96.192.76 18000/TCP 15m -envoy-gateway-metrics-service ClusterIP 10.96.124.73 8443/TCP 15m -``` - -__Note:__ If the `Gateway.Spec.Addresses` is explicitly set, it will be the only addresses that populates the Gateway status. - -[Addresses]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayAddress -[External IPs]: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips diff --git a/docs/latest/user/gatewayapi-support.md b/docs/latest/user/gatewayapi-support.md deleted file mode 100644 index ee14f395258..00000000000 --- a/docs/latest/user/gatewayapi-support.md +++ /dev/null @@ -1,116 +0,0 @@ -# Gateway API Support - -As mentioned in the [system design][] document, Envoy Gateway's managed data plane is configured dynamically through -Kubernetes resources, primarily [Gateway API][] objects. Envoy Gateway supports configuration using the following Gateway API resources. - -## GatewayClass - -A [GatewayClass][] represents a "class" of gateways, i.e. which Gateways should be managed by Envoy Gateway. -Envoy Gateway supports managing __a single__ GatewayClass resource that matches its configured `controllerName` and -follows Gateway API guidelines for [resolving conflicts][] when multiple GatewayClasses exist with a matching -`controllerName`. - -__Note:__ If specifying GatewayClass [parameters reference][], it must refer to an [EnvoyProxy][] resource. - -## Gateway - -When a [Gateway][] resource is created that references the managed GatewayClass, Envoy Gateway will create and manage a -new Envoy Proxy deployment. Gateway API resources that reference this Gateway will configure this managed Envoy Proxy -deployment. - -## HTTPRoute - -An [HTTPRoute][] configures routing of HTTP traffic through one or more Gateways. The following HTTPRoute filters are -supported by Envoy Gateway: - -- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - can be used to modify or add request headers before the request is proxied to its destination. -- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - can be used to modify or add response headers before the response is sent back to the client. -- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. -- `requestRedirect`: [RequestRedirects](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - configure policied for how requests that match the HTTPRoute should be modified and then redirected. -- `urlRewrite`: [UrlRewrites](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - allow for modification of the request's hostname and path before it is proxied to its destination. -- `extensionRef`: [ExtensionRefs][] are used by Envoy Gateway to implement extended filters. Currently, Envoy Gateway - supports rate limiting and request authentication filters. For more information about these filters, refer to the - [rate limiting][] and [request authentication][] documentation. - -__Notes:__ -- The only [BackendRef][] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other destinations such - as arbitrary URLs is not possible. -- The `filters` field within [HTTPBackendRef][] is not supported. - -## TCPRoute - -A [TCPRoute][] configures routing of raw TCP traffic through one or more Gateways. Traffic can be forwarded to the -desired BackendRefs based on a TCP port number. - -__Note:__ A TCPRoute only supports proxying in non-transparent mode, i.e. the backend will see the source IP and port of -the Envoy Proxy instance instead of the client. - -## UDPRoute - -A [UDPRoute][] configures routing of raw UDP traffic through one or more Gateways. Traffic can be forwarded to the -desired BackendRefs based on a UDP port number. - -__Note:__ Similar to TCPRoutes, UDPRoutes only support proxying in non-transparent mode i.e. the backend will see the -source IP and port of the Envoy Proxy instance instead of the client. - -## GRPCRoute - -A [GRPCRoute][] configures routing of [gRPC][] requests through one or more Gateways. They offer request matching by -hostname, gRPC service, gRPC method, or HTTP/2 Header. Envoy Gateway supports the following filters on GRPCRoutes to -provide additional traffic processing: - -- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) - can be used to modify or add request headers before the request is proxied to its destination. -- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) - can be used to modify or add response headers before the response is sent back to the client. -- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) - configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. - -__Notes:__ -- The only [BackendRef][grpc-filter] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other - destinations such as arbitrary URLs is not currently possible. -- The `filters` field within [HTTPBackendRef][] is not supported. - -## TLSRoute - -A [TLSRoute][] configures routing of TCP traffic through one or more Gateways. However, unlike TCPRoutes, TLSRoutes -can match against TLS-specific metadata. - -## ReferenceGrant - -A [ReferenceGrant][] is used to allow a resource to reference another resource in a different namespace. Normally an -HTTPRoute created in namespace `foo` is not allowed to reference a Service in namespace `bar`. A ReferenceGrant permits -these types of cross-namespace references. Envoy Gateway supports the following ReferenceGrant use-cases: - -- Allowing an HTTPRoute, GRPCRoute, TLSRoute, UDPRoute, or TCPRoute to reference a Service in a different namespace. -- Allowing an HTTPRoute's `requestMirror` filter to include a BackendRef that references a Service in a different - namespace. -- Allowing a Gateway's [SecretObjectReference][] to reference a secret in a different namespace. - -[system design]: https://gateway.envoyproxy.io/latest/design/system-design.html -[Gateway API]: https://gateway-api.sigs.k8s.io/ -[GatewayClass]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayClass -[parameters reference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.ParametersReference -[Gateway]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway -[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute -[Service]: https://kubernetes.io/docs/concepts/services-networking/service/ -[BackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef -[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPBackendRef -[TCPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute -[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute -[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute -[gRPC]: https://grpc.io/ -[TLSRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute -[ReferenceGrant]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.ReferenceGrant -[SecretObjectReference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference -[rate limiting]: https://gateway.envoyproxy.io/latest/user/rate-limit.html -[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html -[EnvoyProxy]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoyproxy -[resolving conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/?h=conflict#conflicts -[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType -[grpc-filter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter diff --git a/docs/latest/user/grpc-routing.md b/docs/latest/user/grpc-routing.md deleted file mode 100644 index 7421c936311..00000000000 --- a/docs/latest/user/grpc-routing.md +++ /dev/null @@ -1,200 +0,0 @@ -# GRPC Routing - -The [GRPCRoute][] resource allows users to configure gRPC routing by matching HTTP/2 traffic and forwarding it to backend gRPC servers. -To learn more about gRPC routing, refer to the [Gateway API documentation][]. - -## Prerequisites - -Install Envoy Gateway: - -```shell -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -## Installation - -Install the gRPC routing example resources: - -```shell -kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/grpc-routing.yaml -``` - -The manifest installs a [GatewayClass][], [Gateway][], a Deployment, a Service, and a GRPCRoute resource. -The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. - -__Note:__ Envoy Gateway is configured by default to manage a GatewayClass with -`controllerName: gateway.envoyproxy.io/gatewayclass-controller`. - -## Verification - -Check the status of the GatewayClass: - -```shell -kubectl get gc --selector=example=grpc-routing -``` - -The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass. - -A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is -provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this -Gateway. Check the status of the Gateway: - -```shell -kubectl get gateways --selector=example=grpc-routing -``` - -The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also -provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend -services. - -Check the status of the GRPCRoute: - -```shell -kubectl get grpcroutes --selector=example=grpc-routing -o yaml -``` - -The status for the GRPCRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway. -The `example-route` matches any traffic for "grpc-example.com" and forwards it to the "yages" Service. - -## Testing the Configuration - -Before testing GRPC routing to the `yages` backend, get the Gateway's address. - -```shell -export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}') -``` - -Test GRPC routing to the `yages` backend using the [grpcurl][] command. - -```shell -grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping -``` - -You should see the below response - -```shell -{ - "text": "pong" -} -``` - -Envoy Gateway also supports [gRPC-Web][] requests for this configuration. The below `curl` command can be used to send a grpc-Web request with over HTTP/2. You should receive the same response seen in the previous command. - -```shell -curl --http2-prior-knowledge -s ${GATEWAY_HOST}:80/yages.Echo/Ping -H 'Host: grpc-example.com' -H 'Content-Type: application/grpc-web-text' -H 'Accept: application/grpc-web-text' -XPOST -d'AAAAAAA=' | base64 -d -``` - -## GRPCRoute Match -The `matches` field can be used to restrict the route to a specific set of requests based on GRPC's service and/or method names. -It supports two match types: `Exact` and `RegularExpression`. - -### Exact - -`Exact` match is the default match type. - -The following example shows how to match a request based on the service and method names for `grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo`, -as well as a match for all services with a method name `Ping` which matches `yages.Echo/Ping` in our deployment. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something", - "foo" - ], -... -``` - -## Setting Request Headers - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - "headers": { - "Accept": [ - "*/*" - ], - "Set-Header": [ - "foo" - ], -... -``` - -## Removing Request Headers - -Headers can be removed from a request by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something" - ], -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-79665566f5-s589f" -... -``` - -Check the logs of the pods and you will see that the original deployment and the new deployment each got a request: - -```shell -$ kubectl logs deploy/backend && kubectl logs deploy/backend-2 -... -Starting server, listening on port 3000 (http) -Echoing back request made to /get to client (10.42.0.10:41566) -Starting server, listening on port 3000 (http) -Echoing back request made to /get to client (10.42.0.10:45096) -``` - -## Multiple BackendRefs - -When an `HTTPRoute` has multiple `backendRefs` and an `HTTPRequestMirrorFilter`, traffic splitting will still behave the same as it normally would for the main `backendRefs` while the `backendRef` of the `HTTPRequestMirrorFilter` will continue receiving mirrored copies of the incoming requests. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: X-Foo: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< x-foo: value1 -< add-header: foo -< -... - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "X-Foo: value1" - ] -... -``` - -## Setting Response Headers - -Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it -will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header -if the response already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: set-header: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< set-header: foo -< - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "set-header": value1" - ] -... -``` - -## Removing Response Headers - -Headers can be removed from a response by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it -will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header -if the response already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: remove-header: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "remove-header": value1" - ] -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-79665566f5-s589f" -... -``` - -## Multiple backendRefs - -If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is -configured. - -First, create a second instance of the example app from the quickstart: - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-75bcd4c969-lsxpz" -... -``` - -## Weighted backendRefs - -If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` -field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is -assumed to be `1`. - -The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of -requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the -HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. - -The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the -backend-2 service. - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 500 Internal Server Error -< server: envoy -< content-length: 0 -< -``` - -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/docs/latest/user/http-urlrewrite.md b/docs/latest/user/http-urlrewrite.md deleted file mode 100644 index 88e29c3269c..00000000000 --- a/docs/latest/user/http-urlrewrite.md +++ /dev/null @@ -1,295 +0,0 @@ -# HTTP URL Rewrite - -[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be -used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. - -## Prerequisites - -Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Rewrite URL Prefix Path - -You can configure to rewrite the prefix in the url like below. In this example, any curls to -`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. - -```shell -cat < GET /get/origin/path HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> - -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:03:28 GMT -< content-length: 503 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/replace/origin/path", - "host": "path.rewrite.example", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Original-Path": [ - "/get/origin/path" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "fd84b842-9937-4fb5-83c7-61470d854b90" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. - -## Rewrite URL Full Path - -You can configure to rewrite the fullpath in the url like below. In this example, any request sent to -`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to -`http://${GATEWAY_HOST}/force/replace/fullpath`. - -```shell -cat < GET /get/origin/path/extra HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:09:31 GMT -< content-length: 512 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/force/replace/fullpath", - "host": "path.rewrite.example", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Original-Path": [ - "/get/origin/path/extra" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "8ab774d6-9ffa-4faa-abbb-f45b0db00895" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is -`/force/replace/fullpath`. - -## Rewrite Host Name - -You can configure to rewrite the hostname like below. In this example, any requests sent to -`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. - -```shell -cat < GET /get HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:15:15 GMT -< content-length: 481 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "envoygateway.io", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Forwarded-Host": [ - "path.rewrite.example" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "39aa447c-97b9-45a3-a675-9fb266ab1af0" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. - -[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPURLRewriteFilter diff --git a/docs/latest/user/installation.md b/docs/latest/user/installation.md deleted file mode 100644 index 667fbb47f8a..00000000000 --- a/docs/latest/user/installation.md +++ /dev/null @@ -1,110 +0,0 @@ -# Installation - -## Prerequisites - -A Kubernetes cluster. - -__Note:__ Refer to the [Compatibility Matrix](../intro/compatibility.rst) for supported Kubernetes versions. - -## Installation - -Install the Gateway API CRDs and Envoy Gateway: - -```shell -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -Install the GatewayClass, Gateway, HTTPRoute and example app: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default -``` - -**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for -traffic on port 80 on its globally-routable IP address, to make it easy to use -browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is -using a privileged port (<1024), it will map this internally to an -unprivileged port, so that Envoy Gateway doesn't need additional privileges. -It's important to be aware of this mapping, since you may need to take it into -consideration when debugging. - -[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml - -## Helm chart customizations - -Some of the quick ways of using the helm install command for envoy gateway installation are below. - -### Increase the replicas - -``` -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set deployment.replicas=2 -``` - -### Change the kubernetesClusterDomain name -If you have installed your cluster with different domain name you can use below command. - -``` -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set kubernetesClusterDomain= -``` - -**Note**: Above are some of the ways we can directly use for customization of our installation. But if you are looking for more complex changes [values.yaml](https://helm.sh/docs/chart_template_guide/values_files/) comes to rescue. - -### Using values.yaml file for complex installation. - -```yaml -deployment: - envoyGateway: - resources: - limits: - cpu: 700m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - ports: - - name: grpc - port: 18005 - targetPort: 18000 - - name: ratelimit - port: 18006 - targetPort: 18001 - -config: - envoyGateway: - logging: - level: - default: debug -``` - -Here we have made three changes to our values.yaml file. Increase the resources limit for cpu to `700m`, changed the port for grpc to `18005` and for ratelimit to `18006` and also updated the logging level to `debug`. - -You can use the below command to install the envoy gateway using values.yaml file. - -``` -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -f values.yaml -``` - -If you want to know all the available fields inside the values.yaml file checkout the `helm` section [here](../helm/api.md) - -## Open Ports - -These are the ports used by Envoy Gateway and the managed Envoy Proxy. - -| Envoy Gateway | Address | Port | -|:----------------------:|:---------:|:------:| -| Xds EnvoyProxy Server | 0.0.0.0 | 18000 | -| Xds RateLimit Server | 0.0.0.0 | 18001 | -| Admin Server | 127.0.0.1 | 19000 | - - - -| Envoy Proxy | Address | Port | -|:-----------------: |:-----------:| :-----: | -| Admin Server | 127.0.0.1 | 19000 | -| Heath Check Listener| 0.0.0.0 | 19001 | \ No newline at end of file diff --git a/docs/latest/user/quickstart.md b/docs/latest/user/quickstart.md deleted file mode 100644 index a6267d0464d..00000000000 --- a/docs/latest/user/quickstart.md +++ /dev/null @@ -1,97 +0,0 @@ -# Quickstart - -This guide will help you get started with Envoy Gateway in a few simple steps. - -## Prerequisites - -A Kubernetes cluster. - -__Note:__ Refer to the [Compatibility Matrix](../intro/compatibility.rst) for supported Kubernetes versions. - -## Installation - -Install the Gateway API CRDs and Envoy Gateway: - -```shell -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -Install the GatewayClass, Gateway, HTTPRoute and example app: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default -``` - -**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for -traffic on port 80 on its globally-routable IP address, to make it easy to use -browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is -using a privileged port (<1024), it will map this internally to an -unprivileged port, so that Envoy Gateway doesn't need additional privileges. -It's important to be aware of this mapping, since you may need to take it into -consideration when debugging. - -[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml - -## Testing the Configuration - -Get the name of the Envoy service created the by the example Gateway: - -```shell -export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward to the Envoy service: - -```shell -kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & -``` - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://localhost:8888/get -``` - -### External LoadBalancer Support - -You can also test the same functionality by sending traffic to the External IP. To get the external IP of the -Envoy service, run: - -```shell -export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -``` - -In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace -`ip` in the above command with `hostname`. - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get -``` - -## Clean-Up - -Use the steps in this section to uninstall everything from the quickstart guide. - -Delete the GatewayClass, Gateway, HTTPRoute and Example App: - -```shell -kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml --ignore-not-found=true -``` - -Delete the Gateway API CRDs and Envoy Gateway: - -```shell -helm uninstall eg -n envoy-gateway-system -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. diff --git a/docs/latest/user/rate-limit.md b/docs/latest/user/rate-limit.md deleted file mode 100644 index 9beebf65f4b..00000000000 --- a/docs/latest/user/rate-limit.md +++ /dev/null @@ -1,808 +0,0 @@ -# Rate Limit - -Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. - -Here are some reasons why you may want to implements Rate limits - -* To prevent malicious activity such as DDoS attacks. -* To prevent applications and its resources (such as a database) from getting overloaded. -* To create API limits based on user entitlements. - -Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied -i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit -if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. - -Envoy Gateway introduces a new CRD called [RateLimitFilter][] that allows the user to describe their rate limit intent. This instantiated resource -can be linked to a [HTTPRoute][] resource using an [ExtensionRef][] filter. - -## Prerequisites - -### Install Envoy Gateway - -* Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -### Install Redis - -* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. -Lets install a Redis deployment in the `redis-system` namespce. - -```shell -cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com -; (1 server found) -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 -;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 -;; WARNING: recursion requested but not available - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -; COOKIE: 24fb86eba96ebf62 (echoed) -;; QUESTION SECTION: -;foo.bar.com. IN A - -;; ADDITIONAL SECTION: -foo.bar.com. 0 IN A 10.244.0.19 -_udp.foo.bar.com. 0 IN SRV 0 0 42376 . - -;; Query time: 1 msec -;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) -;; WHEN: Fri Jan 13 10:20:34 UTC 2023 -;; MSG SIZE rcvd: 114 -``` - -## Clean-Up - -Follow the steps from the [Quickstart Guide](quickstart.md) to uninstall Envoy Gateway. - -Delete the CoreDNS example manifest and the UDPRoute: - -```shell -kubectl delete deploy/coredns -kubectl delete service/coredns -kubectl delete cm/coredns -kubectl delete udproute/coredns -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. - -[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute -[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/udp_filters/udp_proxy diff --git a/docs/latest/user_docs.rst b/docs/latest/user_docs.rst deleted file mode 100644 index 823582cacb7..00000000000 --- a/docs/latest/user_docs.rst +++ /dev/null @@ -1,32 +0,0 @@ -User Guides -=========== - -Learn how to deploy, use, and operate Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - user/installation - user/quickstart - user/http-routing - user/http-redirect - user/http-urlrewrite - user/http-traffic-splitting - user/http-traffic-mirroring - user/http-request-headers - user/http-response-headers - user/secure-gateways - user/tls-cert-manager - user/tls-passthrough - user/tls-termination - user/tcp-routing - user/udp-routing - user/grpc-routing - user/authn - user/rate-limit - user/envoy-patch-policy - user/egctl - user/customize-envoyproxy - user/deployment-mode - user/gateway-address - user/gatewayapi-support diff --git a/docs/v0.2.0/about_docs.rst b/docs/v0.2.0/about_docs.rst deleted file mode 100644 index ecaa28247b9..00000000000 --- a/docs/v0.2.0/about_docs.rst +++ /dev/null @@ -1,9 +0,0 @@ -About the Documentation -======================= - -Learn how to contribute to Envoy Gateway documentation. - -.. toctree:: - :maxdepth: 1 - - dev/DOCS diff --git a/docs/v0.2.0/conf.py b/docs/v0.2.0/conf.py deleted file mode 100644 index 94f7896b25a..00000000000 --- a/docs/v0.2.0/conf.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import re - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.duration', - 'sphinx.ext.autosectionlabel', - 'myst_parser', -] - -autosectionlabel_prefix_document = True -myst_heading_anchors = 3 - -html_theme = 'alabaster' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -version = os.environ["BUILD_VERSION"] -envoyVersion = os.environ["ENVOY_PROXY_VERSION"] -gatewayAPIVersion = os.environ["GATEWAYAPI_VERSION"] - -project = 'Envoy Gateway' -author = 'Envoy Gateway Project Authors' - -copyright = 'Envoy Gateway Project Authors | GitHub | Latest Docs' - -source_suffix = { - '.rst': 'restructuredtext', - '.md': 'markdown', -} - -variables_to_export = [ - "envoyVersion", - "gatewayAPIVersion", -] - -frozen_locals = dict(locals()) -rst_epilog = '\n'.join(map(lambda x: f".. |{x}| replace:: {frozen_locals[x]}", variables_to_export)) -del frozen_locals diff --git a/docs/v0.2.0/design/config-api.md b/docs/v0.2.0/design/config-api.md deleted file mode 100644 index 3696860dd54..00000000000 --- a/docs/v0.2.0/design/config-api.md +++ /dev/null @@ -1,350 +0,0 @@ -# Configuration API Design - -## Motivation - -[Issue 51][issue_51] specifies the need to design an API for configuring Envoy Gateway. The control plane is configured -statically at startup and the data plane is configured dynamically through Kubernetes resources, primarily -[Gateway API][gw_api] objects. Refer to the Envoy Gateway [design doc][design_doc] for additional details regarding -Envoy Gateway terminology and configuration. - -## Goals - -* Define an __initial__ API to configure Envoy Gateway at startup. -* Define an __initial__ API for configuring the managed data plane, e.g. Envoy proxies. - -## Non-Goals - -* Implementation of the configuration APIs. -* Define the `status` subresource of the configuration APIs. -* Define a __complete__ set of APIs for configuring Envoy Gateway. As stated in the [Goals](#goals), this document - defines the initial configuration APIs. -* Define an API for deploying/provisioning/operating Envoy Gateway. If needed, a future Envoy Gateway operator would be - responsible for designing and implementing this type of API. -* Specify tooling for managing the API, e.g. generate protos, CRDs, controller RBAC, etc. - -## Control Plane API - -The `EnvoyGateway` API defines the control plane configuration, e.g. Envoy Gateway. Key points of this API are: - -* It will define Envoy Gateway's startup configuration file. If the file does not exist, Envoy Gateway will start up - with default configuration parameters. -* EnvoyGateway inlines the `TypeMeta` API. This allows EnvoyGateway to be versioned and managed as a GroupVersionKind - scheme. -* EnvoyGateway does not contain a metadata field since it's currently represented as a static configuration file instead of - a Kubernetes resource. -* Since EnvoyGateway does not surface status, EnvoyGatewaySpec is inlined. -* If data plane static configuration is required in the future, Envoy Gateway will use a separate file for this purpose. - -The `v1alpha1` version and `config.gateway.envoyproxy.io` API group get generated: - -```go -// gateway/api/config/v1alpha1/doc.go - -// Package v1alpha1 contains API Schema definitions for the config.gateway.envoyproxy.io API group. -// -// +groupName=config.gateway.envoyproxy.io -package v1alpha1 -``` - -The initial `EnvoyGateway` API: - -```go -// gateway/api/config/v1alpha1/envoygateway.go - -package valpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EnvoyGateway is the Schema for the envoygateways API -type EnvoyGateway struct { - metav1.TypeMeta `json:",inline"` - - // EnvoyGatewaySpec defines the desired state of Envoy Gateway. - EnvoyGatewaySpec `json:",inline"` -} - -// EnvoyGatewaySpec defines the desired state of Envoy Gateway configuration. -type EnvoyGatewaySpec struct { - // Gateway defines Gateway-API specific configuration. If unset, default - // configuration parameters will apply. - // - // +optional - Gateway *Gateway `json:"gateway,omitempty"` - - // Provider defines the desired provider configuration. If unspecified, - // the Kubernetes provider is used with default parameters. - // - // +optional - Provider *Provider `json:"provider,omitempty"` -} - -// Gateway defines desired Gateway API configuration of Envoy Gateway. -type Gateway struct { - // ControllerName defines the name of the Gateway API controller. If unspecified, - // defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following - // for additional details: - // - // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass - // - // +optional - ControllerName string `json:"controllerName,omitempty"` -} - -// Provider defines the desired configuration of a provider. -// +union -type Provider struct { - // Type is the type of provider to use. If unset, the Kubernetes provider is used. - // - // +unionDiscriminator - Type ProviderType `json:"type,omitempty"` - // Kubernetes defines the configuration of the Kubernetes provider. Kubernetes - // provides runtime configuration via the Kubernetes API. - // - // +optional - Kubernetes *KubernetesProvider `json:"kubernetes,omitempty"` - - // File defines the configuration of the File provider. File provides runtime - // configuration defined by one or more files. - // - // +optional - File *FileProvider `json:"file,omitempty"` -} - -// ProviderType defines the types of providers supported by Envoy Gateway. -type ProviderType string - -const ( - // KubernetesProviderType defines the "Kubernetes" provider. - KubernetesProviderType ProviderType = "Kubernetes" - - // FileProviderType defines the "File" provider. - FileProviderType ProviderType = "File" -) - -// KubernetesProvider defines configuration for the Kubernetes provider. -type KubernetesProvider struct { - // TODO: Add config as use cases are better understood. -} - -// FileProvider defines configuration for the File provider. -type FileProvider struct { - // TODO: Add config as use cases are better understood. -} -``` - -__Note:__ Provider-specific configuration is defined in the `{$PROVIDER_NAME}Provider` API. - -### Gateway - -Gateway defines desired configuration of [Gateway API][gw_api] controllers that reconcile and translate Gateway API -resources into the Intermediate Representation (IR). Refer to the Envoy Gateway [design doc][design_doc] for additional -details. - -### Provider - -Provider defines the desired configuration of an Envoy Gateway provider. A provider is an infrastructure component that -Envoy Gateway calls to establish its runtime configuration. Provider is a [union type][union]. Therefore, Envoy Gateway -can be configured with only one provider based on the `type` discriminator field. Refer to the Envoy Gateway -[design doc][design_doc] for additional details. - -### Control Plane Configuration - -The configuration file is defined by the EnvoyGateway API type. At startup, Envoy Gateway searches for the configuration -at "/etc/envoy-gateway/config.yaml". - -Start Envoy Gateway: - -```shell -$ ./envoy-gateway -``` - -Since the configuration file does not exist, Envoy Gateway will start with default configuration parameters. - -The Kubernetes provider can be configured explicitly using `provider.kubernetes`: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: {} -EOF -``` - -This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. - -The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: - foo: bar -EOF -``` - -__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for -illustration purposes only. - -The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the -File provider: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: File - file: - foo: bar -EOF -``` - -__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration -purposes only. - -Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use -default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to -manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -gateway: - controllerName: foo -``` - -With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: - -```shell -$ ./envoy-gateway -``` - -## Data Plane API - -The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. -Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through -`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure -configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: - -* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane - infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. -* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: - > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the - > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are - > not propagated down to existing Gateways. - -The initial `EnvoyProxy` API: - -```go -// gateway/api/config/v1alpha1/envoyproxy.go - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EnvoyProxy is the Schema for the envoyproxies API. -type EnvoyProxy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EnvoyProxySpec `json:"spec,omitempty"` - Status EnvoyProxyStatus `json:"status,omitempty"` -} - -// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure -// configuration. -type EnvoyProxySpec struct { - // Undefined by this design spec. -} - -// EnvoyProxyStatus defines the observed state of EnvoyProxy. -type EnvoyProxyStatus struct { - // Undefined by this design spec. -} -``` - -The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use -cases are better understood. - -### Data Plane Configuration - -GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is -running with the Kubernetes provider. - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 -``` - -Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration -parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening -on port 80. - -The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer -service: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parametersRef: - name: example-config - group: config.gateway.envoyproxy.io - kind: EnvoyProxy ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: example-config -spec: - networkPublishing: - type: ClusterIPService -``` - -__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. - -[issue_51]: https://github.com/envoyproxy/gateway/issues/51 -[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md -[gw_api]: https://gateway-api.sigs.k8s.io/ -[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/docs/v0.2.0/design/gatewayapi-translator.md b/docs/v0.2.0/design/gatewayapi-translator.md deleted file mode 100644 index 3cf0da94f5a..00000000000 --- a/docs/v0.2.0/design/gatewayapi-translator.md +++ /dev/null @@ -1,250 +0,0 @@ -# Gateway API Translator Design - -The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate -Representation (IR). - -## Assumptions - -Initially target core conformance features only, to be followed by extended conformance features. - -## Inputs and Outputs - -The main inputs to the Gateway API translator are: - -- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. - -__Note:__ ReferenceGrant is not fully implemented as of v0.2. - -The outputs of the Gateway API translator are: - -- Xds and Infra Internal Representations (IRs). -- Status updates for GatewayClass, Gateways, HTTPRoutes - -## Listener Compatibility - -Envoy Gateway follows Gateway API listener compatibility spec: -> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. - -__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. - -### Listener Compatibility Examples - -#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -## Computing Status - -Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway -must compute the appropriate status fields and conditions for managed resources. - -Status is computed and set for: - -- The managed GatewayClass (`gatewayclass.status.conditions`). -- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the - Envoy Deployment and Service status are also included to calculate Gateway status. -- Listeners for each Gateway (`gateway.status.listeners`). -- The ParentRef for each Route (`route.status.parents`). - -The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to -the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and -updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to -update resource status on the Kubernetes API server. - -## Outline - -The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway -API resources. - -1. Process Gateway Listeners - - Validate unique hostnames, ports, and protocols. - - Validate and compute supported kinds. - - Validate allowed namespaces (validate selector if specified). - - Validate TLS fields if specified, including resolving referenced Secrets. - -2. Process HTTPRoutes - - foreach route rule: - - compute matches - - [core] path exact, path prefix - - [core] header exact - - [extended] query param exact - - [extended] HTTP method - - compute filters - - [core] request header modifier (set/add/remove) - - [core] request redirect (hostname, statuscode) - - [extended] request mirror - - compute backends - - [core] Kubernetes services - - foreach route parent ref: - - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) - - foreach matching listener: - - foreach hostname intersection with route: - - add each computed route rule to host - -## Context Structs - -To help store, access and manipulate information as it's processed during the translation process, a set of context -structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support -processing. - -`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. - -```go -type GatewayContext struct { - // The managed Gateway - *v1beta1.Gateway - - // A list of Gateway ListenerContexts. - listeners []*ListenerContext -} -``` - -`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on -the associated Gateway. - -```go -type ListenerContext struct { - // The Gateway listener. - *v1beta1.Listener - - // The Gateway this Listener belongs to. - gateway *v1beta1.Gateway - - // An index used for managing this listener in the list of Gateway listeners. - listenerStatusIdx int - - // Only Routes in namespaces selected by the selector may be attached - // to the Gateway this listener belongs to. - namespaceSelector labels.Selector - - // The TLS Secret for this Listener, if applicable. - tlsSecret *v1.Secret -} -``` - -`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. - -```go -type RouteContext interface { - client.Object - - // GetRouteType returns the Kind of the Route object, HTTPRoute, - // TLSRoute, TCPRoute, UDPRoute etc. - GetRouteType() string - - // GetHostnames returns the hosts targeted by the Route object. - GetHostnames() []string - - // GetParentReferences returns the ParentReference of the Route object. - GetParentReferences() []v1beta1.ParentReference - - // GetRouteParentContext returns RouteParentContext by using the Route - // objects' ParentReference. - GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext -} -``` - -[message bus]: watching.md diff --git a/docs/v0.2.0/design/roadmap.md b/docs/v0.2.0/design/roadmap.md deleted file mode 100644 index d6ec649e4a2..00000000000 --- a/docs/v0.2.0/design/roadmap.md +++ /dev/null @@ -1,60 +0,0 @@ -# Roadmap - -This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of -the project. - -## Contributing to the Roadmap - -- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use - case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update - this document accordingly. -- To help with an existing roadmap item, comment on or assign yourself to the associated issue. -- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A - maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be - discussed in an issue or a community meeting before implementing it. - -If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. -Look for issues with the `help wanted` label to get started. - -## Details - -Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific -roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by -contributing to the project. - -`Last Updated: October 2022` - -### [v0.2.0][v0.2.0]: Establish a Solid Foundation - -- Complete the core Envoy Gateway implementation- [Issue #60][60]. -- Establish initial testing, e2e, integration, etc- [Issue #64][64]. -- Establish user and developer project documentation- [Issue #17][17]. -- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. -- Setup a CI/CD pipeline- [Issue #63][63]. - -### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms - -- Global Rate Limiting -- AuthN/AuthZ- [Issue #336][336]. -- Lets Encrypt Integration - -### [v0.4.0][v0.4.0]: Manageability and Scale - -- Tooling for devs/infra admins to aid in managing/maintaining EG -- Support advanced provisioning use cases (e.g. multi-cluster, serverless, etc.) -- Perf testing (EG specifically) -- Support for Chaos engineering? - -[issue]: https://github.com/envoyproxy/gateway/issues -[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing -[pr]: https://github.com/envoyproxy/gateway/compare -[milestones]: https://github.com/envoyproxy/gateway/milestones -[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 -[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 -[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 -[60]: https://github.com/envoyproxy/gateway/issues/60 -[64]: https://github.com/envoyproxy/gateway/issues/64 -[17]: https://github.com/envoyproxy/gateway/issues/17 -[65]: https://github.com/envoyproxy/gateway/issues/65 -[63]: https://github.com/envoyproxy/gateway/issues/63 -[336]: https://github.com/envoyproxy/gateway/issues/336 diff --git a/docs/v0.2.0/design/system-design.md b/docs/v0.2.0/design/system-design.md deleted file mode 100644 index 731cb0925b0..00000000000 --- a/docs/v0.2.0/design/system-design.md +++ /dev/null @@ -1,171 +0,0 @@ -# System Design - -## Goals - -* Define the system components needed to satisfy the requirements of Envoy Gateway. - -## Non-Goals - -* Create a detailed design and interface specification for each system component. - -## Terminology - -* Control Plane- A collection of inter-related software components for providing application gateway and routing - functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. - These services are detailed in the [components](#components) section. -* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. - -## Architecture - -![Architecture](../images/architecture.png) - -## Configuration - -Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through -Kubernetes resources, primarily [Gateway API][gw_api] objects. - -### Static Configuration - -Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, -configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the -configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. - -### Dynamic Configuration - -Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using -reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is -defined as Kubernetes resources that provide the following services: - -* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is - expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be - referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, - e.g. expose Envoy network endpoints using a NodePort service instead of a LoadBalancer service. -* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP - requests for "www.example.com" to a backend service running a web server. This configuration is expressed through - [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. - Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] - reference. - -## Components - -Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described -in the [Watching Components Design][wcd]. - -### Provider - -A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve -services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap -via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A -provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). - -#### Kubernetes Provider - -* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the - [dynamic configuration](#dynamic-configuration). -* Manages the data plane through Kubernetes API CRUD operations. -* Uses Kubernetes for Service discovery. -* Uses etcd (via Kubernetes API) to persist data. - -#### File Provider - -* Uses a file watcher to watch files in a directory that define the data plane configuration. -* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. -* Uses the host's DNS for Service discovery. -* If needed, the local filesystem is used to persist data. - -### Resource Watcher - -The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The -mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes -provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator -as output. - -### Resource Translator - -The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate -Representation (IR). It is responsible for: - -* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. -* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. - -__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. - -### Intermediate Representation (IR) - -The Intermediate Representation defines internal data models that external resources are translated into. This allows -Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR -used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. - -* Infra IR- Used as the internal definition of the managed data plane infrastructure. -* xDS IR- Used as the internal definition of the managed data plane xDS configuration. - -### xDS Translator - -The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. - -### xDS Server - -The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server -Protocol and is responsible for using xDS to configure the data plane. - -### Infra Manager - -The Infra Manager is a provider-specific component responsible for managing the following infrastructure: - -* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, - Service, etc. resources to run Envoy in a Kubernetes cluster. -* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require - external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning - and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to - users through the [Custom Route Filters][crf] extension. - -The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. - -## Design Decisions - -* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with - `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy - Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. - `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy - infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. -* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. - * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. - * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners - in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are - met: - * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies - either the “HTTPS” or “TLS” Protocol. - * Each Listener within the group specifies a unique "Hostname". - * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no - other Listener matches. - * Envoy Gateway does __not__ merge listeners across multiple Gateways. -* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. - * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. -* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. - * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. -* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. - -The draft for this document is [here][draft_design]. - -[gw_api]: https://gateway-api.sigs.k8s.io -[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass -[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway -[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute -[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute -[go_cp]: https://github.com/envoyproxy/go-control-plane -[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting -[rls]: https://github.com/envoyproxy/ratelimit -[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit -[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional -[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts -[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners -[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route -[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional -[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster -[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference -[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ -[ wcd ]: ./watching.md -[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 -[roadmap]: roadmap.md diff --git a/docs/v0.2.0/design/watching.md b/docs/v0.2.0/design/watching.md deleted file mode 100644 index b8477a30e2d..00000000000 --- a/docs/v0.2.0/design/watching.md +++ /dev/null @@ -1,117 +0,0 @@ -# Watching Components Design - -Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch -external resources, and "publish" what they see for other components to consume; others watch what another publishes and -act on it (such as the resource translator watches what the providers publish, and then publishes its own results that -are watched by another component). Some of these internally published results are consumed by multiple components. - -To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the -standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub -pattern. - -## Pub - -Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" -tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if -we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by -the IR translator: - - ```go - type ResourceTable struct { - // gateway classes are cluster-scoped; no namespace - GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] - - // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. - Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] - - HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] - } - ``` - -The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; -updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` -method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher -doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just -`.Store` its data and `watchable` will do the right thing. - -## Sub - -Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or -`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: - - ```go - func(ctx context.Context) error { - for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { - fullState := irInput{ - GatewayClasses: k8sTable.GatewayClasses.LoadAll(), - Gateways: k8sTable.Gateways.LoadAll(), - HTTPRoutes: snapshot.State, - } - translate(irInput) - } - } - ``` - -Or, to watch multiple maps in the same loop: - - ```go - func worker(ctx context.Context) error { - classCh := k8sTable.GatewayClasses.Subscribe(ctx) - gwCh := k8sTable.Gateways.Subscribe(ctx) - routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) - for ctx.Err() == nil { - var arg irInput - select { - case snapshot := <-classCh: - arg.GatewayClasses = snapshot.State - case snapshot := <-gwCh: - arg.Gateways = snapshot.State - case snapshot := <-routeCh: - arg.Routes = snapshot.State - } - if arg.GateWayClasses == nil { - arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() - } - if arg.GateWays == nil { - arg.Gateways = k8sTable.GateWays.LoadAll() - } - if arg.HTTPRoutes == nil { - arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() - } - translate(irInput) - } - } - ``` - -From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; -but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a -handy way to know when to run, `.Load` and friends can be used without subscribing. - -There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but -it's probably wise to just have one publisher for each map. - -The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when -`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple -mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in -to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of -each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need -to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. - -If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` -entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you -must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to -help with this. - -## Other Notes - -The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the -map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb -`chan`. - -A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types -be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and -so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can -generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate -methods for. - -[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/docs/v0.2.0/design_docs.rst b/docs/v0.2.0/design_docs.rst deleted file mode 100644 index 4e95a518d1e..00000000000 --- a/docs/v0.2.0/design_docs.rst +++ /dev/null @@ -1,12 +0,0 @@ -Design Docs -=========== - -Learn about the internal details of Envoy Gateway. - -.. toctree:: - :maxdepth: 2 - - design/system-design - design/gatewayapi-translator - design/watching - design/config-api diff --git a/docs/v0.2.0/dev/CODEOWNERS.md b/docs/v0.2.0/dev/CODEOWNERS.md deleted file mode 100644 index d4229b6b23f..00000000000 --- a/docs/v0.2.0/dev/CODEOWNERS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Maintainers - -## The following maintainers, listed in alphabetical order, own everything - -- @AliceProxy -- @arkodg -- @skriss -- @Xunzhuo -- @youngnick -- @zirain - -## Emeritus Maintainers - -- @danehans -- @alexgervais diff --git a/docs/v0.2.0/dev/CODE_OF_CONDUCT.md b/docs/v0.2.0/dev/CODE_OF_CONDUCT.md deleted file mode 100644 index a0a295770f3..00000000000 --- a/docs/v0.2.0/dev/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,3 +0,0 @@ -# Community Code of Conduct - -Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/docs/v0.2.0/dev/CONTRIBUTING.md b/docs/v0.2.0/dev/CONTRIBUTING.md deleted file mode 100644 index d7770bdeff2..00000000000 --- a/docs/v0.2.0/dev/CONTRIBUTING.md +++ /dev/null @@ -1,183 +0,0 @@ -# Contributing - -We welcome contributions from the community. Please carefully review the [project goals](GOALS.md) -and following guidelines to streamline your contributions. - -## Communication - -* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no - one else is working on it and ask you to open a GitHub issue. -* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or - changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to - agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process - for major features is also important so that [affiliations with commit access](CODEOWNERS.md) can - come to agreement on the design. If it's appropriate to write a design document, the document must - be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable - location. -* Small patches and bug fixes don't need prior communication. - -## Inclusivity - -The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere -to the following guidelines for all code, APIs, and documentation: - -* The following words and phrases are not allowed: - * *Whitelist*: use allowlist instead. - * *Blacklist*: use denylist or blocklist instead. - * *Master*: use primary instead. - * *Slave*: use secondary or replica instead. -* Documentation should be written in an inclusive style. The [Google developer - documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent - reference on this topic. -* The above policy is not considered definitive and may be amended in the future as industry best - practices evolve. Additional comments on this topic may be provided by maintainers during code - review. - -## Submitting a PR - -* Fork the repo. -* Hack -* DCO sign-off each commit. This can be done with `git commit -s`. -* Submit your PR. -* Tests will automatically run for you. -* We will **not** merge any PR that is not passing tests. -* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage - build. If your PR cannot have 100% coverage for some reason please clearly explain why when you - open it. -* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/docs) folder of the repo as - well as the [changelog](../releases). -* All code comments and documentation are expected to have proper English grammar and punctuation. - If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try - to find some help but there are no guarantees. -* Your PR title should be descriptive, and generally start with a subsystem name followed by a - colon. Examples: - * "docs: fix grammar error" - * "translator: add new feature" -* Your PR commit message will be used as the commit message when your PR is merged. You should - update this field if your PR diverges during review. -* Your PR description should have details on what the PR does. If it fixes an existing issue it - should end with "Fixes #XXX". -* If your PR is co-authored or based on an earlier PR from another contributor, - please attribute them with `Co-authored-by: name `. See - GitHub's [multiple author - guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) - for further details. -* When all tests are passing and all other conditions described herein are satisfied, a maintainer - will be assigned to review and merge the PR. -* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits - are new commits and/or merges. We squash and merge so the number of commits you have in the PR - doesn't matter. -* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. - We reserve the right to close PRs that are not making progress. This is generally defined as no - changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. - Closing stale PRs helps us to keep on top of all the work currently in flight. - -## Maintainer PR Review Policy - -* See [CODEOWNERS.md](CODEOWNERS.md) for the current list of maintainers. -* A maintainer representing a different affiliation from the PR owner is required to review and - approve the PR. -* When the project matures, it is expected that a "domain expert" for the code the PR touches should - review the PR. This person does not require commit access, just domain knowledge. -* The above rules may be waived for PRs which only update docs or comments, or trivial changes to - tests and tools (where trivial is decided by the maintainer in question). -* If there is a question on who should review a PR please discuss in Slack. -* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. -* Please make sure that the PR title, commit message, and description are updated if the PR changes - significantly during review. -* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge - title with the original title, and the commit body with every individual commit from the PR. - The maintainer doing the merge should make sure the title follows the guidelines above and should - overwrite the body with the original commit message from the PR (cleaning it up if necessary) - while preserving the PR author's final DCO sign-off. - -## Decision making - -This is a new and complex project, and we need to make a lot of decisions very quickly. -To this end, we've settled on this process for making (possibly contentious) decisions: - -* For decisions that need a record, we create an issue. -* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. -* Maintainers can cast binding votes on that comment by reacting or replying in another comment. -* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. -* Voting will be resolved by simple majority. -* In the event of deadlocks, the question will be put to steering instead. - -## DCO: Sign your work - -The sign-off is a simple line at the end of the explanation for the -patch, which certifies that you wrote it or otherwise have the right to -pass it on as an open-source patch. The rules are pretty simple: if you -can certify the below (from -[developercertificate.org](https://developercertificate.org/)): - -``` -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -using your real name (sorry, no pseudonyms or anonymous contributions.) - -You can add the sign-off when creating the git commit via `git commit -s`. - -If you want this to be automatic you can set up some aliases: - -```bash -git config --add alias.amend "commit -s --amend" -git config --add alias.c "commit -s" -``` - -## Fixing DCO - -If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best -practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) -the commit history to a single commit, append the DCO sign-off as described above, and [force -push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in -your history: - -```bash -git rebase -i HEAD^^ -(interactive squash + DCO append) -git push origin -f -``` - -Note, that in general rewriting history in this way is a hindrance to the review process and this -should only be done to correct a DCO mistake. diff --git a/docs/v0.2.0/dev/DOCS.md b/docs/v0.2.0/dev/DOCS.md deleted file mode 100644 index fb49b9d55dd..00000000000 --- a/docs/v0.2.0/dev/DOCS.md +++ /dev/null @@ -1,63 +0,0 @@ -# Working on the Envoy Gateway Docs - -The documentation for the Envoy Gateway lives in the `docs/` directory. Any -individual document can be written using either [reStructuredText] or [Markdown], -you can choose the format that you're most comfortable with when working on the -documentation. - -## Documentation Structure - -We supported the versioned Docs now, the directory name under docs represents -the version of docs. The root of the latest site is in `docs/latest/index.rst`. -This is probably where to start if you're trying to understand how things fit together. - -Note that the new contents should be added to `docs/latest` and will be cut off at -the next release. The contents under `docs/v0.2.0` are auto-generated, -and usually do not need to make changes to them, unless if you find the current release pages have -some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` -and `docs/v0.2.0`. - -It's important to note that a given document _must_ have a reference in some -`.. toctree::` section for the document to be reachable. Not everything needs -to be in `docs/index.rst`'s `toctree` though. - -You can access the website which represents the current release in default, -and you can access the website which contains the latest version changes in -[Here][latest-website] or at the footer of the pages. - -## Documentation Workflow - -To work with the docs, just edit reStructuredText or Markdown files in `docs`, -then run - -```bash -make docs -``` - -This will create `docs/html` with the built HTML pages. You can view the docs -either simply by pointing a web browser at the `file://` path to your -`docs/html`, or by firing up a static webserver from that directory, e.g. - -``` shell -make docs-serve -``` - -If you want to generate a new release version of the docs, like `v0.3.0`, then run - -```bash -make docs-release TAG=v0.3.0 -``` - -This will update the VERSION file at the project root, which records current release version, -and it will be used in the pages version context and binary version output. Also, this will generate -new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` -in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. - -## Publishing Docs - -Whenever docs are pushed to `main`, CI will publish the built docs to GitHub -Pages. For more details, see `.github/workflows/docs.yaml`. - -[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -[Markdown]: https://daringfireball.net/projects/markdown/syntax -[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/docs/v0.2.0/dev/GOALS.md b/docs/v0.2.0/dev/GOALS.md deleted file mode 120000 index f2174592394..00000000000 --- a/docs/v0.2.0/dev/GOALS.md +++ /dev/null @@ -1 +0,0 @@ -../../../GOALS.md \ No newline at end of file diff --git a/docs/v0.2.0/dev/README.md b/docs/v0.2.0/dev/README.md deleted file mode 100644 index 4f94c06bb37..00000000000 --- a/docs/v0.2.0/dev/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# Developer Guide - -Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. - -## Prerequisites - -### go - -* Version: 1.18.2 -* Installation Guide: https://go.dev/doc/install - -### make - -* Recommended Version: 4.0 or later -* Installation Guide: https://www.gnu.org/software/make - -### docker - -* Optional when you want to build a Docker image or run `make` inside Docker. -* Recommended Version: 20.10.16 -* Installation Guide: https://docs.docker.com/engine/install - -### python3 - -* Need a `python3` program -* Must have a functioning `venv` module; this is part of the standard - library, but some distributions (such as Debian and Ubuntu) replace - it with a stub and require you to install a `python3-venv` package - separately. - -## Quickstart - -* Run `make help` to see all the available targets to build, test and run Envoy Gateway. - -### Building - -* Run `make build` to build the Envoy Gateway binary. __Note:__ The binary gets generated in the `bin/` directory - -### Testing - -* Run `make test` to run the golang tests. - -### Running Linters - -* Run `make lint` to make sure your code passes all the linter checks. - -### Building and Pushing the Image - -* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. -* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. - -__Note:__ Replace `IMAGE` with your registry's image name. - -### Deploying Envoy Gateway for Test/Dev - -* Run `make create-cluster` to create a [Kind][] cluster. - -#### Option 1: Use the Latest [gateway-dev][] Image - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` - to use a different image tag. - -#### Option 2: Use a Custom Image - -* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. -* Run `make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. - -### Deploying Envoy Gateway in Kubernetes - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to - the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or - tag. -* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. - -__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. - -### Demo Setup - -* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource -(similar to steps outlined in the [Quickstart][] docs) and test the configuration. -* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. - -### Run Gateway API Conformance Tests - -The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the -Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run -`TAG=latest make run-conformance` to run the conformance tests. - -#### On a Linux Host - -* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] - image, and run Gateway API conformance tests. - -#### On a Mac Host - -Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following -workarounds to run conformance tests: - -* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run - `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image - to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to - uninstall Envoy Gateway. -* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. - -__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` -is unspecified, the short SHA of your current branch is used. - -### Debugging the Envoy Config - -An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port -(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. - -Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: - -```shell -export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward the admin interface port: - -```shell -kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 -``` - -Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. - -There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. - -[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[make]: https://www.gnu.org/software/make/ -[Github Actions]: https://docs.github.com/en/actions -[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows -[Kind]: https://kind.sigs.k8s.io/ -[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ -[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ -[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ -[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags -[mac_connect]: https://github.com/chipmk/docker-mac-net-connect -[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface diff --git a/docs/v0.2.0/dev/releasing.md b/docs/v0.2.0/dev/releasing.md deleted file mode 100644 index 30bb5fe8c58..00000000000 --- a/docs/v0.2.0/dev/releasing.md +++ /dev/null @@ -1,142 +0,0 @@ -# Release Process - -This document guides maintainers through the process of creating an Envoy Gateway release. - -## Creating a Minor Release - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch to create the release notes and release docs. Reference previous [release notes][] for additional details. -3. Sign, commit, and push your changes to your fork and submit a [Pull Request][] to merge the changes listed below - into the `main` branch. Do not proceed until all your PRs have merged and the [Build and Test][build-and-test GitHub action] has completed for your final PR: - - 1. Add Release Announcement. - 2. Add Release Versioned Documents. - - ``` shell - make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -4. Create a new release branch from `main`. The release branch should be named - `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -5. Push the branch to the Envoy Gateway repo. - - ```shell - git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. Tag the head of your release branch with the release tag. For example: - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' - ``` - - __Note:__ The tag version differs from the release branch by including the `.0` patch version. - -7. Push the tag to the Envoy Gateway repository. - - ```shell - git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -8. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -9. Confirm that the [release workflow][] completed successfully. -10. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -11. Confirm that the [release][] was created. -12. Confirm that the steps in the [Quickstart Guide][] work as expected. -13. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: - - ```console - # Release Announcement - - Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] - (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. - ``` - -If you find any bugs in this process, please create an issue. - -## Creating a Release Candidate - -### RC Prerequisites - -- Permissions to push to the Envoy Gateway repository. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export RELEASE_CANDIDATE_NUMBER=1 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Tag the head of the main branch with the release candidate number. - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' - ``` - -3. Push the tag to the Envoy Gateway repository. - - ```shell - git push v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} - ``` - -4. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -5. Confirm that the [release workflow][] completed successfully. -6. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -7. Confirm that the [release][] was created. -8. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test - the quickstart steps using the release candidate by manually updating the links. -9. [Generate][] the GitHub changelog. -10. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. -11. If you find any bugs in this process, please create an issue. - -## Announcing the Release - -It's important that the world knows about the release. Use the following steps to announce the release. - -1. Set the release information in the Envoy Gateway Slack channel. For example: - - ```shell - Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -2. Send a message to the Envoy Gateway Slack channel. For example: - - ```shell - On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway - v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. - Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs - to start using Envoy Gateway. - ... - ``` - - Link to the GitHub release and release announcement page that highlights the release. - -[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes -[Pull Request]: https://github.com/envoyproxy/gateway/pulls -[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[build-and-test GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml -[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml -[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml -[image]: https://hub.docker.com/r/envoyproxy/gateway/tags -[release]: https://github.com/envoyproxy/gateway/releases -[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes diff --git a/docs/v0.2.0/dev_docs.rst b/docs/v0.2.0/dev_docs.rst deleted file mode 100644 index e546e14c1ad..00000000000 --- a/docs/v0.2.0/dev_docs.rst +++ /dev/null @@ -1,15 +0,0 @@ -Developer Docs -============== - -Learn how to contribute to Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - dev/GOALS - dev/CODE_OF_CONDUCT - dev/CODEOWNERS - dev/CONTRIBUTING - dev/README - dev/DOCS - dev/releasing diff --git a/docs/v0.2.0/get_involved.rst b/docs/v0.2.0/get_involved.rst deleted file mode 100644 index f17febd5651..00000000000 --- a/docs/v0.2.0/get_involved.rst +++ /dev/null @@ -1,9 +0,0 @@ -Getting Involved -================ - -We welcome contributions from the community. Please carefully review the -`project goals `_ -and the -`code of conduct `_ -before diving in. - diff --git a/docs/v0.2.0/images/architecture.png b/docs/v0.2.0/images/architecture.png deleted file mode 100644 index 1d4131fbea7..00000000000 Binary files a/docs/v0.2.0/images/architecture.png and /dev/null differ diff --git a/docs/v0.2.0/index.rst b/docs/v0.2.0/index.rst deleted file mode 100644 index 7da20035f47..00000000000 --- a/docs/v0.2.0/index.rst +++ /dev/null @@ -1,32 +0,0 @@ -`Envoy Gateway `_ -==================== - -Release: |version| - -.. image:: https://img.shields.io/badge/slack-join-orange.svg - :target: https://envoyproxy.slack.com/archives/C03E6NHLESV - :alt: Join the Envoy Slack - -Envoy Gateway is an open source project for managing `Envoy Proxy`_ as a standalone or Kubernetes-based application -gateway. `Gateway API`_ resources are used to dynamically provision and configure the managed Envoy Proxies. Whether -you are interested in using or contributing to Envoy Gateway, the following resources will help you get started: - -.. toctree:: - :maxdepth: 1 - - intro/compatibility - user_docs - design_docs - dev_docs - releases - roadmap - about_docs - get_involved - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved`. - -.. _Envoy Proxy: https://www.envoyproxy.io/ -.. _Gateway API: https://gateway-api.sigs.k8s.io/ diff --git a/docs/v0.2.0/intro/compatibility.rst b/docs/v0.2.0/intro/compatibility.rst deleted file mode 100644 index 4dc06f769e5..00000000000 --- a/docs/v0.2.0/intro/compatibility.rst +++ /dev/null @@ -1,19 +0,0 @@ -Compatibility Matrix -==================== - -Envoy Gateway relies on the Envoy Proxy and the Gateway API, and runs -within a Kubernetes cluster. Not all versions of each of these products -can function together for Envoy Gateway. Supported version combinations -are listed below; **bold** type indicates the versions of the Envoy Proxy -and the Gateway API actually compiled into each Envoy Gateway release. - -+--------------------------+---------------------+---------------------+----------------------------+ -| Envoy Gateway version | Envoy Proxy version | Gateway API version | Kubernetes minimum version | -+--------------------------+---------------------+---------------------+----------------------------+ -| v0.2.0 | **v1.23-latest** | **v0.5.1** | v1.24 | -+--------------------------+---------------------+---------------------+----------------------------+ - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved<../get_involved>`. diff --git a/docs/v0.2.0/releases.rst b/docs/v0.2.0/releases.rst deleted file mode 100644 index 090c6707fd2..00000000000 --- a/docs/v0.2.0/releases.rst +++ /dev/null @@ -1,10 +0,0 @@ -Releases -======== - -Learn more about Envoy Gateway releases. - -.. toctree:: - :maxdepth: 1 - - releases/README - releases/v0.2 diff --git a/docs/v0.2.0/releases/README.md b/docs/v0.2.0/releases/README.md deleted file mode 100644 index 93d3366efb0..00000000000 --- a/docs/v0.2.0/releases/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Release Details - -This document provides details for Envoy Gateway releases. Envoy Gateway follows the Semantic Versioning [v2.0.0 spec][] -for release versioning. Since Envoy Gateway is a new project, minor releases are the only defined releases. Envoy -Gateway maintainers will establish additional release details, e.g. patch releases, at a future date. - -## Stable Releases - -Stable releases of Envoy Gateway include: - -* Minor Releases- A new release branch and corresponding tag are created from the `main` branch. A minor release - is supported for 6 months following the release date. As the project matures, Envoy Gateway maintainers will reassess - the support timeframe. - -Minor releases happen quarterly and follow the schedule below. - -## Release Management - -Minor releases are handled by a designated Envoy Gateway maintainer. This maintainer is considered the Release Manager -for the release. The details for creating a release are outlined in the [release guide][]. The Release Manager is -responsible for coordinating the overall release. This includes identifying issues to be fixed in the release, -communications with the Envoy Gateway community, and the mechanics of the release. - -| Quarter | Release Manager | -|:-------:|:--------------------------------------------------------------:| -| 2022 Q4 | Daneyon Hansen ([danehans](https://github.com/danehans)) | -| 2023 Q1 | TBD | - -## Release Schedule - -In order to align with the Envoy Proxy [release schedule][], Envoy Gateway releases are produced on a fixed schedule -(the 22nd day of each quarter), with an acceptable delay of up to 2 weeks, and a hard deadline of 3 weeks. - -| Version | Expected | Actual | Difference | End of Life | -|:-------:|:-----------:|:-----------:|:----------:|:-----------:| -| 0.2.0 | 2022/10/22 | 2022/10/20 | -2 day | 2023/4/20 | -| 0.3.0 | 2023/01/22 | | | | - -[v2.0.0 spec]: https://semver.org/spec/v2.0.0.html -[release guide]: ../dev/releasing.md -[release schedule]: https://github.com/envoyproxy/envoy/blob/main/RELEASES.md#major-release-schedule diff --git a/docs/v0.2.0/releases/v0.2.md b/docs/v0.2.0/releases/v0.2.md deleted file mode 100644 index a0dc0e885de..00000000000 --- a/docs/v0.2.0/releases/v0.2.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.2 -linktitle: v0.2 -subtitle: Major Update -description: Envoy Gateway v0.2 release announcement. -publishdate: 2022-10-20 -release: v0.2.0 -skip_list: true -aliases: -- /releases/v0.2 -- /releases/v0.2.0 ---- -# Envoy Gateway Release v0.2 - -We are pleased to announce the release of Envoy Gateway v0.2! - -This is the first functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Kubernetes Support - -Run Envoy Gateway in a Kubernetes cluster. Checkout the [quickstart guide][] to get started with Envoy Gateway in a few -simple steps. - -### Gateway API Support - -Envoy Gateway supports Gateway API resources for running and configuring a managed fleet of Envoy proxies. Envoy Gateway -passes Gateway API core [conformance tests][] and supports GatewayClass, Gateway, HTTPRoute, and TLSRoute resources. See -the [documentation][docs] for additional details on how to use Envoy Gateway for your edge proxy and API gateway needs. - -## Envoy Gateway at EnvoyCon NA - -Envoy Gateway will be at [EnvoyCon NA][] this October in Detroit. Don't miss [our talk][] to learn more about the -release and future direction of the project. - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.2.0.yaml -[matrix]: https://gateway.envoyproxy.io/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.2.0 -[conformance tests]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=conformance -[quickstart guide]: https://gateway.envoyproxy.io/user/quickstart.html -[EnvoyCon NA]: https://events.linuxfoundation.org/envoycon-north-america/program/schedule/ -[our talk]: https://sched.co/1AO5S diff --git a/docs/v0.2.0/roadmap.rst b/docs/v0.2.0/roadmap.rst deleted file mode 100644 index 711b6245503..00000000000 --- a/docs/v0.2.0/roadmap.rst +++ /dev/null @@ -1,9 +0,0 @@ -Roadmap -======= - -Learn about the future direction of Envoy Gateway. - -.. toctree:: - :maxdepth: 2 - - design/roadmap diff --git a/docs/v0.2.0/user/http-redirect.md b/docs/v0.2.0/user/http-redirect.md deleted file mode 100644 index 5b6588535ea..00000000000 --- a/docs/v0.2.0/user/http-redirect.md +++ /dev/null @@ -1,127 +0,0 @@ -# HTTP Redirects - -The [HTTPRoute][] resource can issue redirects to clients or rewrite paths sent upstream using filters. Note that -HTTPRoute rules cannot use both filter types at once. Currently, Envoy Gateway only supports __core__ -[HTTPRoute filters][] which consist of `RequestRedirect` and `RequestHeaderModifier` at the time of this writing. To -learn more about HTTP routing, refer to the [Gateway API documentation][]. - -## Prerequisites - -Follow the steps from the [Secure Gateways](secure-gateways.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTPS. - -## Redirects - -Redirects return HTTP 3XX responses to a client, instructing it to retrieve a different resource. A -[`RequestRedirect` filter][req_filter] instructs Gateways to emit a redirect response to requests that match the rule. -For example, to issue a permanent redirect (301) from HTTP to HTTPS, configure `requestRedirect.statusCode=301` and -`requestRedirect.scheme="https"`: - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something", - "foo" - ], -... -``` - -## Setting Request Headers - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - "headers": { - "Accept": [ - "*/*" - ], - "Set-Header": [ - "foo" - ], -... -``` - -## Removing Request Headers - -Headers can be removed from a request by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something" - ], -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-79665566f5-s589f" -... -``` - -## Multiple backendRefs - -If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is -configured. - -First, create a second instance of the example app from the quickstart: - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-75bcd4c969-lsxpz" -... -``` - -## Weighted backendRefs - -If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` -field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is -assumed to be `1`. - -The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of -requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the -HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. - -The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the -backend-2 service. - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 500 Internal Server Error -< server: envoy -< content-length: 0 -< -``` - -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/docs/v0.2.0/user/quickstart.md b/docs/v0.2.0/user/quickstart.md deleted file mode 100644 index 5361ca187ea..00000000000 --- a/docs/v0.2.0/user/quickstart.md +++ /dev/null @@ -1,87 +0,0 @@ -# Quickstart - -This guide will help you get started with Envoy Gateway in a few simple steps. - -## Prerequisites - -A Kubernetes cluster. - -__Note:__ Refer to the [Compatibility Matrix](../intro/compatibility.rst) for supported Kubernetes versions. - -## Installation - -Install the Gateway API CRDs and Envoy Gateway: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/install.yaml -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -Install the GatewayClass, Gateway, HTTPRoute and example app: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/quickstart.yaml -``` - -## Testing the Configuration - -Get the name of the Envoy service created the by the example Gateway: - -```shell -export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward to the Envoy service: - -```shell -kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:8080 & -``` - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://localhost:8888/get -``` - -### External LoadBalancer Support - -You can also test the same functionality by sending traffic to the External IP. To get the external IP of the -Envoy service, run: - -```shell -export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -``` - -In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace -`ip` in the above command with `hostname`. - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST:8080/get -``` - -## Clean-Up - -Use the steps in this section to uninstall everything from the quickstart guide. - -Delete the GatewayClass, Gateway, HTTPRoute and Example App: - -```shell -kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/quickstart.yaml --ignore-not-found=true -``` - -Delete the Gateway API CRDs and Envoy Gateway: - -```shell -kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/install.yaml --ignore-not-found=true -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. diff --git a/docs/v0.2.0/user/secure-gateways.md b/docs/v0.2.0/user/secure-gateways.md deleted file mode 100644 index cd5199495b9..00000000000 --- a/docs/v0.2.0/user/secure-gateways.md +++ /dev/null @@ -1,256 +0,0 @@ -# Secure Gateways - -This guide will help you get started using secure Gateways. The guide uses a self-signed CA, so it should be used for -testing and demonstration purposes only. - -## Prerequisites - -- OpenSSL to generate TLS assets. - -## Installation - -Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## TLS Certificates - -Generate the certificates and keys used by the Gateway to terminate client TLS connections. - -Create a root certificate and private key to sign certificates: - -```shell -openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt -``` - -Create a certificate and a private key for `www.example.com`: - -```shell -openssl req -out www.example.com.csr -newkey rsa:2048 -nodes -keyout www.example.com.key -subj "/CN=www.example.com/O=example organization" -openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in www.example.com.csr -out www.example.com.crt -``` - -Store the cert/key in a Secret: - -```shell -kubectl create secret tls example-cert --key=www.example.com.key --cert=www.example.com.crt -``` - -Update the Gateway from the Quickstart guide to include an HTTPS listener that listens on port `8443` and references the -`example-cert` Secret: - -```shell -kubectl patch gateway eg --type=json --patch '[{ - "op": "add", - "path": "/spec/listeners/-", - "value": { - "name": "https", - "protocol": "HTTPS", - "port": 8443, - "tls": { - "mode": "Terminate", - "certificateRefs": [{ - "kind": "Secret", - "group": "", - "name": "example-cert", - }], - }, - }, -}]' -``` - -Verify the Gateway status: - -```shell -kubectl get gateway/eg -o yaml -``` - -## Testing - -### Clusters without External LoadBalancer Support - -Get the name of the Envoy service created the by the example Gateway: - -```shell -export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward to the Envoy service: - -```shell -kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8043:8443 & -``` - -Query the example app through Envoy proxy: - -```shell -curl -v -HHost:www.example.com --resolve "www.example.com:8043:127.0.0.1" \ ---cacert example.com.crt https://www.example.com:8043/get -``` - -### Clusters with External LoadBalancer Support - -Get the External IP of the Gateway: - -```shell -export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}') -``` - -Query the example app through the Gateway: - -```shell -curl -v -HHost:www.example.com --resolve "www.example.com:8443:${GATEWAY_HOST}" \ ---cacert example.com.crt https://www.example.com:8443/get -``` - -## Multiple HTTPS Listeners - -Create a TLS cert/key for the additional HTTPS listener: - -```shell -openssl req -out foo.example.com.csr -newkey rsa:2048 -nodes -keyout foo.example.com.key -subj "/CN=foo.example.com/O=example organization" -openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in foo.example.com.csr -out foo.example.com.crt -``` - -Store the cert/key in a Secret: - -```shell -kubectl create secret tls foo-cert --key=foo.example.com.key --cert=foo.example.com.crt -``` - -Create another HTTPS listener on the example Gateway: - -```shell -kubectl patch gateway eg --type=json --patch '[{ - "op": "add", - "path": "/spec/listeners/-", - "value": { - "name": "https-foo", - "protocol": "HTTPS", - "port": 8443, - "hostname": "foo.example.com", - "tls": { - "mode": "Terminate", - "certificateRefs": [{ - "kind": "Secret", - "group": "", - "name": "foo-cert", - }], - }, - }, -}]' -``` - -Update the HTTPRoute to route traffic for hostname `foo.example.com` to the example backend service: - -```shell -kubectl patch httproute backend --type=json --patch '[{ - "op": "add", - "path": "/spec/hostnames/-", - "value": "foo.example.com", -}]' -``` - -Verify the Gateway status: - -```shell -kubectl get gateway/eg -o yaml -``` - -Follow the steps in the [Testing section](#testing) to test connectivity to the backend app through both Gateway -listeners. Replace `www.example.com` with `foo.example.com` to test the new HTTPS listener. - -## Cross Namespace Certificate References - -A Gateway can be configured to reference a certificate in a different namespace. This is allowed by a [ReferenceGrant][] -created in the target namespace. Without the ReferenceGrant, a cross-namespace reference is invalid. - -Before proceeding, ensure you can query the HTTPS backend service from the [Testing section](#testing). - -To demonstrate cross namespace certificate references, create a ReferenceGrant that allows Gateways from the "default" -namespace to reference Secrets in the "envoy-gateway-system" namespace: - -```console -$ cat < - Extension APIs diff --git a/docs/v0.3.0/conf.py b/docs/v0.3.0/conf.py deleted file mode 100644 index 76ef5717548..00000000000 --- a/docs/v0.3.0/conf.py +++ /dev/null @@ -1,43 +0,0 @@ -import os - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.duration', - 'sphinx.ext.autosectionlabel', - 'myst_parser', -] - -autosectionlabel_prefix_document = True -myst_heading_anchors = 3 - -html_theme = 'alabaster' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -version = os.environ["BUILD_VERSION"] -envoyVersion = os.environ["ENVOY_PROXY_VERSION"] -gatewayAPIVersion = os.environ["GATEWAYAPI_VERSION"] - -project = 'Envoy Gateway' -author = 'Envoy Gateway Project Authors' - -copyright = 'Envoy Gateway Project Authors | GitHub | Latest Docs' - -source_suffix = { - '.rst': 'restructuredtext', - '.md': 'markdown', -} - -variables_to_export = [ - "version", - "envoyVersion", - "gatewayAPIVersion", -] - -frozen_locals = dict(locals()) -rst_epilog = '\n'.join(map(lambda x: f".. |{x}| replace:: {frozen_locals[x]}", variables_to_export)) -del frozen_locals diff --git a/docs/v0.3.0/design/config-api.md b/docs/v0.3.0/design/config-api.md deleted file mode 100644 index 3696860dd54..00000000000 --- a/docs/v0.3.0/design/config-api.md +++ /dev/null @@ -1,350 +0,0 @@ -# Configuration API Design - -## Motivation - -[Issue 51][issue_51] specifies the need to design an API for configuring Envoy Gateway. The control plane is configured -statically at startup and the data plane is configured dynamically through Kubernetes resources, primarily -[Gateway API][gw_api] objects. Refer to the Envoy Gateway [design doc][design_doc] for additional details regarding -Envoy Gateway terminology and configuration. - -## Goals - -* Define an __initial__ API to configure Envoy Gateway at startup. -* Define an __initial__ API for configuring the managed data plane, e.g. Envoy proxies. - -## Non-Goals - -* Implementation of the configuration APIs. -* Define the `status` subresource of the configuration APIs. -* Define a __complete__ set of APIs for configuring Envoy Gateway. As stated in the [Goals](#goals), this document - defines the initial configuration APIs. -* Define an API for deploying/provisioning/operating Envoy Gateway. If needed, a future Envoy Gateway operator would be - responsible for designing and implementing this type of API. -* Specify tooling for managing the API, e.g. generate protos, CRDs, controller RBAC, etc. - -## Control Plane API - -The `EnvoyGateway` API defines the control plane configuration, e.g. Envoy Gateway. Key points of this API are: - -* It will define Envoy Gateway's startup configuration file. If the file does not exist, Envoy Gateway will start up - with default configuration parameters. -* EnvoyGateway inlines the `TypeMeta` API. This allows EnvoyGateway to be versioned and managed as a GroupVersionKind - scheme. -* EnvoyGateway does not contain a metadata field since it's currently represented as a static configuration file instead of - a Kubernetes resource. -* Since EnvoyGateway does not surface status, EnvoyGatewaySpec is inlined. -* If data plane static configuration is required in the future, Envoy Gateway will use a separate file for this purpose. - -The `v1alpha1` version and `config.gateway.envoyproxy.io` API group get generated: - -```go -// gateway/api/config/v1alpha1/doc.go - -// Package v1alpha1 contains API Schema definitions for the config.gateway.envoyproxy.io API group. -// -// +groupName=config.gateway.envoyproxy.io -package v1alpha1 -``` - -The initial `EnvoyGateway` API: - -```go -// gateway/api/config/v1alpha1/envoygateway.go - -package valpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EnvoyGateway is the Schema for the envoygateways API -type EnvoyGateway struct { - metav1.TypeMeta `json:",inline"` - - // EnvoyGatewaySpec defines the desired state of Envoy Gateway. - EnvoyGatewaySpec `json:",inline"` -} - -// EnvoyGatewaySpec defines the desired state of Envoy Gateway configuration. -type EnvoyGatewaySpec struct { - // Gateway defines Gateway-API specific configuration. If unset, default - // configuration parameters will apply. - // - // +optional - Gateway *Gateway `json:"gateway,omitempty"` - - // Provider defines the desired provider configuration. If unspecified, - // the Kubernetes provider is used with default parameters. - // - // +optional - Provider *Provider `json:"provider,omitempty"` -} - -// Gateway defines desired Gateway API configuration of Envoy Gateway. -type Gateway struct { - // ControllerName defines the name of the Gateway API controller. If unspecified, - // defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following - // for additional details: - // - // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass - // - // +optional - ControllerName string `json:"controllerName,omitempty"` -} - -// Provider defines the desired configuration of a provider. -// +union -type Provider struct { - // Type is the type of provider to use. If unset, the Kubernetes provider is used. - // - // +unionDiscriminator - Type ProviderType `json:"type,omitempty"` - // Kubernetes defines the configuration of the Kubernetes provider. Kubernetes - // provides runtime configuration via the Kubernetes API. - // - // +optional - Kubernetes *KubernetesProvider `json:"kubernetes,omitempty"` - - // File defines the configuration of the File provider. File provides runtime - // configuration defined by one or more files. - // - // +optional - File *FileProvider `json:"file,omitempty"` -} - -// ProviderType defines the types of providers supported by Envoy Gateway. -type ProviderType string - -const ( - // KubernetesProviderType defines the "Kubernetes" provider. - KubernetesProviderType ProviderType = "Kubernetes" - - // FileProviderType defines the "File" provider. - FileProviderType ProviderType = "File" -) - -// KubernetesProvider defines configuration for the Kubernetes provider. -type KubernetesProvider struct { - // TODO: Add config as use cases are better understood. -} - -// FileProvider defines configuration for the File provider. -type FileProvider struct { - // TODO: Add config as use cases are better understood. -} -``` - -__Note:__ Provider-specific configuration is defined in the `{$PROVIDER_NAME}Provider` API. - -### Gateway - -Gateway defines desired configuration of [Gateway API][gw_api] controllers that reconcile and translate Gateway API -resources into the Intermediate Representation (IR). Refer to the Envoy Gateway [design doc][design_doc] for additional -details. - -### Provider - -Provider defines the desired configuration of an Envoy Gateway provider. A provider is an infrastructure component that -Envoy Gateway calls to establish its runtime configuration. Provider is a [union type][union]. Therefore, Envoy Gateway -can be configured with only one provider based on the `type` discriminator field. Refer to the Envoy Gateway -[design doc][design_doc] for additional details. - -### Control Plane Configuration - -The configuration file is defined by the EnvoyGateway API type. At startup, Envoy Gateway searches for the configuration -at "/etc/envoy-gateway/config.yaml". - -Start Envoy Gateway: - -```shell -$ ./envoy-gateway -``` - -Since the configuration file does not exist, Envoy Gateway will start with default configuration parameters. - -The Kubernetes provider can be configured explicitly using `provider.kubernetes`: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: {} -EOF -``` - -This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. - -The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: - foo: bar -EOF -``` - -__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for -illustration purposes only. - -The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the -File provider: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: File - file: - foo: bar -EOF -``` - -__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration -purposes only. - -Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use -default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to -manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -gateway: - controllerName: foo -``` - -With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: - -```shell -$ ./envoy-gateway -``` - -## Data Plane API - -The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. -Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through -`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure -configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: - -* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane - infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. -* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: - > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the - > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are - > not propagated down to existing Gateways. - -The initial `EnvoyProxy` API: - -```go -// gateway/api/config/v1alpha1/envoyproxy.go - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EnvoyProxy is the Schema for the envoyproxies API. -type EnvoyProxy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EnvoyProxySpec `json:"spec,omitempty"` - Status EnvoyProxyStatus `json:"status,omitempty"` -} - -// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure -// configuration. -type EnvoyProxySpec struct { - // Undefined by this design spec. -} - -// EnvoyProxyStatus defines the observed state of EnvoyProxy. -type EnvoyProxyStatus struct { - // Undefined by this design spec. -} -``` - -The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use -cases are better understood. - -### Data Plane Configuration - -GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is -running with the Kubernetes provider. - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 -``` - -Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration -parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening -on port 80. - -The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer -service: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parametersRef: - name: example-config - group: config.gateway.envoyproxy.io - kind: EnvoyProxy ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: example-config -spec: - networkPublishing: - type: ClusterIPService -``` - -__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. - -[issue_51]: https://github.com/envoyproxy/gateway/issues/51 -[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md -[gw_api]: https://gateway-api.sigs.k8s.io/ -[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/docs/v0.3.0/design/egctl.md b/docs/v0.3.0/design/egctl.md deleted file mode 100644 index 53a5e7b998a..00000000000 --- a/docs/v0.3.0/design/egctl.md +++ /dev/null @@ -1,56 +0,0 @@ -# Introduce egctl - -## Motivation - -EG should provide a command line tool with following capabilities: - -- Collect configuration from envoy proxy and gateway -- Analyse system configuration to diagnose any issues in envoy gateway - -This tool is named `egctl`. - -## Syntax - -Use the following syntax to run `egctl` commands from your terminal window: - -```console -egctl [command] [entity] [name] [flags] -``` - -where `command`, `name`, and `flags` are: - -* `command`: Specifies the operation that you want to perform on one or more resources, - for example `config`, `version`. - -* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. - -* `name`: Specifies the name of the specified instance. - -* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. - -If you need help, run `egctl help` from the terminal window. - -## Operation - -The following table includes short descriptions and the general syntax for all the `egctl` operations: - -| Operation | Syntax | Description | -| --------- | -------------------------------- | --------------------------------------------------------------------------- | -| `version` | `egctl version` | Prints out build version information. | -| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | -| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | - -## Examples - -Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: - -```console -# Retrieve all information about proxy configuration from envoy -egctl config envoy-proxy all - -# Retrieve listener information about proxy configuration from envoy -egctl config envoy-proxy listener - -# Retrieve information about envoy gateway -egctl config envoy-gateway -``` diff --git a/docs/v0.3.0/design/gatewayapi-support.md b/docs/v0.3.0/design/gatewayapi-support.md deleted file mode 100644 index 4ce9f1b041e..00000000000 --- a/docs/v0.3.0/design/gatewayapi-support.md +++ /dev/null @@ -1,119 +0,0 @@ -# Gateway API Support - -As mentioned in the [system design][] document, Envoy Gateway's managed data plane is configured dynamically through -Kubernetes resources, primarily [Gateway API][] objects. Envoy Gateway supports configuration using the following Gateway API resources. - -## GatewayClass - -A [GatewayClass][] represents a "class" of gateways, i.e. which Gateways should be managed by Envoy Gateway. -Envoy Gateway supports managing __a single__ GatewayClass resource that matches its configured `controllerName` and -follows Gateway API guidelines for [resolving conflicts][] when multiple GatewayClasses exist with a matching -`controllerName`. - -__Note:__ If specifying GatewayClass [parameters reference][], it must refer to an [EnvoyProxy][] resource. - -## Gateway - -When a [Gateway][] resource is created that references the managed GatewayClass, Envoy Gateway will create and manage a -new Envoy Proxy deployment. Gateway API resources that reference this Gateway will configure this managed Envoy Proxy -deployment. - -__Note:__ Envoy Gateway does not support multiple certificate references or specifying an [address][] for the Gateway. - -## HTTPRoute - -An [HTTPRoute][] configures routing of HTTP traffic through one or more Gateways. The following HTTPRoute filters are -supported by Envoy Gateway: - -- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - can be used to modify or add request headers before the request is proxied to its destination. -- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - can be used to modify or add response headers before the response is sent back to the client. -- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. -- `requestRedirect`: [RequestRedirects](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - configure policied for how requests that match the HTTPRoute should be modified and then redirected. -- `urlRewrite`: [UrlRewrites](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) - allow for modification of the request's hostname and path before it is proxied to its destination. -- `extensionRef`: [ExtensionRefs][] are used by Envoy Gateway to implement extended filters. Currently, Envoy Gateway - supports rate limiting and request authentication filters. For more information about these filters, refer to the - [rate limiting][] and [request authentication][] documentation. - -__Notes:__ -- The only [BackendRef][] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other destinations such - as arbitrary URLs is not possible. -- The `filters` field within [HTTPBackendRef][] is not supported. - -## TCPRoute - -A [TCPRoute][] configures routing of raw TCP traffic through one or more Gateways. Traffic can be forwarded to the -desired BackendRefs based on a TCP port number. - -__Note:__ A TCPRoute only supports proxying in non-transparent mode, i.e. the backend will see the source IP and port of -the Envoy Proxy instance instead of the client. - -## UDPRoute - -A [UDPRoute][] configures routing of raw UDP traffic through one or more Gateways. Traffic can be forwarded to the -desired BackendRefs based on a UDP port number. - -__Note:__ Similar to TCPRoutes, UDPRoutes only support proxying in non-transparent mode i.e. the backend will see the -source IP and port of the Envoy Proxy instance instead of the client. - -## GRPCRoute - -A [GRPCRoute][] configures routing of [gRPC][] requests through one or more Gateways. They offer request matching by -hostname, gRPC service, gRPC method, or HTTP/2 Header. Envoy Gateway supports the following filters on GRPCRoutes to -provide additional traffic processing: - -- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) - can be used to modify or add request headers before the request is proxied to its destination. -- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) - can be used to modify or add response headers before the response is sent back to the client. -- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) - configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. - -__Notes:__ -- The only [BackendRef][grpc-filter] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other - destinations such as arbitrary URLs is not currently possible. -- The `filters` field within [HTTPBackendRef][] is not supported. - -## TLSRoute - -A [TLSRoute][] configures routing of TCP traffic through one or more Gateways. However, unlike TCPRoutes, TLSRoutes -can match against TLS-specific metadata. - -## ReferenceGrant - -A [ReferenceGrant][] is used to allow a resource to reference another resource in a different namespace. Normally an -HTTPRoute created in namespace `foo` is not allowed to reference a Service in namespace `bar`. A ReferenceGrant permits -these types of cross-namespace references. Envoy Gateway supports the following ReferenceGrant use-cases: - -- Allowing an HTTPRoute, GRPCRoute, TLSRoute, UDPRoute, or TCPRoute to reference a Service in a different namespace. -- Allowing an HTTPRoute's `requestMirror` filter to include a BackendRef that references a Service in a different - namespace. -- Allowing a Gateway's [SecretObjectReference][] to reference a secret in a different namespace. - -[system design]: https://gateway.envoyproxy.io/latest/design/system-design.html -[Gateway API]: https://gateway-api.sigs.k8s.io/ -[GatewayClass]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayClass -[parameters reference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.ParametersReference -[Gateway]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway -[address]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayAddress -[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute -[Service]: https://kubernetes.io/docs/concepts/services-networking/service/ -[BackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef -[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPBackendRef -[TCPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute -[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute -[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute -[gRPC]: https://grpc.io/ -[TLSRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute -[ReferenceGrant]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.ReferenceGrant -[SecretObjectReference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference -[rate limiting]: https://gateway.envoyproxy.io/latest/user/rate-limit.html -[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html -[EnvoyProxy]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoyproxy -[resolving conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/?h=conflict#conflicts -[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType -[grpc-filter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter diff --git a/docs/v0.3.0/design/gatewayapi-translator.md b/docs/v0.3.0/design/gatewayapi-translator.md deleted file mode 100644 index 3cf0da94f5a..00000000000 --- a/docs/v0.3.0/design/gatewayapi-translator.md +++ /dev/null @@ -1,250 +0,0 @@ -# Gateway API Translator Design - -The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate -Representation (IR). - -## Assumptions - -Initially target core conformance features only, to be followed by extended conformance features. - -## Inputs and Outputs - -The main inputs to the Gateway API translator are: - -- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. - -__Note:__ ReferenceGrant is not fully implemented as of v0.2. - -The outputs of the Gateway API translator are: - -- Xds and Infra Internal Representations (IRs). -- Status updates for GatewayClass, Gateways, HTTPRoutes - -## Listener Compatibility - -Envoy Gateway follows Gateway API listener compatibility spec: -> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. - -__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. - -### Listener Compatibility Examples - -#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -## Computing Status - -Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway -must compute the appropriate status fields and conditions for managed resources. - -Status is computed and set for: - -- The managed GatewayClass (`gatewayclass.status.conditions`). -- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the - Envoy Deployment and Service status are also included to calculate Gateway status. -- Listeners for each Gateway (`gateway.status.listeners`). -- The ParentRef for each Route (`route.status.parents`). - -The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to -the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and -updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to -update resource status on the Kubernetes API server. - -## Outline - -The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway -API resources. - -1. Process Gateway Listeners - - Validate unique hostnames, ports, and protocols. - - Validate and compute supported kinds. - - Validate allowed namespaces (validate selector if specified). - - Validate TLS fields if specified, including resolving referenced Secrets. - -2. Process HTTPRoutes - - foreach route rule: - - compute matches - - [core] path exact, path prefix - - [core] header exact - - [extended] query param exact - - [extended] HTTP method - - compute filters - - [core] request header modifier (set/add/remove) - - [core] request redirect (hostname, statuscode) - - [extended] request mirror - - compute backends - - [core] Kubernetes services - - foreach route parent ref: - - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) - - foreach matching listener: - - foreach hostname intersection with route: - - add each computed route rule to host - -## Context Structs - -To help store, access and manipulate information as it's processed during the translation process, a set of context -structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support -processing. - -`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. - -```go -type GatewayContext struct { - // The managed Gateway - *v1beta1.Gateway - - // A list of Gateway ListenerContexts. - listeners []*ListenerContext -} -``` - -`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on -the associated Gateway. - -```go -type ListenerContext struct { - // The Gateway listener. - *v1beta1.Listener - - // The Gateway this Listener belongs to. - gateway *v1beta1.Gateway - - // An index used for managing this listener in the list of Gateway listeners. - listenerStatusIdx int - - // Only Routes in namespaces selected by the selector may be attached - // to the Gateway this listener belongs to. - namespaceSelector labels.Selector - - // The TLS Secret for this Listener, if applicable. - tlsSecret *v1.Secret -} -``` - -`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. - -```go -type RouteContext interface { - client.Object - - // GetRouteType returns the Kind of the Route object, HTTPRoute, - // TLSRoute, TCPRoute, UDPRoute etc. - GetRouteType() string - - // GetHostnames returns the hosts targeted by the Route object. - GetHostnames() []string - - // GetParentReferences returns the ParentReference of the Route object. - GetParentReferences() []v1beta1.ParentReference - - // GetRouteParentContext returns RouteParentContext by using the Route - // objects' ParentReference. - GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext -} -``` - -[message bus]: watching.md diff --git a/docs/v0.3.0/design/request-authentication.md b/docs/v0.3.0/design/request-authentication.md deleted file mode 100644 index 50490fdb5eb..00000000000 --- a/docs/v0.3.0/design/request-authentication.md +++ /dev/null @@ -1,513 +0,0 @@ -# Request Authentication - -## Overview - -[Issue 336][] specifies the need for exposing a user-facing API to configure request authentication. Request -authentication is defined as an authentication mechanism to be enforced by Envoy on a per-request basis. A connection -will be rejected if it contains invalid authentication information, based on the `AuthenticationFilter` API type -proposed in this design document. - -Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and -implementation-specific API [support levels][] for implementors such as Envoy Gateway to expose features. Since -implementing request authentication is not covered by `Core` or `Extended` APIs, an `Implementation-specific` API will -be created for this purpose. - -## Goals - -* Define an API for configuring request authentication. -* Implement [JWT] as the first supported authentication type. -* Allow users that manage routes, e.g. [HTTPRoute][], to authenticate matching requests before forwarding to a backend - service. -* Support HTTPRoutes as an Authentication API referent. HTTPRoute provides multiple [extension points][]. The - [HTTPRouteFilter][] is the extension point supported by the Authentication API. - -## Non-Goals - -* Allow infrastructure administrators to override or establish default authentication policies. -* Support referents other than HTTPRoute. -* Support Gateway API extension points other than HTTPRouteFilter. - -## Use-Cases - -These use-cases are presented as an aid for how users may attempt to utilize the outputs of the design. They are not an -exhaustive list of features for authentication support in Envoy Gateway. - -As a Service Producer, I need the ability to: -* Authenticate a request before forwarding it to a backend service. -* Have different authentication mechanisms per route rule. -* Choose from different authentication mechanisms supported by Envoy, e.g. OIDC. - -### Authentication API Type - -The Authentication API type defines authentication configuration for authenticating requests through managed Envoy -proxies. - -```go -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - -) - -type AuthenticationFilter struct { - metav1.TypeMeta - metav1.ObjectMeta - - // Spec defines the desired state of the Authentication type. - Spec AuthenticationFilterSpec - - // Note: The status sub-resource has been excluded but may be added in the future. -} - -// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. -// +union -type AuthenticationFilterSpec struct { - // Type defines the type of authentication provider to use. Supported provider types are: - // - // * JWT: A provider that uses JSON Web Token (JWT) for authenticating requests. - // - // +unionDiscriminator - Type AuthenticationFilterType - - // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple - // jwtProviders are specified, the JWT is considered valid if any of the providers - // successfully validate the JWT. - JwtProviders []JwtAuthenticationFilterProvider -} - -... -``` - -Refer to [PR 773][] for the detailed AuthenticationFilter API spec. - -The status subresource is not included in the AuthenticationFilter API. Status will be surfaced by an HTTPRoute that -references an AuthenticationFilter. For example, an HTTPRoute will surface the `ResolvedRefs=False` status condition if it -references an AuthenticationFilter that does not exist. It may be beneficial to add AuthenticationFilter status fields in the future -based on defined use-cases. For example, a remote [JWKS][] can be validated based on the specified URI and have an -appropriate status condition surfaced. - -#### AuthenticationFilter Example - -The following is an AuthenticationFilter example with one JWT authentication provider: - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example -spec: - type: JWT - jwtProviders: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json - -``` - -__Note:__ `type` is a union type, allowing only one of any supported provider type such as `jwtProviders` to be -specified. - -The following is an example HTTPRoute configured to use the above JWT authentication provider: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example - backendRefs: - - name: backend - port: 3000 -``` - -Requests for `www.example.com/foo` will be authenticated using the referenced JWT provider before being forwarded to the -backend service named "backend". - -## Implementation Details - -The JWT authentication type is translated to an Envoy [JWT authentication filter][] and a cluster is created for each -remote [JWKS][]. The following examples provide additional details on how Gateway API and AuthenticationFilter resources are -translated into Envoy configuration. - -### Example 1: One Route with One JWT Provider - -The following cluster is created from the above HTTPRoute and AuthenticationFilter: - -```yaml -dynamic_clusters: - - name: foo.com|443 - load_assignment: - cluster_name: foo.com|443 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: foo.com - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: foo.com - common_tls_context: - validation_context: - match_subject_alt_names: - - exact: "*.foo.com" - trusted_ca: - filename: /etc/ssl/certs/ca-certificates.crt -``` - -A JWT authentication HTTP filter is added to the HTTP Connection Manager. For example: - -```yaml -dynamic_resources: - dynamic_listeners: - - name: example_listener - address: - socket_address: - address: 1.2.3.4 - port_value: 80 - filter_chains: - - filters: - - name: envoy.http_connection_manager - http_filters: - - name: envoy.filters.http.jwt_authn - typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication -``` - -This JWT authentication HTTP filter contains two fields: -* The `providers` field specifies how a JWT should be verified, such as where to extract the token, where to fetch the - public key ([JWKS][]) and where to output its payload. This field is built from the source resource `namespace-name`, and - the JWT provider name of an AuthenticationFilter. -* The `rules` field specifies matching rules and their requirements. If a request matches a rule, its requirement - applies. The requirement specifies which JWT providers should be used. This field is built from a HTTPRoute - `matches` rule that references the AuthenticationFilter. When a referenced Authentication specifies multiple - `jwtProviders`, the JWT is considered valid if __any__ of the providers successfully validate the JWT. - -The following JWT authentication HTTP filter `providers` configuration is created from the above AuthenticationFilter. - -```yaml -providers: - example: - issuer: https://www.example.com - audiences: - - foo.com - remote_jwks: - http_uri: - uri: https://foo.com/jwt/public-key/jwks.json - cluster: example_jwks_cluster - timeout: 1s -``` - -The following JWT authentication HTTP filter `rules` configuration is created from the above HTTPRoute. - -```yaml -rules: - - match: - prefix: /foo - requires: - provider_name: default-example-example -``` - -### Example 2: Two HTTPRoutes with Different AuthenticationFilters - -The following example contains: -* Two HTTPRoutes with different hostnames. -* Each HTTPRoute references a different AuthenticationFilter. -* Each AuthenticationFilter contains a different JWT provider. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example1 -spec: - type: JWT - jwtProviders: - - name: example1 - issuer: https://www.example1.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example2 -spec: - type: JWT - jwtProviders: - - name: example2 - issuer: https://www.example2.com - audiences: - - bar.com - remoteJwks: - uri: https://bar.com/jwt/public-key/jwks.json ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example1 -spec: - hostnames: - - www.example1.com - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: eg - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example1 - backendRefs: - - name: backend - port: 3000 ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example2 -spec: - hostnames: - - www.example2.com - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: eg - rules: - - matches: - - path: - type: PathPrefix - value: /bar - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example2 - backendRefs: - - name: backend2 - port: 3000 -``` - -The following xDS configuration is created from the above example resources: - -```yaml -configs: -... -dynamic_listeners: - - name: default-eg-http - ... - default_filter_chain: - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - '@type': >- - type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: http - rds: - config_source: - ... - route_config_name: default-eg-http - http_filters: - - name: envoy.filters.http.jwt_authn - typed_config: - '@type': >- - type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication - providers: - default-example1-example1: - issuer: https://www.example1.com - audiences: - - foo.com - remote_jwks: - http_uri: - uri: https://foo.com/jwt/public-key/jwks.json - cluster: default-example1-example1-jwt - default-example2-example2: - issuer: https://www.example2.com - audiences: - - bar.com - remote_jwks: - http_uri: - uri: https://bar.com/jwt/public-key/jwks.json - cluster: default-example2-example2-jwt - rules: - - match: - exact: /foo - requires: - provider_name: default-example1-example1 - - match: - exact: /bar - requires: - provider_name: default-example2-example2 - - name: envoy.filters.http.router - typed_config: - '@type': >- - type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -dynamic_route_configs: - - route_config: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - name: default-eg-http - virtual_hosts: - - name: default-eg-http - domains: - - '*' - routes: - - match: - prefix: /foo - headers: - - name: ':authority' - string_match: - exact: www.example1.com - route: - cluster: default-backend-rule-0-match-0-www.example1.com - - match: - prefix: /bar - headers: - - name: ':authority' - string_match: - exact: www.example2.com - route: - cluster: default-backend2-rule-0-match-0-www.example2.com -dynamic_active_clusters: - - cluster: - name: default-backend-rule-0-match-0-www.example.com - ... - endpoints: - - locality: {} - lb_endpoints: - - endpoint: - address: - socket_address: - address: $BACKEND_SERVICE1_IP - port_value: 3000 - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - name: default-backend-rule-1-match-0-www.example.com - ... - endpoints: - - locality: {} - lb_endpoints: - - endpoint: - address: - socket_address: - address: $BACKEND_SERVICE2_IP - port_value: 3000 -... -``` - -__Note:__ The JWT provider cluster and route is omitted from the above example for brevity. - -### Implementation Outline - -* Update the Kubernetes provider to get/watch AuthenticationFilter resources that are referenced by managed HTTPRoutes. - Add the referenced AuthenticationFilter object to the resource map and publish it. -* Update the resource translator to include the AuthenticationFilter API in HTTPRoute processing. -* Update the xDS translator to translate an AuthenticationFilter into xDS resources. The translator should perform the - following: - * Convert a list of JWT rules from the xds IR into an Envoy JWT filter config. - * Create a JWT authentication HTTP filter. - * Build the HTTP Connection Manager (HCM) HTTP filters. - * Build the HCM. - * When building the Listener, create an HCM for each filter-chain. - -## Adding Authentication Types - -Additional authentication types can be added in the future through the `AuthenticationFilterType` API. For -example, to add the `Foo` authentication type: - -Define the `Foo` authentication provider: - -```go -package v1alpha1 - -// FooAuthenticationFilterProvider defines the "Foo" authentication filter provider type. -type FooAuthenticationFilterProvider struct { - // TODO: Define fields of the Foo authentication filter provider type. -} -``` - -Add the `FooAuthenticationFilterProvider` type to `AuthenticationFilterSpec`: - -```go -package v1alpha1 - -type AuthenticationFilterSpec struct { - ... - - // Foo defines the Foo authentication type. For additional - // details, see: - // - // - // - // +optional - Foo *FooAuthenticationFilterProvider -} -``` - -Lastly, add the type to the `AuthenticationType` enum: - -```go -// AuthenticationType is a type of authentication provider. -// +kubebuilder:validation:Enum=JWT,FOO -type AuthenticationFilterType string - -const ( - // JwtAuthenticationProviderType is the JWT authentication provider type. - FooAuthenticationFilterProviderType AuthenticationFilterType = "FOO" -) -``` - -The AuthenticationFilter API should support additional authentication types in the future, for example: -- OAuth2 -- OIDC - -## Outstanding Questions - -- If Envoy Gateway owns the AuthenticationFilter API, is an xDS IR equivalent needed? -- Should local [JWKS][] be implemented before remote [JWKS][]? -- How should Envoy obtain the trusted CA for a remote [JWKS][]? -- Should HTTPS be the only supported scheme for remote [JWKS][]? -- Should OR'ing JWT providers be supported? -- Should Authentication provide status? -- Are the API field validation rules acceptable? - -[Issue 336]: https://github.com/envoyproxy/gateway/issues/336 -[Gateway API]: https://gateway-api.sigs.k8s.io/ -[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels -[JWT]: https://jwt.io/ -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[extension points]: https://gateway-api.sigs.k8s.io/concepts/api-overview/?h=extension#extension-points -[HTTPRouteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter -[JWKS]: https://www.rfc-editor.org/rfc/rfc7517 -[JWT authentication filter]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn -[PR 773]: https://github.com/envoyproxy/gateway/pull/733 diff --git a/docs/v0.3.0/design/roadmap.md b/docs/v0.3.0/design/roadmap.md deleted file mode 100644 index 735f19e6981..00000000000 --- a/docs/v0.3.0/design/roadmap.md +++ /dev/null @@ -1,80 +0,0 @@ -# Roadmap - -This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of -the project. - -## Contributing to the Roadmap - -- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use - case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update - this document accordingly. -- To help with an existing roadmap item, comment on or assign yourself to the associated issue. -- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A - maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be - discussed in an issue or a community meeting before implementing it. - -If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. -Look for issues with the `help wanted` label to get started. - -## Details - -Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific -roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by -contributing to the project. - -`Last Updated: November 2022` - -### [v0.2.0][v0.2.0]: Establish a Solid Foundation - -- Complete the core Envoy Gateway implementation- [Issue #60][60]. -- Establish initial testing, e2e, integration, etc- [Issue #64][64]. -- Establish user and developer project documentation- [Issue #17][17]. -- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. -- Setup a CI/CD pipeline- [Issue #63][63]. - -### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms - -- Support extended Gateway API fields [Issue #707][707]. -- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. -- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. -- Rate Limiting [Issue #670][670]. -- Authentication [Issue #336][336]. - -### [v0.4.0][v0.4.0]: Customizing Envoy Gateway - -- Extending Envoy Gateway control plane [Issue #20][20] -- Helm based installation for Envoy Gateway [Issue #650][650] -- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] -- Configuring xDS Resources [Issue #24][24] and [Issue #31][31]. - - -### [v0.5.0][v0.5.0]: Observability and Scale - -- Observability for control plane and data plane [Issue #701][701]. - -[issue]: https://github.com/envoyproxy/gateway/issues -[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing -[pr]: https://github.com/envoyproxy/gateway/compare -[milestones]: https://github.com/envoyproxy/gateway/milestones -[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 -[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 -[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 -[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 -[17]: https://github.com/envoyproxy/gateway/issues/17 -[20]: https://github.com/envoyproxy/gateway/issues/20 -[24]: https://github.com/envoyproxy/gateway/issues/24 -[31]: https://github.com/envoyproxy/gateway/issues/31 -[60]: https://github.com/envoyproxy/gateway/issues/60 -[63]: https://github.com/envoyproxy/gateway/issues/63 -[64]: https://github.com/envoyproxy/gateway/issues/64 -[65]: https://github.com/envoyproxy/gateway/issues/65 -[336]: https://github.com/envoyproxy/gateway/issues/336 -[641]: https://github.com/envoyproxy/gateway/issues/641 -[642]: https://github.com/envoyproxy/gateway/issues/642 -[648]: https://github.com/envoyproxy/gateway/issues/648 -[650]: https://github.com/envoyproxy/gateway/issues/650 -[643]: https://github.com/envoyproxy/gateway/issues/643 -[670]: https://github.com/envoyproxy/gateway/issues/670 -[675]: https://github.com/envoyproxy/gateway/issues/675 -[701]: https://github.com/envoyproxy/gateway/issues/701 -[707]: https://github.com/envoyproxy/gateway/issues/707 diff --git a/docs/v0.3.0/design/system-design.md b/docs/v0.3.0/design/system-design.md deleted file mode 100644 index 731cb0925b0..00000000000 --- a/docs/v0.3.0/design/system-design.md +++ /dev/null @@ -1,171 +0,0 @@ -# System Design - -## Goals - -* Define the system components needed to satisfy the requirements of Envoy Gateway. - -## Non-Goals - -* Create a detailed design and interface specification for each system component. - -## Terminology - -* Control Plane- A collection of inter-related software components for providing application gateway and routing - functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. - These services are detailed in the [components](#components) section. -* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. - -## Architecture - -![Architecture](../images/architecture.png) - -## Configuration - -Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through -Kubernetes resources, primarily [Gateway API][gw_api] objects. - -### Static Configuration - -Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, -configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the -configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. - -### Dynamic Configuration - -Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using -reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is -defined as Kubernetes resources that provide the following services: - -* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is - expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be - referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, - e.g. expose Envoy network endpoints using a NodePort service instead of a LoadBalancer service. -* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP - requests for "www.example.com" to a backend service running a web server. This configuration is expressed through - [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. - Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] - reference. - -## Components - -Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described -in the [Watching Components Design][wcd]. - -### Provider - -A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve -services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap -via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A -provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). - -#### Kubernetes Provider - -* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the - [dynamic configuration](#dynamic-configuration). -* Manages the data plane through Kubernetes API CRUD operations. -* Uses Kubernetes for Service discovery. -* Uses etcd (via Kubernetes API) to persist data. - -#### File Provider - -* Uses a file watcher to watch files in a directory that define the data plane configuration. -* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. -* Uses the host's DNS for Service discovery. -* If needed, the local filesystem is used to persist data. - -### Resource Watcher - -The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The -mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes -provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator -as output. - -### Resource Translator - -The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate -Representation (IR). It is responsible for: - -* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. -* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. - -__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. - -### Intermediate Representation (IR) - -The Intermediate Representation defines internal data models that external resources are translated into. This allows -Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR -used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. - -* Infra IR- Used as the internal definition of the managed data plane infrastructure. -* xDS IR- Used as the internal definition of the managed data plane xDS configuration. - -### xDS Translator - -The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. - -### xDS Server - -The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server -Protocol and is responsible for using xDS to configure the data plane. - -### Infra Manager - -The Infra Manager is a provider-specific component responsible for managing the following infrastructure: - -* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, - Service, etc. resources to run Envoy in a Kubernetes cluster. -* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require - external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning - and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to - users through the [Custom Route Filters][crf] extension. - -The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. - -## Design Decisions - -* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with - `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy - Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. - `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy - infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. -* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. - * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. - * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners - in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are - met: - * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies - either the “HTTPS” or “TLS” Protocol. - * Each Listener within the group specifies a unique "Hostname". - * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no - other Listener matches. - * Envoy Gateway does __not__ merge listeners across multiple Gateways. -* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. - * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. -* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. - * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. -* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. - -The draft for this document is [here][draft_design]. - -[gw_api]: https://gateway-api.sigs.k8s.io -[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass -[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway -[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute -[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute -[go_cp]: https://github.com/envoyproxy/go-control-plane -[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting -[rls]: https://github.com/envoyproxy/ratelimit -[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit -[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional -[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts -[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners -[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route -[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional -[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster -[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference -[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ -[ wcd ]: ./watching.md -[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 -[roadmap]: roadmap.md diff --git a/docs/v0.3.0/design/tcp-udp-design.md b/docs/v0.3.0/design/tcp-udp-design.md deleted file mode 100644 index 276221b897b..00000000000 --- a/docs/v0.3.0/design/tcp-udp-design.md +++ /dev/null @@ -1,47 +0,0 @@ -# TCP and UDP Proxy Design - -Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP -and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the -design decision. - -Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) - and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) -, so ideally, Envoy Gateway should also be able to work in these two modes: - -## Non-transparent Proxy Mode -For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the -TCP traffic from the downstream to the upstream. - -For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when -proxying the UDP datagrams to the upstream. - -In this mode, the upstream will see Envoy's IP address and port. - -## Transparent Proxy Mode -For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies -the TCP traffic from the downstream to the upstream. - -For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address -when proxying the UDP datagrams to the upstream. - -In this mode, the upstream will see the original downstream IP address and Envoy's mac address. - -Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward -the port number. - -## The Implications of Transparent Proxy Mode - -### Escalated Privilege -Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated -CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. - -### Routing -The upstream can see the original source IP, but the original port number won't be passed, so the return -traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back -to the right port number of the downstream, which requires routing at the upstream side to be set up. -In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. - -## The Design Decision (For Now) - -The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and -port of the deployed Envoy instance instead of the client. diff --git a/docs/v0.3.0/design/watching.md b/docs/v0.3.0/design/watching.md deleted file mode 100644 index b8477a30e2d..00000000000 --- a/docs/v0.3.0/design/watching.md +++ /dev/null @@ -1,117 +0,0 @@ -# Watching Components Design - -Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch -external resources, and "publish" what they see for other components to consume; others watch what another publishes and -act on it (such as the resource translator watches what the providers publish, and then publishes its own results that -are watched by another component). Some of these internally published results are consumed by multiple components. - -To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the -standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub -pattern. - -## Pub - -Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" -tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if -we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by -the IR translator: - - ```go - type ResourceTable struct { - // gateway classes are cluster-scoped; no namespace - GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] - - // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. - Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] - - HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] - } - ``` - -The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; -updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` -method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher -doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just -`.Store` its data and `watchable` will do the right thing. - -## Sub - -Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or -`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: - - ```go - func(ctx context.Context) error { - for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { - fullState := irInput{ - GatewayClasses: k8sTable.GatewayClasses.LoadAll(), - Gateways: k8sTable.Gateways.LoadAll(), - HTTPRoutes: snapshot.State, - } - translate(irInput) - } - } - ``` - -Or, to watch multiple maps in the same loop: - - ```go - func worker(ctx context.Context) error { - classCh := k8sTable.GatewayClasses.Subscribe(ctx) - gwCh := k8sTable.Gateways.Subscribe(ctx) - routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) - for ctx.Err() == nil { - var arg irInput - select { - case snapshot := <-classCh: - arg.GatewayClasses = snapshot.State - case snapshot := <-gwCh: - arg.Gateways = snapshot.State - case snapshot := <-routeCh: - arg.Routes = snapshot.State - } - if arg.GateWayClasses == nil { - arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() - } - if arg.GateWays == nil { - arg.Gateways = k8sTable.GateWays.LoadAll() - } - if arg.HTTPRoutes == nil { - arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() - } - translate(irInput) - } - } - ``` - -From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; -but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a -handy way to know when to run, `.Load` and friends can be used without subscribing. - -There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but -it's probably wise to just have one publisher for each map. - -The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when -`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple -mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in -to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of -each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need -to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. - -If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` -entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you -must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to -help with this. - -## Other Notes - -The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the -map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb -`chan`. - -A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types -be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and -so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can -generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate -methods for. - -[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/docs/v0.3.0/design_docs.rst b/docs/v0.3.0/design_docs.rst deleted file mode 100644 index 29d4e9ff259..00000000000 --- a/docs/v0.3.0/design_docs.rst +++ /dev/null @@ -1,17 +0,0 @@ -Design Docs -=========== - -Learn about the internal details of Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - design/system-design - design/gatewayapi-support - design/gatewayapi-translator - design/watching - design/config-api - design/tcp-udp-design - design/egctl - design/ratelimit - design/request-authentication diff --git a/docs/v0.3.0/dev/CODEOWNERS.md b/docs/v0.3.0/dev/CODEOWNERS.md deleted file mode 100644 index d4229b6b23f..00000000000 --- a/docs/v0.3.0/dev/CODEOWNERS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Maintainers - -## The following maintainers, listed in alphabetical order, own everything - -- @AliceProxy -- @arkodg -- @skriss -- @Xunzhuo -- @youngnick -- @zirain - -## Emeritus Maintainers - -- @danehans -- @alexgervais diff --git a/docs/v0.3.0/dev/CODE_OF_CONDUCT.md b/docs/v0.3.0/dev/CODE_OF_CONDUCT.md deleted file mode 100644 index a0a295770f3..00000000000 --- a/docs/v0.3.0/dev/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,3 +0,0 @@ -# Community Code of Conduct - -Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/docs/v0.3.0/dev/CONTRIBUTING.md b/docs/v0.3.0/dev/CONTRIBUTING.md deleted file mode 100644 index d7770bdeff2..00000000000 --- a/docs/v0.3.0/dev/CONTRIBUTING.md +++ /dev/null @@ -1,183 +0,0 @@ -# Contributing - -We welcome contributions from the community. Please carefully review the [project goals](GOALS.md) -and following guidelines to streamline your contributions. - -## Communication - -* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no - one else is working on it and ask you to open a GitHub issue. -* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or - changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to - agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process - for major features is also important so that [affiliations with commit access](CODEOWNERS.md) can - come to agreement on the design. If it's appropriate to write a design document, the document must - be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable - location. -* Small patches and bug fixes don't need prior communication. - -## Inclusivity - -The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere -to the following guidelines for all code, APIs, and documentation: - -* The following words and phrases are not allowed: - * *Whitelist*: use allowlist instead. - * *Blacklist*: use denylist or blocklist instead. - * *Master*: use primary instead. - * *Slave*: use secondary or replica instead. -* Documentation should be written in an inclusive style. The [Google developer - documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent - reference on this topic. -* The above policy is not considered definitive and may be amended in the future as industry best - practices evolve. Additional comments on this topic may be provided by maintainers during code - review. - -## Submitting a PR - -* Fork the repo. -* Hack -* DCO sign-off each commit. This can be done with `git commit -s`. -* Submit your PR. -* Tests will automatically run for you. -* We will **not** merge any PR that is not passing tests. -* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage - build. If your PR cannot have 100% coverage for some reason please clearly explain why when you - open it. -* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/docs) folder of the repo as - well as the [changelog](../releases). -* All code comments and documentation are expected to have proper English grammar and punctuation. - If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try - to find some help but there are no guarantees. -* Your PR title should be descriptive, and generally start with a subsystem name followed by a - colon. Examples: - * "docs: fix grammar error" - * "translator: add new feature" -* Your PR commit message will be used as the commit message when your PR is merged. You should - update this field if your PR diverges during review. -* Your PR description should have details on what the PR does. If it fixes an existing issue it - should end with "Fixes #XXX". -* If your PR is co-authored or based on an earlier PR from another contributor, - please attribute them with `Co-authored-by: name `. See - GitHub's [multiple author - guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) - for further details. -* When all tests are passing and all other conditions described herein are satisfied, a maintainer - will be assigned to review and merge the PR. -* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits - are new commits and/or merges. We squash and merge so the number of commits you have in the PR - doesn't matter. -* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. - We reserve the right to close PRs that are not making progress. This is generally defined as no - changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. - Closing stale PRs helps us to keep on top of all the work currently in flight. - -## Maintainer PR Review Policy - -* See [CODEOWNERS.md](CODEOWNERS.md) for the current list of maintainers. -* A maintainer representing a different affiliation from the PR owner is required to review and - approve the PR. -* When the project matures, it is expected that a "domain expert" for the code the PR touches should - review the PR. This person does not require commit access, just domain knowledge. -* The above rules may be waived for PRs which only update docs or comments, or trivial changes to - tests and tools (where trivial is decided by the maintainer in question). -* If there is a question on who should review a PR please discuss in Slack. -* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. -* Please make sure that the PR title, commit message, and description are updated if the PR changes - significantly during review. -* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge - title with the original title, and the commit body with every individual commit from the PR. - The maintainer doing the merge should make sure the title follows the guidelines above and should - overwrite the body with the original commit message from the PR (cleaning it up if necessary) - while preserving the PR author's final DCO sign-off. - -## Decision making - -This is a new and complex project, and we need to make a lot of decisions very quickly. -To this end, we've settled on this process for making (possibly contentious) decisions: - -* For decisions that need a record, we create an issue. -* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. -* Maintainers can cast binding votes on that comment by reacting or replying in another comment. -* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. -* Voting will be resolved by simple majority. -* In the event of deadlocks, the question will be put to steering instead. - -## DCO: Sign your work - -The sign-off is a simple line at the end of the explanation for the -patch, which certifies that you wrote it or otherwise have the right to -pass it on as an open-source patch. The rules are pretty simple: if you -can certify the below (from -[developercertificate.org](https://developercertificate.org/)): - -``` -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -using your real name (sorry, no pseudonyms or anonymous contributions.) - -You can add the sign-off when creating the git commit via `git commit -s`. - -If you want this to be automatic you can set up some aliases: - -```bash -git config --add alias.amend "commit -s --amend" -git config --add alias.c "commit -s" -``` - -## Fixing DCO - -If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best -practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) -the commit history to a single commit, append the DCO sign-off as described above, and [force -push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in -your history: - -```bash -git rebase -i HEAD^^ -(interactive squash + DCO append) -git push origin -f -``` - -Note, that in general rewriting history in this way is a hindrance to the review process and this -should only be done to correct a DCO mistake. diff --git a/docs/v0.3.0/dev/DOCS.md b/docs/v0.3.0/dev/DOCS.md deleted file mode 100644 index fb49b9d55dd..00000000000 --- a/docs/v0.3.0/dev/DOCS.md +++ /dev/null @@ -1,63 +0,0 @@ -# Working on the Envoy Gateway Docs - -The documentation for the Envoy Gateway lives in the `docs/` directory. Any -individual document can be written using either [reStructuredText] or [Markdown], -you can choose the format that you're most comfortable with when working on the -documentation. - -## Documentation Structure - -We supported the versioned Docs now, the directory name under docs represents -the version of docs. The root of the latest site is in `docs/latest/index.rst`. -This is probably where to start if you're trying to understand how things fit together. - -Note that the new contents should be added to `docs/latest` and will be cut off at -the next release. The contents under `docs/v0.2.0` are auto-generated, -and usually do not need to make changes to them, unless if you find the current release pages have -some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` -and `docs/v0.2.0`. - -It's important to note that a given document _must_ have a reference in some -`.. toctree::` section for the document to be reachable. Not everything needs -to be in `docs/index.rst`'s `toctree` though. - -You can access the website which represents the current release in default, -and you can access the website which contains the latest version changes in -[Here][latest-website] or at the footer of the pages. - -## Documentation Workflow - -To work with the docs, just edit reStructuredText or Markdown files in `docs`, -then run - -```bash -make docs -``` - -This will create `docs/html` with the built HTML pages. You can view the docs -either simply by pointing a web browser at the `file://` path to your -`docs/html`, or by firing up a static webserver from that directory, e.g. - -``` shell -make docs-serve -``` - -If you want to generate a new release version of the docs, like `v0.3.0`, then run - -```bash -make docs-release TAG=v0.3.0 -``` - -This will update the VERSION file at the project root, which records current release version, -and it will be used in the pages version context and binary version output. Also, this will generate -new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` -in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. - -## Publishing Docs - -Whenever docs are pushed to `main`, CI will publish the built docs to GitHub -Pages. For more details, see `.github/workflows/docs.yaml`. - -[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -[Markdown]: https://daringfireball.net/projects/markdown/syntax -[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/docs/v0.3.0/dev/GOALS.md b/docs/v0.3.0/dev/GOALS.md deleted file mode 100644 index 519be9f180f..00000000000 --- a/docs/v0.3.0/dev/GOALS.md +++ /dev/null @@ -1,79 +0,0 @@ -# Goals - -The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption -through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing -use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer -fundamental interactions. - -## Objectives - -### Expressive API -The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. - -The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This -expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy -a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of -the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar -nouns that [users](#personas) understand. - -The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who -add functionality on top of Envoy Gateway, such as commercial API gateway products. - -This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer -on top. - -### Batteries included -Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on -delivering core business value. - -The project plans to include additional infrastructure components required by users to fulfill their Ingress and API -gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and -possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to -override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status -sub-resources. - -Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators -will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to -operate. - -### All environments -Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. - -Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto -standard for Kubernetes ingress supporting the [Gateway API][]. -Additional goals include multi-cluster support and various runtime environments. - -### Extensibility -Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. - -It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation -for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power -of xDS. - -Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points -for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, -allowing vendors to shift to a higher management plane layer. - -## Non-objectives - -### Cannibalize vendor models -Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor -monetization model, though some vendors may be affected by it. - -### Disrupt current Envoy usage patterns -Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user -with Envoy Proxy, xDS, or go-control-plane. - -## Personas -_In order of priority_ - -### 1. Application developer -The application developer spends the majority of their time developing business logic code. They require the ability to -manage access to their application. - -### 2. Infrastructure administrators -The infrastructure administrators are responsible for the installation, maintenance, and operation of -API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. -Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. - -[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/docs/v0.3.0/dev/README.md b/docs/v0.3.0/dev/README.md deleted file mode 100644 index 0c3b055b619..00000000000 --- a/docs/v0.3.0/dev/README.md +++ /dev/null @@ -1,152 +0,0 @@ -# Developer Guide - -Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. - -## Prerequisites - -### go - -* Version: 1.19 -* Installation Guide: https://go.dev/doc/install - -### make - -* Recommended Version: 4.0 or later -* Installation Guide: https://www.gnu.org/software/make - -### docker - -* Optional when you want to build a Docker image or run `make` inside Docker. -* Recommended Version: 20.10.16 -* Installation Guide: https://docs.docker.com/engine/install - -### python3 - -* Need a `python3` program -* Must have a functioning `venv` module; this is part of the standard - library, but some distributions (such as Debian and Ubuntu) replace - it with a stub and require you to install a `python3-venv` package - separately. - -## Quickstart - -* Run `make help` to see all the available targets to build, test and run Envoy Gateway. - -### Building - -* Run `make build` to build the Envoy Gateway binary. __Note:__ The binary gets generated in the `bin/` directory - -### Testing - -* Run `make test` to run the golang tests. - -### Running Linters - -* Run `make lint` to make sure your code passes all the linter checks. - -### Building and Pushing the Image - -* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. -* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. - -__Note:__ Replace `IMAGE` with your registry's image name. - -### Deploying Envoy Gateway for Test/Dev - -* Run `make create-cluster` to create a [Kind][] cluster. - -#### Option 1: Use the Latest [gateway-dev][] Image - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` - to use a different image tag. - -#### Option 2: Use a Custom Image - -* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. -* Run `make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. - -### Deploying Envoy Gateway in Kubernetes - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to - the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or - tag. -* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. - -__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. - -### Demo Setup - -* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource -(similar to steps outlined in the [Quickstart][] docs) and test the configuration. -* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. - -### Run Gateway API Conformance Tests - -The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the -Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run -`TAG=latest make run-conformance` to run the conformance tests. - -#### On a Linux Host - -* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] - image, and run Gateway API conformance tests. - -#### On a Mac Host - -Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following -workarounds to run conformance tests: - -* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run - `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image - to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to - uninstall Envoy Gateway. -* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. - -__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` -is unspecified, the short SHA of your current branch is used. - -### Debugging the Envoy Config - -An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port -(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. - -Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: - -```shell -export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward the admin interface port: - -```shell -kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 -``` - -Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. - -There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. - -### JWT Testing - -An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] -user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs -verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching -settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure -and is hosted in the repo. - -[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[make]: https://www.gnu.org/software/make/ -[Github Actions]: https://docs.github.com/en/actions -[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows -[Kind]: https://kind.sigs.k8s.io/ -[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ -[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ -[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ -[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags -[mac_connect]: https://github.com/chipmk/docker-mac-net-connect -[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface -[jwt]: https://tools.ietf.org/html/rfc7519 -[jwks]: https://tools.ietf.org/html/rfc7517 -[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html -[JWT Debugger]: https://jwt.io/ -[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/docs/v0.3.0/dev/releasing.md b/docs/v0.3.0/dev/releasing.md deleted file mode 100644 index f0004caf336..00000000000 --- a/docs/v0.3.0/dev/releasing.md +++ /dev/null @@ -1,195 +0,0 @@ -# Release Process - -This document guides maintainers through the process of creating an Envoy Gateway release. - -- [Release Candidate](#release-candidate) -- [Minor Release](#minor-release) -- [Announce the Release](#announce-the-release) - -## Release Candidate - -The following steps should be used for creating a release candidate. - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export RELEASE_CANDIDATE_NUMBER=1 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. -3. Sign, commit, and push your changes to your fork. -4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and - the [Build and Test][] has successfully completed. -5. Create a new release branch from `main`. The release branch should be named - `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. Push the branch to the Envoy Gateway repo. - - ```shell - git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] - for additional details on updating the image tag. -8. Sign, commit, and push your changes to your fork. -9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not - proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. -10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' - ``` - -11. Push the tag to the Envoy Gateway repository. - - ```shell - git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} - ``` - -12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -13. Confirm that the [release workflow][] completed successfully. -14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -15. Confirm that the [release][] was created. -16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test - the quickstart steps using the release candidate by manually updating the links. -17. [Generate][] the GitHub changelog. -18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. -19. If you find any bugs in this process, please create an issue. - -## Minor Release - -The following steps should be used for creating a minor release. - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. -- A release branch that has been cut from the corresponding release candidate. Refer to the - [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. - - 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release - notes should be an accumulation of the release candidate release notes and any changes since the release - candidate. - 2. Create a release announcement. Refer to [PR #635] as an example release announcement. - 3. Generate the versioned release docs: - - ``` shell - make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -3. Sign, commit, and push your changes to your fork. -4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged - and the [Build and Test][] has completed for your final PR. - -5. Checkout the release branch. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. If the tip of the release branch does not match the tip of `main`, perform the following: - - 1. Create a topic branch from the release branch. - 2. Cherry-pick the commits from `main` that differ from the release branch. - 3. Run tests locally, e.g. `make lint`. - 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. - 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. - 6. Do not proceed until the PR has merged and CI passes for the merged PR. - 7. If you are still on your topic branch, change to the release branch: - - ```shell - git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - - 8. Ensure your local release branch is up-to-date: - - ```shell - git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -7. Tag the head of your release branch with the release tag. For example: - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' - ``` - - __Note:__ The tag version differs from the release branch by including the `.0` patch version. - -8. Push the tag to the Envoy Gateway repository. - - ```shell - git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -10. Confirm that the [release workflow][] completed successfully. -11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -12. Confirm that the [release][] was created. -13. Confirm that the steps in the [Quickstart Guide][] work as expected. -14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: - - ```console - # Release Announcement - - Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] - (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. - ``` - -If you find any bugs in this process, please create an issue. - -## Announce the Release - -It's important that the world knows about the release. Use the following steps to announce the release. - -1. Set the release information in the Envoy Gateway Slack channel. For example: - - ```shell - Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -2. Send a message to the Envoy Gateway Slack channel. For example: - - ```shell - On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway - v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. - Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs - to start using Envoy Gateway. - ... - ``` - - Link to the GitHub release and release announcement page that highlights the release. - -[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes -[Pull Request]: https://github.com/envoyproxy/gateway/pulls -[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml -[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml -[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml -[image]: https://hub.docker.com/r/envoyproxy/gateway/tags -[release]: https://github.com/envoyproxy/gateway/releases -[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes -[PR #635]: https://github.com/envoyproxy/gateway/pull/635 -[PR #958]: https://github.com/envoyproxy/gateway/pull/958 -[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/docs/v0.3.0/dev_docs.rst b/docs/v0.3.0/dev_docs.rst deleted file mode 100644 index e546e14c1ad..00000000000 --- a/docs/v0.3.0/dev_docs.rst +++ /dev/null @@ -1,15 +0,0 @@ -Developer Docs -============== - -Learn how to contribute to Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - dev/GOALS - dev/CODE_OF_CONDUCT - dev/CODEOWNERS - dev/CONTRIBUTING - dev/README - dev/DOCS - dev/releasing diff --git a/docs/v0.3.0/get_involved.rst b/docs/v0.3.0/get_involved.rst deleted file mode 100644 index f17febd5651..00000000000 --- a/docs/v0.3.0/get_involved.rst +++ /dev/null @@ -1,9 +0,0 @@ -Getting Involved -================ - -We welcome contributions from the community. Please carefully review the -`project goals `_ -and the -`code of conduct `_ -before diving in. - diff --git a/docs/v0.3.0/images/architecture.png b/docs/v0.3.0/images/architecture.png deleted file mode 100644 index 1d4131fbea7..00000000000 Binary files a/docs/v0.3.0/images/architecture.png and /dev/null differ diff --git a/docs/v0.3.0/index.rst b/docs/v0.3.0/index.rst deleted file mode 100644 index b31be95cc01..00000000000 --- a/docs/v0.3.0/index.rst +++ /dev/null @@ -1,33 +0,0 @@ -`Envoy Gateway `_ -==================== - -Release: |version| - -.. image:: https://img.shields.io/badge/slack-join-orange.svg - :target: https://envoyproxy.slack.com/archives/C03E6NHLESV - :alt: Join the Envoy Slack - -Envoy Gateway is an open source project for managing `Envoy Proxy`_ as a standalone or Kubernetes-based application -gateway. `Gateway API`_ resources are used to dynamically provision and configure the managed Envoy Proxies. Whether -you are interested in using or contributing to Envoy Gateway, the following resources will help you get started: - -.. toctree:: - :maxdepth: 1 - - intro/compatibility - user_docs - design_docs - dev_docs - api_docs - releases - roadmap - about_docs - get_involved - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved`. - -.. _Envoy Proxy: https://www.envoyproxy.io/ -.. _Gateway API: https://gateway-api.sigs.k8s.io/ diff --git a/docs/v0.3.0/intro/compatibility.rst b/docs/v0.3.0/intro/compatibility.rst deleted file mode 100644 index bffbd8997dc..00000000000 --- a/docs/v0.3.0/intro/compatibility.rst +++ /dev/null @@ -1,23 +0,0 @@ -Compatibility Matrix -==================== - -Envoy Gateway relies on the Envoy Proxy and the Gateway API, and runs -within a Kubernetes cluster. Not all versions of each of these products -can function together for Envoy Gateway. Supported version combinations -are listed below; **bold** type indicates the versions of the Envoy Proxy -and the Gateway API actually compiled into each Envoy Gateway release. - -+--------------------------+---------------------+---------------------+----------------------------+ -| Envoy Gateway version | Envoy Proxy version | Gateway API version | Kubernetes version | -+--------------------------+---------------------+---------------------+----------------------------+ -| v0.3.0 | **v1.25-latest** | **v0.6.1** | v1.24, v1.25, v1.26 | -+--------------------------+---------------------+---------------------+----------------------------+ -| v0.2.0 | **v1.23-latest** | **v0.5.1** | v1.24 | -+--------------------------+---------------------+---------------------+----------------------------+ -| latest | **dev-latest** | **v0.6.1** | v1.24, v1.25, v1.26 | -+--------------------------+---------------------+---------------------+----------------------------+ - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved<../get_involved>`. diff --git a/docs/v0.3.0/releases.rst b/docs/v0.3.0/releases.rst deleted file mode 100644 index 20d05b25ac8..00000000000 --- a/docs/v0.3.0/releases.rst +++ /dev/null @@ -1,11 +0,0 @@ -Releases -======== - -Learn more about Envoy Gateway releases. - -.. toctree:: - :maxdepth: 1 - - releases/README - releases/v0.2 - releases/v0.3 diff --git a/docs/v0.3.0/releases/README.md b/docs/v0.3.0/releases/README.md deleted file mode 100644 index 3f6df9e7ea6..00000000000 --- a/docs/v0.3.0/releases/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Release Details - -This document provides details for Envoy Gateway releases. Envoy Gateway follows the Semantic Versioning [v2.0.0 spec][] -for release versioning. Since Envoy Gateway is a new project, minor releases are the only defined releases. Envoy -Gateway maintainers will establish additional release details, e.g. patch releases, at a future date. - -## Stable Releases - -Stable releases of Envoy Gateway include: - -* Minor Releases- A new release branch and corresponding tag are created from the `main` branch. A minor release - is supported for 6 months following the release date. As the project matures, Envoy Gateway maintainers will reassess - the support timeframe. - -Minor releases happen quarterly and follow the schedule below. - -## Release Management - -Minor releases are handled by a designated Envoy Gateway maintainer. This maintainer is considered the Release Manager -for the release. The details for creating a release are outlined in the [release guide][]. The Release Manager is -responsible for coordinating the overall release. This includes identifying issues to be fixed in the release, -communications with the Envoy Gateway community, and the mechanics of the release. - -| Quarter | Release Manager | -|:-------:|:--------------------------------------------------------------:| -| 2022 Q4 | Daneyon Hansen ([danehans](https://github.com/danehans)) | -| 2023 Q1 | Xunzhuo Liu ([Xunzhuo](https://github.com/Xunzhuo)) | - -## Release Schedule - -In order to align with the Envoy Proxy [release schedule][], Envoy Gateway releases are produced on a fixed schedule -(the 22nd day of each quarter), with an acceptable delay of up to 2 weeks, and a hard deadline of 3 weeks. - -| Version | Expected | Actual | Difference | End of Life | -|:-------:|:-----------:|:-----------:|:----------:|:-----------:| -| 0.2.0 | 2022/10/22 | 2022/10/20 | -2 day | 2023/4/20 | -| 0.3.0 | 2023/01/22 | 2023/02/09 | +17 day | 2023/08/09 | - -[v2.0.0 spec]: https://semver.org/spec/v2.0.0.html -[release guide]: ../dev/releasing.md -[release schedule]: https://github.com/envoyproxy/envoy/blob/main/RELEASES.md#major-release-schedule diff --git a/docs/v0.3.0/releases/v0.2.md b/docs/v0.3.0/releases/v0.2.md deleted file mode 100644 index a0dc0e885de..00000000000 --- a/docs/v0.3.0/releases/v0.2.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.2 -linktitle: v0.2 -subtitle: Major Update -description: Envoy Gateway v0.2 release announcement. -publishdate: 2022-10-20 -release: v0.2.0 -skip_list: true -aliases: -- /releases/v0.2 -- /releases/v0.2.0 ---- -# Envoy Gateway Release v0.2 - -We are pleased to announce the release of Envoy Gateway v0.2! - -This is the first functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Kubernetes Support - -Run Envoy Gateway in a Kubernetes cluster. Checkout the [quickstart guide][] to get started with Envoy Gateway in a few -simple steps. - -### Gateway API Support - -Envoy Gateway supports Gateway API resources for running and configuring a managed fleet of Envoy proxies. Envoy Gateway -passes Gateway API core [conformance tests][] and supports GatewayClass, Gateway, HTTPRoute, and TLSRoute resources. See -the [documentation][docs] for additional details on how to use Envoy Gateway for your edge proxy and API gateway needs. - -## Envoy Gateway at EnvoyCon NA - -Envoy Gateway will be at [EnvoyCon NA][] this October in Detroit. Don't miss [our talk][] to learn more about the -release and future direction of the project. - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.2.0.yaml -[matrix]: https://gateway.envoyproxy.io/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.2.0 -[conformance tests]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=conformance -[quickstart guide]: https://gateway.envoyproxy.io/user/quickstart.html -[EnvoyCon NA]: https://events.linuxfoundation.org/envoycon-north-america/program/schedule/ -[our talk]: https://sched.co/1AO5S diff --git a/docs/v0.3.0/releases/v0.3.md b/docs/v0.3.0/releases/v0.3.md deleted file mode 100644 index 96d6d6d49eb..00000000000 --- a/docs/v0.3.0/releases/v0.3.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.3 -linktitle: v0.3 -subtitle: Major Update -description: Envoy Gateway v0.3 release announcement. -publishdate: 2023-02-09 -release: v0.3.0 -skip_list: true -aliases: -- /releases/v0.3 -- /releases/v0.3.0 ---- -# Envoy Gateway Release v0.3 - -We are pleased to announce the release of Envoy Gateway v0.3! - -This is the second functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Add Support for extended Gateway API fields - -+ Added Support for HTTPRoute URLRewrite Filter -+ Added Support for HTTPRoute RequestMirror Filter -+ Added Support for HTTPRoute ResponseHeaderModifier Filter - -### Add Support for experimental Gateway APIs - -+ Added Support for the TCPRoute API -+ Added Support for the UDPRoute API -+ Added Support for the GRPCRoute API - -### Add Support for Rate Limiting - -+ Added Support for Global Rate Limiting - -### Add Support for Authentication - -+ Added Support for Request Authentication - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.3.0.yaml -[matrix]: https://gateway.envoyproxy.io/v0.3.0/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/v0.3.0/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.3.0 diff --git a/docs/v0.3.0/roadmap.rst b/docs/v0.3.0/roadmap.rst deleted file mode 100644 index 711b6245503..00000000000 --- a/docs/v0.3.0/roadmap.rst +++ /dev/null @@ -1,9 +0,0 @@ -Roadmap -======= - -Learn about the future direction of Envoy Gateway. - -.. toctree:: - :maxdepth: 2 - - design/roadmap diff --git a/docs/v0.3.0/user/authn.md b/docs/v0.3.0/user/authn.md deleted file mode 100644 index 3b40738f305..00000000000 --- a/docs/v0.3.0/user/authn.md +++ /dev/null @@ -1,94 +0,0 @@ -# Request Authentication - -This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks -if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only -supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. - -## Installation - -Follow the steps from the [Quickstart](quickstart.md) guide to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Configuration - -Allow requests with a valid JWT by creating an [AuthenticationFilter][] and referencing it from the example HTTPRoute. - -```shell -kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.3.0/examples/kubernetes/authn/jwt.yaml -``` - -The HTTPRoute is now updated to authenticate requests for `/foo` and allow unauthenticated requests to `/bar`. The -`/foo` route rule references an AuthenticationFilter that provides the JWT authentication configuration. - -Verify the HTTPRoute configuration and status: - -```shell -kubectl get httproute/backend -o yaml -``` - -The AuthenticationFilter is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] -provider for authenticating the JWT. - -Verify the AuthenticationFilter configuration: - -```shell -kubectl get authenticationfilter/jwt-example -o yaml -``` - -## Testing - -Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](quickstart.md) guide is set. If not, follow the -Quickstart instructions to set the variable. - -```shell -echo $GATEWAY_HOST -``` - -Verify that requests to `/foo` are denied without a JWT: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo -``` - -A `401` HTTP response code should be returned. - -Get the JWT used for testing request authentication: - -```shell -TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - -``` - -__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's -header. - -Verify that a request to `/foo` with a valid JWT is allowed: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo -``` - -A `200` HTTP response code should be returned. - -Verify that requests to `/bar` are allowed __without__ a JWT: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar -``` - -## Clean-Up - -Follow the steps from the [Quickstart](quickstart.md) guide to uninstall Envoy Gateway and the example manifest. - -Delete the AuthenticationFilter: - -```shell -kubectl delete authenticationfilter/jwt-example -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. - -[jwt]: https://tools.ietf.org/html/rfc7519 -[AuthenticationFilter]: https://gateway.envoyproxy.io/v0.3.0/api/extension_types.html#authenticationfilter -[jwks]: https://tools.ietf.org/html/rfc7517 diff --git a/docs/v0.3.0/user/grpc-routing.md b/docs/v0.3.0/user/grpc-routing.md deleted file mode 100644 index 31a25c44268..00000000000 --- a/docs/v0.3.0/user/grpc-routing.md +++ /dev/null @@ -1,100 +0,0 @@ -# GRPC Routing - -The [GRPCRoute][] resource allows users to configure gRPC routing by matching HTTP/2 traffic and forwarding it to backend gRPC servers. -To learn more about gRPC routing, refer to the [Gateway API documentation][]. - -## Prerequisites - -Install Envoy Gateway: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -## Installation - -Install the gRPC routing example resources: - -```shell -kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.3.0/examples/kubernetes/grpc-routing.yaml -``` - -The manifest installs a [GatewayClass][], [Gateway][], a Deployment, a Service, and a GRPCRoute resource. -The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. - -__Note:__ Envoy Gateway is configured by default to manage a GatewayClass with -`controllerName: gateway.envoyproxy.io/gatewayclass-controller`. - -## Verification - -Check the status of the GatewayClass: - -```shell -kubectl get gc --selector=example=grpc-routing -``` - -The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass. - -A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is -provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this -Gateway. Check the status of the Gateway: - -```shell -kubectl get gateways --selector=example=grpc-routing -``` - -The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also -provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend -services. - -Check the status of the GRPCRoute: - -```shell -kubectl get grpcroutes --selector=example=grpc-routing -o yaml -``` - -The status for the GRPCRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway. -The `example-route` matches any traffic for "grpc-example.com" and forwards it to the "yages" Service. - -## Testing the Configuration - -Before testing GRPC routing to the `yages` backend, get the Gateway's address. - -```shell -export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}') -``` - -Test GRPC routing to the `yages` backend using the [grpcurl][] command. - -```shell -grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping -``` - -You should see the below response - -```shell -{ - "text": "pong" -} -``` - -Envoy Gateway also supports [gRPC-Web][] requests for this configuration. The below `curl` command can be used to send a grpc-Web request with over HTTP/2. You should receive the same response seen in the previous command. - -```shell -curl --http2-prior-knowledge -s ${GATEWAY_HOST}:80/yages.Echo/Ping -H 'Host: grpc-example.com' -H 'Content-Type: application/grpc-web-text' -H 'Accept: application/grpc-web-text' -XPOST -d'AAAAAAA=' | base64 -d -``` - - -[GRPCRoute]: https://gateway-api.sigs.k8s.io/api-types/grpcroute/ -[Gateway API documentation]: https://gateway-api.sigs.k8s.io/ -[GatewayClass]: https://gateway-api.sigs.k8s.io/api-types/gatewayclass/ -[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway/ -[Envoy proxy]: https://www.envoyproxy.io/ -[grpcurl]: https://github.com/fullstorydev/grpcurl -[gRPC-Web]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2 diff --git a/docs/v0.3.0/user/http-redirect.md b/docs/v0.3.0/user/http-redirect.md deleted file mode 100644 index dcd72749f36..00000000000 --- a/docs/v0.3.0/user/http-redirect.md +++ /dev/null @@ -1,127 +0,0 @@ -# HTTP Redirects - -The [HTTPRoute][] resource can issue redirects to clients or rewrite paths sent upstream using filters. Note that -HTTPRoute rules cannot use both filter types at once. Currently, Envoy Gateway only supports __core__ -[HTTPRoute filters][] which consist of `RequestRedirect` and `RequestHeaderModifier` at the time of this writing. To -learn more about HTTP routing, refer to the [Gateway API documentation][]. - -## Prerequisites - -Follow the steps from the [Secure Gateways](secure-gateways.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTPS. - -## Redirects - -Redirects return HTTP 3XX responses to a client, instructing it to retrieve a different resource. A -[`RequestRedirect` filter][req_filter] instructs Gateways to emit a redirect response to requests that match the rule. -For example, to issue a permanent redirect (301) from HTTP to HTTPS, configure `requestRedirect.statusCode=301` and -`requestRedirect.scheme="https"`: - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something", - "foo" - ], -... -``` - -## Setting Request Headers - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - "headers": { - "Accept": [ - "*/*" - ], - "Set-Header": [ - "foo" - ], -... -``` - -## Removing Request Headers - -Headers can be removed from a request by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something" - ], -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: X-Foo: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< x-foo: value1 -< add-header: foo -< -... - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "X-Foo: value1" - ] -... -``` - -## Setting Response Headers - -Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it -will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header -if the response already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: set-header: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< set-header: foo -< - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "set-header": value1" - ] -... -``` - -## Removing Response Headers - -Headers can be removed from a response by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it -will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header -if the response already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: remove-header: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "remove-header": value1" - ] -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-79665566f5-s589f" -... -``` - -## Multiple backendRefs - -If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is -configured. - -First, create a second instance of the example app from the quickstart: - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-75bcd4c969-lsxpz" -... -``` - -## Weighted backendRefs - -If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` -field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is -assumed to be `1`. - -The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of -requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the -HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. - -The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the -backend-2 service. - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 500 Internal Server Error -< server: envoy -< content-length: 0 -< -``` - -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/docs/v0.3.0/user/http-urlrewrite.md b/docs/v0.3.0/user/http-urlrewrite.md deleted file mode 100644 index 88e29c3269c..00000000000 --- a/docs/v0.3.0/user/http-urlrewrite.md +++ /dev/null @@ -1,295 +0,0 @@ -# HTTP URL Rewrite - -[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be -used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. - -## Prerequisites - -Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Rewrite URL Prefix Path - -You can configure to rewrite the prefix in the url like below. In this example, any curls to -`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. - -```shell -cat < GET /get/origin/path HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> - -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:03:28 GMT -< content-length: 503 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/replace/origin/path", - "host": "path.rewrite.example", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Original-Path": [ - "/get/origin/path" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "fd84b842-9937-4fb5-83c7-61470d854b90" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. - -## Rewrite URL Full Path - -You can configure to rewrite the fullpath in the url like below. In this example, any request sent to -`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to -`http://${GATEWAY_HOST}/force/replace/fullpath`. - -```shell -cat < GET /get/origin/path/extra HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:09:31 GMT -< content-length: 512 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/force/replace/fullpath", - "host": "path.rewrite.example", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Original-Path": [ - "/get/origin/path/extra" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "8ab774d6-9ffa-4faa-abbb-f45b0db00895" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is -`/force/replace/fullpath`. - -## Rewrite Host Name - -You can configure to rewrite the hostname like below. In this example, any requests sent to -`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. - -```shell -cat < GET /get HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:15:15 GMT -< content-length: 481 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "envoygateway.io", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Forwarded-Host": [ - "path.rewrite.example" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "39aa447c-97b9-45a3-a675-9fb266ab1af0" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. - -[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPURLRewriteFilter diff --git a/docs/v0.3.0/user/quickstart.md b/docs/v0.3.0/user/quickstart.md deleted file mode 100644 index a11d3cbe7b1..00000000000 --- a/docs/v0.3.0/user/quickstart.md +++ /dev/null @@ -1,97 +0,0 @@ -# Quickstart - -This guide will help you get started with Envoy Gateway in a few simple steps. - -## Prerequisites - -A Kubernetes cluster. - -__Note:__ Refer to the [Compatibility Matrix](../intro/compatibility.rst) for supported Kubernetes versions. - -## Installation - -Install the Gateway API CRDs and Envoy Gateway: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -Install the GatewayClass, Gateway, HTTPRoute and example app: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml -``` - -**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for -traffic on port 80 on its globally-routable IP address, to make it easy to use -browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is -using a privileged port (<1024), it will map this internally to an -unprivileged port, so that Envoy Gateway doesn't need additional privileges. -It's important to be aware of this mapping, since you may need to take it into -consideration when debugging. - -[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml - -## Testing the Configuration - -Get the name of the Envoy service created the by the example Gateway: - -```shell -export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward to the Envoy service: - -```shell -kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & -``` - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://localhost:8888/get -``` - -### External LoadBalancer Support - -You can also test the same functionality by sending traffic to the External IP. To get the external IP of the -Envoy service, run: - -```shell -export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -``` - -In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace -`ip` in the above command with `hostname`. - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get -``` - -## Clean-Up - -Use the steps in this section to uninstall everything from the quickstart guide. - -Delete the GatewayClass, Gateway, HTTPRoute and Example App: - -```shell -kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml --ignore-not-found=true -``` - -Delete the Gateway API CRDs and Envoy Gateway: - -```shell -kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml --ignore-not-found=true -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. diff --git a/docs/v0.3.0/user/rate-limit.md b/docs/v0.3.0/user/rate-limit.md deleted file mode 100644 index 446a82ee08a..00000000000 --- a/docs/v0.3.0/user/rate-limit.md +++ /dev/null @@ -1,489 +0,0 @@ -# Rate limit - -Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. - -Here are some reasons why you may want to implements Rate limits - -* To prevent malicious activity such as DDoS attacks. -* To prevent applications and its resources (such as a database) from getting overloaded. -* To create API limits based on user entitlements. - -Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied -i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit -if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. - -Envoy Gateway introduces a new CRD called [RateLimitFilter][] that allows the user to describe their rate limit intent. This instantiated resource -can be linked to a [HTTPRoute][] resource using an [ExtensionRef][] filter. - -## Prerequisites - -### Install Envoy Gateway - -* Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -### Install Redis - -* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. -Lets install a Redis deployment in the `redis-system` namespce. - -```shell -cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com -; (1 server found) -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 -;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 -;; WARNING: recursion requested but not available - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -; COOKIE: 24fb86eba96ebf62 (echoed) -;; QUESTION SECTION: -;foo.bar.com. IN A - -;; ADDITIONAL SECTION: -foo.bar.com. 0 IN A 10.244.0.19 -_udp.foo.bar.com. 0 IN SRV 0 0 42376 . - -;; Query time: 1 msec -;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) -;; WHEN: Fri Jan 13 10:20:34 UTC 2023 -;; MSG SIZE rcvd: 114 -``` - -## Clean-Up - -Follow the steps from the [Quickstart Guide](quickstart.md) to uninstall Envoy Gateway. - -Delete the CoreDNS example manifest and the UDPRoute: - -```shell -kubectl delete deploy/coredns -kubectl delete service/coredns -kubectl delete cm/coredns -kubectl delete udproute/coredns -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. - -[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute -[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/v0.3.0/configuration/listeners/udp_filters/udp_proxy diff --git a/docs/v0.3.0/user_docs.rst b/docs/v0.3.0/user_docs.rst deleted file mode 100644 index 3669c0613a0..00000000000 --- a/docs/v0.3.0/user_docs.rst +++ /dev/null @@ -1,22 +0,0 @@ -User Guides -=========== - -Learn how to deploy, use, and operate Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - user/quickstart - user/http-routing - user/http-redirect - user/http-urlrewrite - user/http-traffic-splitting - user/http-request-headers - user/http-response-headers - user/secure-gateways - user/tls-passthrough - user/tcp-routing - user/udp-routing - user/grpc-routing - user/authn - user/rate-limit diff --git a/docs/v0.4.0/about_docs.rst b/docs/v0.4.0/about_docs.rst deleted file mode 100644 index ecaa28247b9..00000000000 --- a/docs/v0.4.0/about_docs.rst +++ /dev/null @@ -1,9 +0,0 @@ -About the Documentation -======================= - -Learn how to contribute to Envoy Gateway documentation. - -.. toctree:: - :maxdepth: 1 - - dev/DOCS diff --git a/docs/v0.4.0/api/config_types.md b/docs/v0.4.0/api/config_types.md deleted file mode 100644 index 87118b7dbe4..00000000000 --- a/docs/v0.4.0/api/config_types.md +++ /dev/null @@ -1,448 +0,0 @@ -# API Reference - -## Packages -- [config.gateway.envoyproxy.io/v1alpha1](#configgatewayenvoyproxyiov1alpha1) - - -## config.gateway.envoyproxy.io/v1alpha1 - -Package v1alpha1 contains API schema definitions for the config.gateway.envoyproxy.io -API group. - - -### Resource Types -- [EnvoyGateway](#envoygateway) -- [EnvoyProxy](#envoyproxy) - - - -## EnvoyGateway - - - -EnvoyGateway is the schema for the envoygateways API. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `EnvoyGateway` -| `EnvoyGatewaySpec` _[EnvoyGatewaySpec](#envoygatewayspec)_ | EnvoyGatewaySpec defines the desired state of EnvoyGateway. | - - -## EnvoyGatewayFileProvider - - - -EnvoyGatewayFileProvider defines configuration for the File provider. - -_Appears in:_ -- [EnvoyGatewayProvider](#envoygatewayprovider) - - - -## EnvoyGatewayKubernetesProvider - - - -EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. - -_Appears in:_ -- [EnvoyGatewayProvider](#envoygatewayprovider) - -| Field | Description | -| --- | --- | -| `rateLimitDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. If unspecified, default settings for the manged Envoy ratelimit deployment resource are applied. | - - -## EnvoyGatewayProvider - - - -EnvoyGatewayProvider defines the desired configuration of a provider. - -_Appears in:_ -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `type` _[ProviderType](#providertype)_ | Type is the type of provider to use. Supported types are "Kubernetes". | -| `kubernetes` _[EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider)_ | Kubernetes defines the configuration of the Kubernetes provider. Kubernetes provides runtime configuration via the Kubernetes API. | -| `file` _[EnvoyGatewayFileProvider](#envoygatewayfileprovider)_ | File defines the configuration of the File provider. File provides runtime configuration defined by one or more files. This type is not implemented until https://github.com/envoyproxy/gateway/issues/1001 is fixed. | - - -## EnvoyGatewaySpec - - - -EnvoyGatewaySpec defines the desired state of Envoy Gateway. - -_Appears in:_ -- [EnvoyGateway](#envoygateway) - -| Field | Description | -| --- | --- | -| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | -| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | -| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | -| `extension` _[Extension](#extension)_ | Extension defines an extension to register for the Envoy Gateway Control Plane. | - - -## EnvoyProxy - - - -EnvoyProxy is the schema for the envoyproxies API. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `EnvoyProxy` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[EnvoyProxySpec](#envoyproxyspec)_ | EnvoyProxySpec defines the desired state of EnvoyProxy. | - - -## EnvoyProxyKubernetesProvider - - - -EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource provider. - -_Appears in:_ -- [EnvoyProxyProvider](#envoyproxyprovider) - -| Field | Description | -| --- | --- | -| `envoyDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | EnvoyDeployment defines the desired state of the Envoy deployment resource. If unspecified, default settings for the manged Envoy deployment resource are applied. | -| `envoyService` _[KubernetesServiceSpec](#kubernetesservicespec)_ | EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings for the manged Envoy service resource are applied. | - - -## EnvoyProxyProvider - - - -EnvoyProxyProvider defines the desired state of a resource provider. - -_Appears in:_ -- [EnvoyProxySpec](#envoyproxyspec) - -| Field | Description | -| --- | --- | -| `type` _[ProviderType](#providertype)_ | Type is the type of resource provider to use. A resource provider provides infrastructure resources for running the data plane, e.g. Envoy proxy, and optional auxiliary control planes. Supported types are "Kubernetes". | -| `kubernetes` _[EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider)_ | Kubernetes defines the desired state of the Kubernetes resource provider. Kubernetes provides infrastructure resources for running the data plane, e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings for managed Kubernetes resources are applied. | - - -## EnvoyProxySpec - - - -EnvoyProxySpec defines the desired state of EnvoyProxy. - -_Appears in:_ -- [EnvoyProxy](#envoyproxy) - -| Field | Description | -| --- | --- | -| `provider` _[EnvoyProxyProvider](#envoyproxyprovider)_ | Provider defines the desired resource provider and provider-specific configuration. If unspecified, the "Kubernetes" resource provider is used with default configuration parameters. | -| `logging` _[ProxyLogging](#proxylogging)_ | Logging defines logging parameters for managed proxies. If unspecified, default settings apply. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. | -| `bootstrap` _string_ | Bootstrap defines the Envoy Bootstrap as a YAML string. Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap to learn more about the syntax. If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration set by Envoy Gateway. Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources from it are not configurable and will result in the `EnvoyProxy` resource being rejected. Backward compatibility across minor versions is not guaranteed. We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. | - - - - -## Extension - - - -Extension defines the configuration for registering an extension to the Envoy Gateway control plane. - -_Appears in:_ -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `resources` _[GroupVersionKind](#groupversionkind) array_ | Resources defines the set of K8s resources the extension will handle. | -| `hooks` _[ExtensionHooks](#extensionhooks)_ | Hooks defines the set of hooks the extension supports | -| `service` _[ExtensionService](#extensionservice)_ | Service defines the configuration of the extension service that the Envoy Gateway Control Plane will call through extension hooks. | - - -## ExtensionHooks - - - -ExtensionHooks defines extension hooks across all supported runners - -_Appears in:_ -- [Extension](#extension) - -| Field | Description | -| --- | --- | -| `xdsTranslator` _[XDSTranslatorHooks](#xdstranslatorhooks)_ | XDSTranslator defines all the supported extension hooks for the xds-translator runner | - - -## ExtensionService - - - -ExtensionService defines the configuration for connecting to a registered extension service. - -_Appears in:_ -- [Extension](#extension) - -| Field | Description | -| --- | --- | -| `host` _string_ | Host define the extension service hostname. | -| `port` _integer_ | Port defines the port the extension service is exposed on. | -| `tls` _[ExtensionTLS](#extensiontls)_ | TLS defines TLS configuration for communication between Envoy Gateway and the extension service. | - - -## ExtensionTLS - - - -ExtensionTLS defines the TLS configuration when connecting to an extension service - -_Appears in:_ -- [ExtensionService](#extensionservice) - -| Field | Description | -| --- | --- | -| `certificateRef` _[SecretObjectReference](#secretobjectreference)_ | CertificateRef contains a references to objects (Kubernetes objects or otherwise) that contains a TLS certificate and private keys. These certificates are used to establish a TLS handshake to the extension server. - CertificateRef can only reference a Kubernetes Secret at this time. | - - -## Gateway - - - -Gateway defines the desired Gateway API configuration of Envoy Gateway. - -_Appears in:_ -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `controllerName` _string_ | ControllerName defines the name of the Gateway API controller. If unspecified, defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following for additional details: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass | - - -## GroupVersionKind - - - -GroupVersionKind unambiguously identifies a Kind. It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind - -_Appears in:_ -- [Extension](#extension) - -| Field | Description | -| --- | --- | -| `group` _string_ | | -| `version` _string_ | | -| `kind` _string_ | | - - -## KubernetesContainerSpec - - - -KubernetesContainerSpec defines the desired state of the Kubernetes container resource. - -_Appears in:_ -- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) - -| Field | Description | -| --- | --- | -| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources required by this container. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | -| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#securitycontext-v1-core)_ | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | -| `image` _string_ | Image specifies the EnvoyProxy container image to be used, instead of the default image. | - - -## KubernetesDeploymentSpec - - - -KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. - -_Appears in:_ -- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) -- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) - -| Field | Description | -| --- | --- | -| `replicas` _integer_ | Replicas is the number of desired pods. Defaults to 1. | -| `pod` _[KubernetesPodSpec](#kubernetespodspec)_ | Pod defines the desired annotations and securityContext of container. | -| `container` _[KubernetesContainerSpec](#kubernetescontainerspec)_ | Container defines the resources and securityContext of container. | - - -## KubernetesPodSpec - - - -KubernetesPodSpec defines the desired state of the Kubernetes pod resource. - -_Appears in:_ -- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) - -| Field | Description | -| --- | --- | -| `annotations` _object (keys:string, values:string)_ | Annotations are the annotations that should be appended to the pods. By default, no pod annotations are appended. | -| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)_ | SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. | - - -## KubernetesServiceSpec - - - -KubernetesServiceSpec defines the desired state of the Kubernetes service resource. - -_Appears in:_ -- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) - -| Field | Description | -| --- | --- | -| `annotations` _object (keys:string, values:string)_ | Annotations that should be appended to the service. By default, no annotations are appended. | -| `type` _[ServiceType](#servicetype)_ | Type determines how the Service is exposed. Defaults to LoadBalancer. Valid options are ClusterIP and LoadBalancer. "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. | - - -## LogComponent - -_Underlying type:_ `string` - -LogComponent defines a component that supports a configured logging level. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. - -_Appears in:_ -- [ProxyLogging](#proxylogging) - - - -## LogLevel - -_Underlying type:_ `string` - -LogLevel defines a log level for system logs. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. - -_Appears in:_ -- [ProxyLogging](#proxylogging) - - - -## ProviderType - -_Underlying type:_ `string` - -ProviderType defines the types of providers supported by Envoy Gateway. - -_Appears in:_ -- [EnvoyGatewayProvider](#envoygatewayprovider) -- [EnvoyProxyProvider](#envoyproxyprovider) - - - -## ProxyLogging - - - -ProxyLogging defines logging parameters for managed proxies. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. - -_Appears in:_ -- [EnvoyProxySpec](#envoyproxyspec) - -| Field | Description | -| --- | --- | -| `level` _object (keys:[LogComponent](#logcomponent), values:[LogLevel](#loglevel))_ | Level is a map of logging level per component, where the component is the key and the log level is the value. If unspecified, defaults to "System: Info". | - - -## RateLimit - - - -RateLimit defines the configuration associated with the Rate Limit Service used for Global Rate Limiting. - -_Appears in:_ -- [EnvoyGatewaySpec](#envoygatewayspec) - -| Field | Description | -| --- | --- | -| `backend` _[RateLimitDatabaseBackend](#ratelimitdatabasebackend)_ | Backend holds the configuration associated with the database backend used by the rate limit service to store state associated with global ratelimiting. | - - -## RateLimitDatabaseBackend - - - -RateLimitDatabaseBackend defines the configuration associated with the database backend used by the rate limit service. - -_Appears in:_ -- [RateLimit](#ratelimit) - -| Field | Description | -| --- | --- | -| `type` _[RateLimitDatabaseBackendType](#ratelimitdatabasebackendtype)_ | Type is the type of database backend to use. Supported types are: * Redis: Connects to a Redis database. | -| `redis` _[RateLimitRedisSettings](#ratelimitredissettings)_ | Redis defines the settings needed to connect to a Redis database. | - - -## RateLimitDatabaseBackendType - -_Underlying type:_ `string` - -RateLimitDatabaseBackendType specifies the types of database backend to be used by the rate limit service. - -_Appears in:_ -- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) - - - -## RateLimitRedisSettings - - - -RateLimitRedisSettings defines the configuration for connecting to a Redis database. - -_Appears in:_ -- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) - -| Field | Description | -| --- | --- | -| `url` _string_ | URL of the Redis Database. | - - -## ServiceType - -_Underlying type:_ `string` - -ServiceType string describes ingress methods for a service - -_Appears in:_ -- [KubernetesServiceSpec](#kubernetesservicespec) - - - -## XDSTranslatorHook - -_Underlying type:_ `string` - -XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support for the xds-translator - -_Appears in:_ -- [XDSTranslatorHooks](#xdstranslatorhooks) - - - -## XDSTranslatorHooks - - - -XDSTranslatorHooks contains all the pre and post hooks for the xds-translator runner. - -_Appears in:_ -- [ExtensionHooks](#extensionhooks) - -| Field | Description | -| --- | --- | -| `pre` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | -| `post` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | - - diff --git a/docs/v0.4.0/api/extension_types.md b/docs/v0.4.0/api/extension_types.md deleted file mode 100644 index 1b1da8e4cdc..00000000000 --- a/docs/v0.4.0/api/extension_types.md +++ /dev/null @@ -1,229 +0,0 @@ -# API Reference - -## Packages -- [gateway.envoyproxy.io/v1alpha1](#gatewayenvoyproxyiov1alpha1) - - -## gateway.envoyproxy.io/v1alpha1 - -Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io API group. - - -### Resource Types -- [AuthenticationFilter](#authenticationfilter) -- [RateLimitFilter](#ratelimitfilter) - - - -## AuthenticationFilter - - - - - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `AuthenticationFilter` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[AuthenticationFilterSpec](#authenticationfilterspec)_ | Spec defines the desired state of the AuthenticationFilter type. | - - -## AuthenticationFilterSpec - - - -AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. - -_Appears in:_ -- [AuthenticationFilter](#authenticationfilter) - -| Field | Description | -| --- | --- | -| `type` _[AuthenticationFilterType](#authenticationfiltertype)_ | Type defines the type of authentication provider to use. Supported provider types are "JWT". | -| `jwtProviders` _[JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) array_ | JWT defines the JSON Web Token (JWT) authentication provider type. When multiple jwtProviders are specified, the JWT is considered valid if any of the providers successfully validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | - - -## AuthenticationFilterType - -_Underlying type:_ `string` - -AuthenticationFilterType is a type of authentication provider. - -_Appears in:_ -- [AuthenticationFilterSpec](#authenticationfilterspec) - - - -## GlobalRateLimit - - - -GlobalRateLimit defines global rate limit configuration. - -_Appears in:_ -- [RateLimitFilterSpec](#ratelimitfilterspec) - -| Field | Description | -| --- | --- | -| `rules` _[RateLimitRule](#ratelimitrule) array_ | Rules are a list of RateLimit selectors and limits. Each rule and its associated limit is applied in a mutually exclusive way i.e. if multiple rules get selected, each of their associated limits get applied, so a single traffic request might increase the rate limit counters for multiple rules if selected. | - - -## HeaderMatch - - - -HeaderMatch defines the match attributes within the HTTP Headers of the request. - -_Appears in:_ -- [RateLimitSelectCondition](#ratelimitselectcondition) - -| Field | Description | -| --- | --- | -| `type` _[HeaderMatchType](#headermatchtype)_ | Type specifies how to match against the value of the header. | -| `name` _string_ | Name of the HTTP header. | -| `value` _string_ | Value within the HTTP header. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. Do not set this field when Type="Distinct", implying matching on any/all unique values within the header. | - - -## HeaderMatchType - -_Underlying type:_ `string` - -HeaderMatchType specifies the semantics of how HTTP header values should be compared. Valid HeaderMatchType values are "Exact", "RegularExpression", and "Distinct". - -_Appears in:_ -- [HeaderMatch](#headermatch) - - - -## JwtAuthenticationFilterProvider - - - -JwtAuthenticationFilterProvider defines the JSON Web Token (JWT) authentication provider type and how JWTs should be verified: - -_Appears in:_ -- [AuthenticationFilterSpec](#authenticationfilterspec) - -| Field | Description | -| --- | --- | -| `name` _string_ | Name defines a unique name for the JWT provider. A name can have a variety of forms, including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. | -| `issuer` _string_ | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. | -| `audiences` _string array_ | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. | -| `remoteJWKS` _[RemoteJWKS](#remotejwks)_ | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. | - - -## RateLimitFilter - - - -RateLimitFilter allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. - - - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` -| `kind` _string_ | `RateLimitFilter` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[RateLimitFilterSpec](#ratelimitfilterspec)_ | Spec defines the desired state of RateLimitFilter. | - - -## RateLimitFilterSpec - - - -RateLimitFilterSpec defines the desired state of RateLimitFilter. - -_Appears in:_ -- [RateLimitFilter](#ratelimitfilter) - -| Field | Description | -| --- | --- | -| `type` _[RateLimitType](#ratelimittype)_ | Type decides the scope for the RateLimits. Valid RateLimitType values are "Global". | -| `global` _[GlobalRateLimit](#globalratelimit)_ | Global defines global rate limit configuration. | - - -## RateLimitRule - - - -RateLimitRule defines the semantics for matching attributes from the incoming requests, and setting limits for them. - -_Appears in:_ -- [GlobalRateLimit](#globalratelimit) - -| Field | Description | -| --- | --- | -| `clientSelectors` _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | ClientSelectors holds the list of select conditions to select specific clients using attributes from the traffic flow. All individual select conditions must hold True for this rule and its limit to be applied. If this field is empty, it is equivalent to True, and the limit is applied. | -| `limit` _[RateLimitValue](#ratelimitvalue)_ | Limit holds the rate limit values. This limit is applied for traffic flows when the selectors compute to True, causing the request to be counted towards the limit. The limit is enforced and the request is ratelimited, i.e. a response with 429 HTTP status code is sent back to the client when the selected requests have reached the limit. | - - -## RateLimitSelectCondition - - - -RateLimitSelectCondition specifies the attributes within the traffic flow that can be used to select a subset of clients to be ratelimited. All the individual conditions must hold True for the overall condition to hold True. - -_Appears in:_ -- [RateLimitRule](#ratelimitrule) - -| Field | Description | -| --- | --- | -| `headers` _[HeaderMatch](#headermatch) array_ | Headers is a list of request headers to match. Multiple header values are ANDed together, meaning, a request MUST match all the specified headers. | -| `sourceIP` _string_ | SourceIP is the IP CIDR that represents the range of Source IP Addresses of the client. These could also be the intermediate addresses through which the request has flown through and is part of the `X-Forwarded-For` header. For example, `192.168.0.1/32`, `192.168.0.0/24`, `001:db8::/64`. All IP Addresses within the specified SourceIP CIDR are treated as a single client selector and share the same rate limit bucket. | - - -## RateLimitType - -_Underlying type:_ `string` - -RateLimitType specifies the types of RateLimiting. - -_Appears in:_ -- [RateLimitFilterSpec](#ratelimitfilterspec) - - - -## RateLimitUnit - -_Underlying type:_ `string` - -RateLimitUnit specifies the intervals for setting rate limits. Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". - -_Appears in:_ -- [RateLimitValue](#ratelimitvalue) - - - -## RateLimitValue - - - -RateLimitValue defines the limits for rate limiting. - -_Appears in:_ -- [RateLimitRule](#ratelimitrule) - -| Field | Description | -| --- | --- | -| `requests` _integer_ | | -| `unit` _[RateLimitUnit](#ratelimitunit)_ | | - - -## RemoteJWKS - - - -RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. - -_Appears in:_ -- [JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) - -| Field | Description | -| --- | --- | -| `uri` _string_ | URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to validate the server certificate. | - - diff --git a/docs/v0.4.0/api_docs.rst b/docs/v0.4.0/api_docs.rst deleted file mode 100644 index b470527c50c..00000000000 --- a/docs/v0.4.0/api_docs.rst +++ /dev/null @@ -1,10 +0,0 @@ -API Docs -============== - -API reference documentation for Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - Config APIs - Extension APIs diff --git a/docs/v0.4.0/conf.py b/docs/v0.4.0/conf.py deleted file mode 100644 index 76ef5717548..00000000000 --- a/docs/v0.4.0/conf.py +++ /dev/null @@ -1,43 +0,0 @@ -import os - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.duration', - 'sphinx.ext.autosectionlabel', - 'myst_parser', -] - -autosectionlabel_prefix_document = True -myst_heading_anchors = 3 - -html_theme = 'alabaster' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -version = os.environ["BUILD_VERSION"] -envoyVersion = os.environ["ENVOY_PROXY_VERSION"] -gatewayAPIVersion = os.environ["GATEWAYAPI_VERSION"] - -project = 'Envoy Gateway' -author = 'Envoy Gateway Project Authors' - -copyright = 'Envoy Gateway Project Authors | GitHub | Latest Docs' - -source_suffix = { - '.rst': 'restructuredtext', - '.md': 'markdown', -} - -variables_to_export = [ - "version", - "envoyVersion", - "gatewayAPIVersion", -] - -frozen_locals = dict(locals()) -rst_epilog = '\n'.join(map(lambda x: f".. |{x}| replace:: {frozen_locals[x]}", variables_to_export)) -del frozen_locals diff --git a/docs/v0.4.0/design/bootstrap.md b/docs/v0.4.0/design/bootstrap.md deleted file mode 100644 index 30f1b52116f..00000000000 --- a/docs/v0.4.0/design/bootstrap.md +++ /dev/null @@ -1,379 +0,0 @@ -# Bootstrap Design - -## Overview - -[Issue 31][] specifies the need for allowing advanced users to specify their custom -Envoy Bootstrap configuration rather than using the default Bootstrap configuration -defined in Envoy Gateway. This allows advanced users to extend Envoy Gateway and -support their custom use cases such setting up tracing and stats configuration -that is not supported by Envoy Gateway. - -## Goals - -* Define an API field to allow a user to specify a custom Bootstrap -* Provide tooling to allow the user to generate the default Bootstrap configuration - as well as validate their custom Bootstrap. - -## Non Goals - -* Allow user to configure only a section of the Bootstrap - -## API - -Leverage the existing [EnvoyProxy][] resource which can be attached to the [GatewayClass][] using -the [parametersRef][] field, and define a `Bootstrap` field within the resource. If this field is set, -the value is used as the Bootstrap configuration for all managed Envoy Proxies created by Envoy Gateway. - -```go -// EnvoyProxySpec defines the desired state of EnvoyProxy. -type EnvoyProxySpec struct { - ...... - // Bootstrap defines the Envoy Bootstrap as a YAML string. - // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap - // to learn more about the syntax. - // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration - // set by Envoy Gateway. - // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources - // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. - // Backward compatibility across minor versions is not guaranteed. - // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default - // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. - // - // +optional - Bootstrap *string `json:"bootstrap,omitempty"` -} -``` - -## Tooling - -A CLI tool `egctl x translate` will be provided to the user to help generate a working Bootstrap configuration. -Here is an example where a user inputs a `GatewayClass` and the CLI generates the `EnvoyProxy` resource with the `Bootstrap` field populated. - -``` -cat < /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: {} -EOF -``` - -This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. - -The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: Kubernetes - kubernetes: - foo: bar -EOF -``` - -__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for -illustration purposes only. - -The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the -File provider: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -provider: - type: File - file: - foo: bar -EOF -``` - -__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration -purposes only. - -Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use -default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to -manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: - -```yaml -$ cat << EOF > /etc/envoy-gateway/config.yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -gateway: - controllerName: foo -``` - -With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: - -```shell -$ ./envoy-gateway -``` - -## Data Plane API - -The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. -Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through -`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure -configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: - -* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane - infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. -* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: - > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the - > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are - > not propagated down to existing Gateways. - -The initial `EnvoyProxy` API: - -```go -// gateway/api/config/v1alpha1/envoyproxy.go - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EnvoyProxy is the Schema for the envoyproxies API. -type EnvoyProxy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EnvoyProxySpec `json:"spec,omitempty"` - Status EnvoyProxyStatus `json:"status,omitempty"` -} - -// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure -// configuration. -type EnvoyProxySpec struct { - // Undefined by this design spec. -} - -// EnvoyProxyStatus defines the observed state of EnvoyProxy. -type EnvoyProxyStatus struct { - // Undefined by this design spec. -} -``` - -The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use -cases are better understood. - -### Data Plane Configuration - -GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is -running with the Kubernetes provider. - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 -``` - -Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration -parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening -on port 80. - -The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer -service: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: example-class -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parametersRef: - name: example-config - group: config.gateway.envoyproxy.io - kind: EnvoyProxy ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: example-gateway -spec: - gatewayClassName: example-class - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: example-config -spec: - networkPublishing: - type: ClusterIPService -``` - -__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. - -[issue_51]: https://github.com/envoyproxy/gateway/issues/51 -[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md -[gw_api]: https://gateway-api.sigs.k8s.io/ -[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/docs/v0.4.0/design/egctl.md b/docs/v0.4.0/design/egctl.md deleted file mode 100644 index 7989ff49e5e..00000000000 --- a/docs/v0.4.0/design/egctl.md +++ /dev/null @@ -1,57 +0,0 @@ -# egctl Design - -## Motivation - -EG should provide a command line tool with following capabilities: - -- Collect configuration from envoy proxy and gateway -- Analyse system configuration to diagnose any issues in envoy gateway - -This tool is named `egctl`. - -## Syntax - -Use the following syntax to run `egctl` commands from your terminal window: - -```console -egctl [command] [entity] [name] [flags] -``` - -where `command`, `name`, and `flags` are: - -* `command`: Specifies the operation that you want to perform on one or more resources, - for example `config`, `version`. - -* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. - -* `name`: Specifies the name of the specified instance. - -* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. - -If you need help, run `egctl help` from the terminal window. - -## Operation - -The following table includes short descriptions and the general syntax for all the `egctl` operations: - -| Operation | Syntax | Description | -| --------------| -------------------------------- | -------------------------------------------------------------------------------------| -| `version` | `egctl version` | Prints out build version information. | -| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | -| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | -| `experimental`| `egctl experimental` | Subcommand for experimental features. These do not guarantee backwards compatibility | - -## Examples - -Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: - -```console -# Retrieve all information about proxy configuration from envoy -egctl config envoy-proxy all - -# Retrieve listener information about proxy configuration from envoy -egctl config envoy-proxy listener - -# Retrieve information about envoy gateway -egctl config envoy-gateway -``` diff --git a/docs/v0.4.0/design/extending-envoy-gateway.md b/docs/v0.4.0/design/extending-envoy-gateway.md deleted file mode 100644 index 61278025eb0..00000000000 --- a/docs/v0.4.0/design/extending-envoy-gateway.md +++ /dev/null @@ -1,324 +0,0 @@ -# Envoy Gateway Extensions Design - -As outlined in the [official goals][] for the Envoy Gateway project, one of the main goals is to "provide a common foundation for vendors to build value-added products -without having to re-engineer fundamental interactions". Development of the Envoy Gateway project has been focused on developing the core features for the project and -Kubernetes Gateway API conformance. This system focuses on the “common foundation for vendors” component by introducing a way for vendors to extend Envoy Gateway. - -To meaningfully extend Envoy Gateway and provide additional features, Extensions need to be able to introduce their own custom resources and have a high level of control -over the configuration generated by Envoy Gateway. Simply applying some static xDS configuration patches or relying on the existing Gateway API resources are both insufficient on their own -as means to add larger features that require dynamic user-configuration. - -As an example, an extension developer may wish to provide their own out-of-the-box authentication filters that require configuration from the end-user. This is a scenario where the ability to introduce -custom resources and attach them to [HTTPRoute][]s as an [ExtensionRef][] is necessary. Providing the same feature through a series of xDS patch resources would be too cumbersome for many end-users that want to avoid -that level of complexity when managing their clusters. - -## Goals - -- Provide a foundation for extending the Envoy Gateway control plane -- Allow Extension Developers to introduce their own custom resources for extending the Gateway-API via [ExtensionRefs][], [policyAttachments][] (future) and [backendRefs][] (future). -- Extension developers should **NOT** have to maintain a custom fork of Envoy Gateway -- Provide a system for extending Envoy Gateway which allows extension projects to ship updates independent of Envoy Gateway's release schedule -- Modify the generated Envoy xDS config -- Setup a foundation for the initial iteration of Extending Envoy Gateway -- Allow an Extension to hook into the infra manager pipeline (future) - -## Non-Goals - -- The initial design does not capture every hook that Envoy Gateway will eventually support. -- Extend [Gateway API Policy Attachments][]. At some point, these will be addressed using this extension system, but the initial implementation omits these. -- Support multiple extensions at the same time. Due to the fact that extensions will be modifying xDS resources after they are generated, handling the order of extension execution for each individual hook point is a challenge. Additionally, there is no -real way to prevent one extension from overwriting or breaking modifications to xDS resources that were made by another extension that was executed first. - -## Overview - -Envoy Gateway can be extended by vendors by means of an extension server developed by the vendor and deployed alongside Envoy Gateway. -An extension server can make use of one or more pre/post hooks inside Envoy Gateway before and after its major components (translator, etc.) to allow the extension to modify the data going into or coming out of these components. -An extension can be created external to Envoy Gateway as its own Kubernetes deployment or loaded as a sidecar. gRPC is used for the calls between Envoy Gateway and an extension. In the hook call, Envoy Gateway sends data as well -as context information to the extension and expects a reply with a modified version of the data that was sent to the extension. Since extensions fundamentally alter the logic and data that Envoy Gateway provides, Extension projects assume responsibility for any bugs and issues -they create as a direct result of their modification of Envoy Gateway. - -## Diagram - -![Architecture](../images/extension-example.png) - -## Registering Extensions in Envoy Gateway - -Information about the extension that Envoy Gateway needs to load is configured in the Envoy Gateway config. - -An example configuration: - -```yaml -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyGateway -extension: - resources: - - group: example.myextension.io - version: v2 - kind: OAuth2Filter - hooks: - post: - - Route - - VirtualHost - - HTTPListener - - Translation - service: - host: my-extension.example - port: 443 - tls: - certificateRef: - name: my-secret - namespace: default -``` - -An extension must supply connection information in the `extension.service` field so that Envoy Gateway can communicate with the extension. The `tls` configuration is optional. - -If the extension wants Envoy Gateway to watch resources for it then the extension must configure the optional `extension.resources` field and supply a list of: - -- `group`: the API group of the resource -- `version`: the API version of the resource -- `kind`: the Kind of resource - -The extension can configure the `extension.hooks` field to specify which hook points it would like to support. If a given hook is not listed here then it will not be executed even -if the extension is configured properly. This allows extension developers to only opt-in to the hook points they want to make use of. - -This configuration is required to be provided at bootstrap and modifying the registered extension during runtime is not currently supported. -Envoy Gateway will keep track of the registered extension and its API `groups` and `kinds` when processing Gateway API resources. - -## Extending Gateway API and the Data Plane - -Envoy Gateway manages [Envoy][] deployments, which act as the data plane that handles actual user traffic. Users configure the data plane using the K8s Gateway API resources which Envoy -Gateway converts into [Envoy specific configuration (xDS)][] to send over to Envoy. - -Gateway API offers [ExtensionRef filters][] and [Policy Attachments][] as extension points for implementers to use. Envoy Gateway extends the Gateway API using these extension points to provide support for [rate limiting][] -and [authentication][] native to the project. The initial design of Envoy Gateway extensions will primarily focus on ExtensionRef filters so that extension developers can reference their own resources as HTTP Filters in the same way -that Envoy Gateway has native support for rate limiting and authentication filters. - -When Envoy Gateway encounters an [HTTPRoute][] or [GRPCRoute][] that has an `ExtensionRef` `filter` with a `group` and `kind` that Envoy Gateway does not support, it will first -check the registered extension to determine if it supports the referenced object before considering it a configuration error. - -This allows users to be able to reference additional filters provided by their Envoy Gateway Extension, in their `HTTPRoute`s / `GRPCRoute`s: - -```yaml -apiVersion: example.myextension.io/v1alpha1 -kind: OAuth2Filter -metadata: - name: oauth2-filter -spec: - ... - ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - clientSelectors: - - path: - type: PathPrefix - value: / - filters: - - type: ExtensionRef - extensionRef: - group: example.myextension.io - kind: OAuth2Filter - name: oauth2-filter - backendRefs: - - name: backend - port: 3000 -``` - -In order to enable the usage of new resources introduced by an extension for translation and xDS modification, Envoy Gateway provides hook points within the translation pipeline, where it calls out to the extension service registered in the [EnvoyGateway config][] -if they specify an `group` that matches the `group` of an `ExtensionRef` filter. The extension will then be able to modify the xDS that Envoy Gateway generated and send back the -modified configuration. If an extension is not registered or if the registered extension does not specify support for the `group` of an `ExtensionRef` filter then Envoy Gateway will treat it as an unknown resource -and provide an error to the user. - -**Note:** Currently (as of [v1beta1][]) Gateway API does not provide a means to specify the namespace or version of an object referenced as an `ExtensionRef`. The extension mechanism will assume that -the namespace of any `ExtensionRef` is the same as the namespace of the `HTTPRoute` or `GRPCRoute` it is attached to rather than treating the `name` field of an `ExtensionRef` as a `name.namespace` string. -If Gateway API adds support for these fields then the design of the Envoy Gateway extensions will be updated to support them. - -## Watching New Resources - -Envoy Gateway will dynamically create new watches on resources introduced by the registered Extension. It does so by using the [controller-runtime][] to create new watches on [Unstructured][] resources that match the `version`s, `group`s, and `kind`s that the -registered extension configured. When communicating with an extension, Envoy Gateway sends these Unstructured resources over to the extension. This eliminates the need for the extension to create its own watches which would have a strong chance of creating race conditions and reconciliation loops when resources change. When an extension receives the Unstructured resources from Envoy Gateway it can perform its own type validation on them. Currently we make the simplifying assumption that the registered extension's `Kinds` are filters referenced by `extensionRef` in `HTTPRouteFilter`s . Support for Policy attachments will be introduced at a later time. - -## xDS Hooks API - -Envoy Gateway supports the following hooks as the initial foundation of the Extension system. Additional hooks can be developed using this extension system at a later point as new use-cases and needs are discovered. The primary iteration of the extension hooks -focuses solely on the modification of xDS resources. - -### Route Modification Hook - -The [Route][] level Hook provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized. -Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the -Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to ext_authz filters. -The Post Route Modify hook also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that created this xDS route -This hook is always executed when an extension is loaded that has added `Route` to the `EnvoyProxy.extensions.hooks.post`, and only on Routes which were generated from an HTTPRoute that uses extension resources as externalRef filters. - -```go -// PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified -message PostRouteModifyRequest { - envoy.config.route.v3.Route route = 1; - PostRouteExtensionContext post_route_context = 2; -} - -// RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway -// additional context information can be added to this message as more use-cases are discovered -message PostRouteExtensionContext { - // Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute - repeated ExtensionResource extension_resources = 1; - - // hostnames are the fully qualified domain names attached to the HTTPRoute - repeated string hostnames = 2; -} - -// ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter -// extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension -// can marshal the bytes from this resource back into an unstructured.Unstructured and then -// perform type checking to obtain the resource. -message ExtensionResource { - bytes unstructured_bytes = 1; -} - -// PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent -// If an extension returns a nil Route then it will not be modified -message PostRouteModifyResponse { - envoy.config.route.v3.Route route = 1; -} -``` - -### VirtualHost Modification Hook - -The [VirtualHost][] Hook provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized. -An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway. -This hook is always executed when an extension is loaded that has added `VirtualHost` to the `EnvoyProxy.extensions.hooks.post`. -An extension may return nil to not make any changes to the VirtualHost. - -```protobuf -// PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified -message PostVirtualHostModifyRequest { - envoy.config.route.v3.VirtualHost virtual_host = 1; - PostVirtualHostExtensionContext post_virtual_host_context = 2; -} - -// Empty for now but we can add fields to the context as use-cases are discovered without -// breaking any clients that use the API -// additional context information can be added to this message as more use-cases are discovered -message PostVirtualHostExtensionContext {} - -// PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent -// If an extension returns a nil Virtual Host then it will not be modified -message PostVirtualHostModifyResponse { - envoy.config.route.v3.VirtualHost virtual_host = 1; -} -``` - -### HTTP Listener Modification Hook - -The HTTP [Listener][] modification hook is the broadest xDS modification Hook available and allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized. -This hook is always executed when an extension is loaded that has added `HTTPListener` to the `EnvoyProxy.extensions.hooks.post`. An extension may return nil -in order to not make any changes to the Listener. - -```protobuf -// PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified -message PostHTTPListenerModifyRequest { - envoy.config.listener.v3.Listener listener = 1; - PostHTTPListenerExtensionContext post_listener_context = 2; -} - -// Empty for now but we can add fields to the context as use-cases are discovered without -// breaking any clients that use the API -// additional context information can be added to this message as more use-cases are discovered -message PostHTTPListenerExtensionContext {} - -// PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent -// If an extension returns a nil Listener then it will not be modified -message PostHTTPListenerModifyResponse { - envoy.config.listener.v3.Listener listener = 1; -} -``` - -### Post xDS Translation Modify Hook - -The Post Translate Modify hook allows an extension to modify the clusters and secrets in the xDS config. -This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than -using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed. -An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension. -The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets -This hook is always executed when an extension is loaded that has added `Translation` to the `EnvoyProxy.extensions.hooks.post`. - -```protobuf -// PostTranslateModifyRequest currently sends only clusters and secrets to an extension. -// The extension is free to add/modify/remove the resources it received. -message PostTranslateModifyRequest { - PostTranslateExtensionContext post_translate_context = 1; - repeated envoy.config.cluster.v3.Cluster clusters = 2; - repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3; -} - -// PostTranslateModifyResponse is the expected response from an extension and contains -// the full list of xDS clusters and secrets to be used for the xDS config. -message PostTranslateModifyResponse { - repeated envoy.config.cluster.v3.Cluster clusters = 1; - repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 2; -} -``` - -### Extension Service - -Currently, an extension must implement all of the following hooks although it may return the input(s) it received -if no modification of the resource is desired. A future expansion of the extension hooks will allow an Extension to specify -with config which Hooks it would like to "subscribe" to and which Hooks it does not wish to support. These specific Hooks were chosen -in order to provide extensions with the ability to have both broad and specific control over xDS resources and to minimize the amount of data being sent. - -```protobuf -service EnvoyGatewayExtension { - rpc PostRouteModify (PostRouteModifyRequest) returns (PostRouteModifyResponse) {}; - rpc PostVirtualHostModify(PostVirtualHostModifyRequest) returns (PostVirtualHostModifyResponse) {}; - rpc PostHTTPListenerModify(PostHTTPListenerModifyRequest) returns (PostHTTPListenerModifyResponse) {}; - rpc PostTranslateModify(PostTranslateModifyRequest) returns (PostTranslateModifyResponse) {}; -} -``` - -## Design Decisions - -- Envoy Gateway watches new custom resources introduced by a loaded extension and passes the resources back to the extension when they are used. - - This decision was made to solve the problem about how resources introduced by an extension get watched. If an extension server watches its own resources then it would need some way to trigger an Envoy Gateway reconfigure when a resource that Envoy Gateway is not watching gets updated. Having Envoy Gateway watch all resources removes any concern about creating race confitions or reconcile loops that would result from Envoy Gateway and the extension server both having so much separate state that needs to be synchronized. -- The Extension Server takes ownership of producing the correct xDS configuration in the hook responses -- The Extension Server will be responsible for ensuring the performance of the hook processing time -- The Post xDS level gRPC hooks all currently send a context field even though it contains nothing for several hooks. These fields exist so that they can be updadated in the future to pass -additional information to extensions as new use-cases and needs are discovered. -- The initial design supplies the scaffolding for both "pre xDS" and "post xDS" hooks. Only the post hooks are currently implemented which operate on xDS resources after they have been generated. -The pre hooks will be implemented at a later date along with one or more hooks in the infra manager. The infra manager level hook(s) will exist to power use-cases such as dynamically creating Deployments/Services for the extension the -whenever Envoy Gateway creates an instance of Envoy Proxy. An extension developer might want to take advantage of this functionality to inject a new authorization service as a sidecar on the Envoy Proxy deployment for reduced latency. -- Multiple extensions are not be supported at the same time. Preventing conflict between multiple extensions that are mangling xDS resources is too difficult to ensure compatibility with and is likely to only generate issues. - -## Known Challenges - -Extending Envoy Gateway by using an external extension server which makes use of hook points in Envoy Gateway does comes with a few trade-offs. One known trade-off is the impact of the time that it takes for the hook calls to be executed. Since an extension would make use of hook points in Envoy Gateway that use gRPC for communication, the time it takes to perform these requests could become a concern for some extension developers. One way to minimize the request time of the hook calls is to load the extension server as a sidecar to Envoy Gateway to minimize the impact of networking on the hook calls. - -[official goals]: https://github.com/envoyproxy/gateway/blob/main/GOALS.md#extensibility -[ExtensionRef filters]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference -[ExtensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference -[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference -[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendObjectReference -[Gateway API Policy attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy -[Policy Attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy -[policyAttachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy -[Envoy]: https://www.envoyproxy.io/ -[Envoy specific configuration (xDS)]: https://www.envoyproxy.io/docs/envoy/v1.25.1/configuration/configuration -[v1beta1]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1 -[rate limiting]: https://gateway.envoyproxy.io/v0.3.0/user/rate-limit.html -[authentication]: https://gateway.envoyproxy.io/v0.3.0/user/authn.html -[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute -[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute -[EnvoyGateway config]: https://gateway.envoyproxy.io/v0.3.0/api/config_types.html#envoygateway -[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime -[Unstructured]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured -[Listener]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener -[VirtualHost]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost -[Route]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-route diff --git a/docs/v0.4.0/design/gatewayapi-translator.md b/docs/v0.4.0/design/gatewayapi-translator.md deleted file mode 100644 index 3cf0da94f5a..00000000000 --- a/docs/v0.4.0/design/gatewayapi-translator.md +++ /dev/null @@ -1,250 +0,0 @@ -# Gateway API Translator Design - -The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate -Representation (IR). - -## Assumptions - -Initially target core conformance features only, to be followed by extended conformance features. - -## Inputs and Outputs - -The main inputs to the Gateway API translator are: - -- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. - -__Note:__ ReferenceGrant is not fully implemented as of v0.2. - -The outputs of the Gateway API translator are: - -- Xds and Infra Internal Representations (IRs). -- Status updates for GatewayClass, Gateways, HTTPRoutes - -## Listener Compatibility - -Envoy Gateway follows Gateway API listener compatibility spec: -> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. - -__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. - -### Listener Compatibility Examples - -#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: "*.envoygateway.io" - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - hostname: whales.envoygateway.io -``` - -#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) - -```yaml -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1beta1 -metadata: - name: gateway-1 - namespace: envoy-gateway -spec: - gatewayClassName: envoy-gateway - listeners: - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All -``` - -## Computing Status - -Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway -must compute the appropriate status fields and conditions for managed resources. - -Status is computed and set for: - -- The managed GatewayClass (`gatewayclass.status.conditions`). -- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the - Envoy Deployment and Service status are also included to calculate Gateway status. -- Listeners for each Gateway (`gateway.status.listeners`). -- The ParentRef for each Route (`route.status.parents`). - -The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to -the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and -updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to -update resource status on the Kubernetes API server. - -## Outline - -The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway -API resources. - -1. Process Gateway Listeners - - Validate unique hostnames, ports, and protocols. - - Validate and compute supported kinds. - - Validate allowed namespaces (validate selector if specified). - - Validate TLS fields if specified, including resolving referenced Secrets. - -2. Process HTTPRoutes - - foreach route rule: - - compute matches - - [core] path exact, path prefix - - [core] header exact - - [extended] query param exact - - [extended] HTTP method - - compute filters - - [core] request header modifier (set/add/remove) - - [core] request redirect (hostname, statuscode) - - [extended] request mirror - - compute backends - - [core] Kubernetes services - - foreach route parent ref: - - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) - - foreach matching listener: - - foreach hostname intersection with route: - - add each computed route rule to host - -## Context Structs - -To help store, access and manipulate information as it's processed during the translation process, a set of context -structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support -processing. - -`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. - -```go -type GatewayContext struct { - // The managed Gateway - *v1beta1.Gateway - - // A list of Gateway ListenerContexts. - listeners []*ListenerContext -} -``` - -`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on -the associated Gateway. - -```go -type ListenerContext struct { - // The Gateway listener. - *v1beta1.Listener - - // The Gateway this Listener belongs to. - gateway *v1beta1.Gateway - - // An index used for managing this listener in the list of Gateway listeners. - listenerStatusIdx int - - // Only Routes in namespaces selected by the selector may be attached - // to the Gateway this listener belongs to. - namespaceSelector labels.Selector - - // The TLS Secret for this Listener, if applicable. - tlsSecret *v1.Secret -} -``` - -`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. - -```go -type RouteContext interface { - client.Object - - // GetRouteType returns the Kind of the Route object, HTTPRoute, - // TLSRoute, TCPRoute, UDPRoute etc. - GetRouteType() string - - // GetHostnames returns the hosts targeted by the Route object. - GetHostnames() []string - - // GetParentReferences returns the ParentReference of the Route object. - GetParentReferences() []v1beta1.ParentReference - - // GetRouteParentContext returns RouteParentContext by using the Route - // objects' ParentReference. - GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext -} -``` - -[message bus]: watching.md diff --git a/docs/v0.4.0/design/rate-limit.md b/docs/v0.4.0/design/rate-limit.md deleted file mode 100644 index b3e88e2ae99..00000000000 --- a/docs/v0.4.0/design/rate-limit.md +++ /dev/null @@ -1,346 +0,0 @@ -# Rate Limit Design - -## Overview - -Rate limit is a feature that allows the user to limit the number of incoming requests -to a predefined value based on attributes within the traffic flow. - -Here are some reasons why a user may want to implements Rate limits - -* To prevent malicious activity such as DDoS attacks. -* To prevent applications and its resources (such as a database) from getting overloaded. -* To create API limits based on user entitlements. - -## Scope Types - -The rate limit type here describes the scope of rate limits. - -* Global - In this case, the rate limit is common across all the instances of Envoy proxies -where its applied i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is -10 requests/second, this limit is common and will be hit if 5 requests pass through the first replica -and 5 requests pass through the second replica within the same second. - -* Local - In this case, the rate limits are specific to each instance/replica of Envoy running. -Note - This is not part of the initial design and will be added as a future enhancement. - -## Match Types - -### Rate limit a specific traffic flow - -* Here is an example of a ratelimit implemented by the application developer to limit a specific user -by matching on a custom `x-user-id` header with a value set to `one` - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-specific-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - name: x-user-id - value: one - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-specific-user - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit all traffic flows - -* Here is an example of a rate limit implemented by the application developer that limits the total requests made -to a specific route to safeguard health of internal application components. In this case, no specific `headers` match -is specified, and the rate limit is applied to all traffic flows accepted by this `HTTPRoute`. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-all-requests -spec: - type: Global - global: - rules: - - limit: - requests: 1000 - unit: Second ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-all-requests - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit per distinct value - -* Here is an example of a rate limit implemented by the application developer to limit any unique user -by matching on a custom `x-user-id` header. Here, user A (recognised from the traffic flow using the header -`x-user-id` and value `a`) will be rate limited at 10 requests/hour and so will user B -(recognised from the traffic flow using the header `x-user-id` and value `b`). - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-per-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - type: Distinct - name: x-user-id - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-per-user - backendRefs: - - name: backend - port: 3000 -``` - -### Rate limit per source IP - -* Here is an example of a rate limit implemented by the application developer that limits the total requests made -to a specific route by matching on source IP. In this case, requests from `x.x.x.x` will be rate limited at 10 requests/hour. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-per-ip -spec: - type: Global - global: - rules: - - clientSelectors: - - sourceIP: x.x.x.x/32 - limit: - requests: 10 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-per-user - backendRefs: - - name: backend - port: 3000 -``` - -## Multiple RateLimitFilters, rules and clientSelectors -* Users can create multiple `RateLimitFilter`s and apply it to the same `HTTPRoute`. In such a case each -`RateLimitFilter` will be applied to the route and matched (and limited) in a mutually exclusive way, independent of each other. -* Rate limits are applied for each `RateLimitFilter` `rule` when ALL the conditions under `clientSelectors` hold true. - -Here's an example highlighting this - - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-all-safeguard-app -spec: - type: Global - global: - rules: - - limit: - requests: 100 - unit: Second ---- - -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-per-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - type: Distinct - name: x-user-id - limit: - requests: 1000 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-per-user - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-all-safeguard-app - backendRefs: - - name: backend - port: 3000 -``` - -* The user has created two `RateLimitFilter`s and has attached it to a `HTTPRoute` - one(`ratelimit-all-safeguard-app`) to -ensure that the backend does not get overwhelmed with requests, any excess requests are rate limited irrespective of -the attributes within the traffic flow, and another(`ratelimit-per-user`) to rate limit each distinct user client -who can be differentiated using the `x-user-id` header, to ensure that each client does not make exessive requests to the backend. -* If user `baz` (identified with the header and value of `x-user-id: baz`) sends 90 requests within the first second, and -user `bar` sends 11 more requests during that same interval of 1 second, and user `bar` sends the 101th request within that second, -the rule defined in `ratelimit-all-safeguard-app` gets activated and Envoy Gateway will ratelimit the request sent by `bar` (and any other -request sent within that 1 second). After 1 second, the rate limit counter associated with the `ratelimit-all-safeguard-app` rule -is reset and again evaluated. -* If user `bar` also ends up sending 90 more requests within the hour, summing up `bar`'s total request count to 101, the rate limit rule -defined within `ratelimit-per-user` will get activated, and `bar`'s requests will be rate limited again until the hour interval ends. -* Within the same above hour, if `baz` sends 991 more requests, summing up `baz`'s total request count to 1001, the rate limit rule defined -within `ratelimit-per-user` will get activated for `baz`, and `baz`'s requests will also be rate limited until the hour interval ends. - -## Design Decisions - -* The initial design uses an Extension filter to apply the Rate Limit functionality on a specific [HTTPRoute][]. -This was preferred over the [PolicyAttachment][] extension mechanism, because it is unclear whether Rate Limit -will be required to be enforced or overridden by the platform administrator or not. -* The RateLimitFilter can only be applied as a filter to a [HTTPRouteRule[], applying it across all backends within a [HTTPRoute][] -and cannot be applied a filter within a [HTTPBackendRef][] for a specific backend. -* The [HTTPRoute][] API has a [matches][] field within each [rule][] to select a specific traffic flow to be routed to -the destination backend. The RateLimitFilter API that can be attached to an HTTPRoute via an [extensionRef][] filter, -also has a `clientSelectors` field within each `rule` to select attributes within the traffic flow to rate limit specific clients. -The two levels of selectors/matches allow for flexibility and aim to hold match information specific to its use, allowing the author/owner -of each configuration to be different. It also allows the `clientSelectors` field within the RateLimitFilter to be enhanced with other matchable -attribute such as [IP subnet][] in the future that are not relevant in the [HTTPRoute][] API. - -## Implementation Details - -### Global Rate limiting - -* [Global rate limiting][] in Envoy Proxy can be achieved using the following - - * [Actions][] can be conifgured per [xDS Route][]. - * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] - defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined - using a [rate limit filter][]. - * Based on information received by the rate limit service and its programmed configuration, a decision is computed, whether to rate limit - the HTTP Request or not, and is sent back to Envoy, which enforces this decision on the data plane. -* Envoy Gateway will leverage this Envoy Proxy feature by - - * Translating the user facing RateLimitFilter API into Rate limit [Actions][] as well as Rate limit service configuration to implement - the desired API intent. - * Envoy Gateway will use the existing [reference implementation][] of the rate limit service. - * The Infrastructure administrator will need to enable the rate limit service using new settings that will be defined in the [EnvoyGateway][] config API. - * The xDS IR will be enhanced to hold the user facing rate limit intent. - * The xDS Translator will be enhanced to translate the rate limit field within the xDS IR into Rate limit [Actions][] as well as instantiate the [rate limit filter][]. - * A new runner called `rate-limit` will be added that subscribes to the xDS IR messages and translates it into a new Rate Limit Infra IR which contains - the [rate limit service configuration][] as well as other information needed to deploy the rate limit service. - * The infrastructure service will be enhanced to subscribe to the Rate Limit Infra IR and deploy a provider specific rate limit service runnable entity. - * A Status field within the RateLimitFilter API will be added to reflect whether the specific configuration was programmed correctly in these multiple locations or not. - -[PolicyAttachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/ -[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute -[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPBackendRef -[matches]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch -[rule]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch -[extensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType -[IP subnet]: https://en.wikipedia.org/wiki/Subnetwork -[Actions]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit-action -[descriptors]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter.html?highlight=descriptor#example-1 -[Global rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting -[xDS Route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-routeaction -[rate limit filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit -[rate limit service]: https://www.envoyproxy.io/docs/envoy/latest/configuration/other_features/rate_limit#config-rate-limit-service -[reference implementation]: https://github.com/envoyproxy/ratelimit -[EnvoyGateway]: https://github.com/envoyproxy/gateway/blob/main/api/config/v1alpha1/envoygateway_types.go -[rate limit service configuration]: https://github.com/envoyproxy/ratelimit#configuration diff --git a/docs/v0.4.0/design/request-authentication.md b/docs/v0.4.0/design/request-authentication.md deleted file mode 100644 index 7f75fa07291..00000000000 --- a/docs/v0.4.0/design/request-authentication.md +++ /dev/null @@ -1,513 +0,0 @@ -# Request Authentication Design - -## Overview - -[Issue 336][] specifies the need for exposing a user-facing API to configure request authentication. Request -authentication is defined as an authentication mechanism to be enforced by Envoy on a per-request basis. A connection -will be rejected if it contains invalid authentication information, based on the `AuthenticationFilter` API type -proposed in this design document. - -Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and -implementation-specific API [support levels][] for implementors such as Envoy Gateway to expose features. Since -implementing request authentication is not covered by `Core` or `Extended` APIs, an `Implementation-specific` API will -be created for this purpose. - -## Goals - -* Define an API for configuring request authentication. -* Implement [JWT] as the first supported authentication type. -* Allow users that manage routes, e.g. [HTTPRoute][], to authenticate matching requests before forwarding to a backend - service. -* Support HTTPRoutes as an Authentication API referent. HTTPRoute provides multiple [extension points][]. The - [HTTPRouteFilter][] is the extension point supported by the Authentication API. - -## Non-Goals - -* Allow infrastructure administrators to override or establish default authentication policies. -* Support referents other than HTTPRoute. -* Support Gateway API extension points other than HTTPRouteFilter. - -## Use-Cases - -These use-cases are presented as an aid for how users may attempt to utilize the outputs of the design. They are not an -exhaustive list of features for authentication support in Envoy Gateway. - -As a Service Producer, I need the ability to: -* Authenticate a request before forwarding it to a backend service. -* Have different authentication mechanisms per route rule. -* Choose from different authentication mechanisms supported by Envoy, e.g. OIDC. - -### Authentication API Type - -The Authentication API type defines authentication configuration for authenticating requests through managed Envoy -proxies. - -```go -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - -) - -type AuthenticationFilter struct { - metav1.TypeMeta - metav1.ObjectMeta - - // Spec defines the desired state of the Authentication type. - Spec AuthenticationFilterSpec - - // Note: The status sub-resource has been excluded but may be added in the future. -} - -// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. -// +union -type AuthenticationFilterSpec struct { - // Type defines the type of authentication provider to use. Supported provider types are: - // - // * JWT: A provider that uses JSON Web Token (JWT) for authenticating requests. - // - // +unionDiscriminator - Type AuthenticationFilterType - - // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple - // jwtProviders are specified, the JWT is considered valid if any of the providers - // successfully validate the JWT. - JwtProviders []JwtAuthenticationFilterProvider -} - -... -``` - -Refer to [PR 773][] for the detailed AuthenticationFilter API spec. - -The status subresource is not included in the AuthenticationFilter API. Status will be surfaced by an HTTPRoute that -references an AuthenticationFilter. For example, an HTTPRoute will surface the `ResolvedRefs=False` status condition if it -references an AuthenticationFilter that does not exist. It may be beneficial to add AuthenticationFilter status fields in the future -based on defined use-cases. For example, a remote [JWKS][] can be validated based on the specified URI and have an -appropriate status condition surfaced. - -#### AuthenticationFilter Example - -The following is an AuthenticationFilter example with one JWT authentication provider: - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example -spec: - type: JWT - jwtProviders: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json - -``` - -__Note:__ `type` is a union type, allowing only one of any supported provider type such as `jwtProviders` to be -specified. - -The following is an example HTTPRoute configured to use the above JWT authentication provider: - -```yaml -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example -spec: - parentRefs: - - name: eg - hostnames: - - www.example.com - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example - backendRefs: - - name: backend - port: 3000 -``` - -Requests for `www.example.com/foo` will be authenticated using the referenced JWT provider before being forwarded to the -backend service named "backend". - -## Implementation Details - -The JWT authentication type is translated to an Envoy [JWT authentication filter][] and a cluster is created for each -remote [JWKS][]. The following examples provide additional details on how Gateway API and AuthenticationFilter resources are -translated into Envoy configuration. - -### Example 1: One Route with One JWT Provider - -The following cluster is created from the above HTTPRoute and AuthenticationFilter: - -```yaml -dynamic_clusters: - - name: foo.com|443 - load_assignment: - cluster_name: foo.com|443 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: foo.com - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: foo.com - common_tls_context: - validation_context: - match_subject_alt_names: - - exact: "*.foo.com" - trusted_ca: - filename: /etc/ssl/certs/ca-certificates.crt -``` - -A JWT authentication HTTP filter is added to the HTTP Connection Manager. For example: - -```yaml -dynamic_resources: - dynamic_listeners: - - name: example_listener - address: - socket_address: - address: 1.2.3.4 - port_value: 80 - filter_chains: - - filters: - - name: envoy.http_connection_manager - http_filters: - - name: envoy.filters.http.jwt_authn - typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication -``` - -This JWT authentication HTTP filter contains two fields: -* The `providers` field specifies how a JWT should be verified, such as where to extract the token, where to fetch the - public key ([JWKS][]) and where to output its payload. This field is built from the source resource `namespace-name`, and - the JWT provider name of an AuthenticationFilter. -* The `rules` field specifies matching rules and their requirements. If a request matches a rule, its requirement - applies. The requirement specifies which JWT providers should be used. This field is built from a HTTPRoute - `matches` rule that references the AuthenticationFilter. When a referenced Authentication specifies multiple - `jwtProviders`, the JWT is considered valid if __any__ of the providers successfully validate the JWT. - -The following JWT authentication HTTP filter `providers` configuration is created from the above AuthenticationFilter. - -```yaml -providers: - example: - issuer: https://www.example.com - audiences: - - foo.com - remote_jwks: - http_uri: - uri: https://foo.com/jwt/public-key/jwks.json - cluster: example_jwks_cluster - timeout: 1s -``` - -The following JWT authentication HTTP filter `rules` configuration is created from the above HTTPRoute. - -```yaml -rules: - - match: - prefix: /foo - requires: - provider_name: default-example-example -``` - -### Example 2: Two HTTPRoutes with Different AuthenticationFilters - -The following example contains: -* Two HTTPRoutes with different hostnames. -* Each HTTPRoute references a different AuthenticationFilter. -* Each AuthenticationFilter contains a different JWT provider. - -```yaml -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example1 -spec: - type: JWT - jwtProviders: - - name: example1 - issuer: https://www.example1.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: example2 -spec: - type: JWT - jwtProviders: - - name: example2 - issuer: https://www.example2.com - audiences: - - bar.com - remoteJwks: - uri: https://bar.com/jwt/public-key/jwks.json ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example1 -spec: - hostnames: - - www.example1.com - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: eg - rules: - - matches: - - path: - type: PathPrefix - value: /foo - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example1 - backendRefs: - - name: backend - port: 3000 ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: example2 -spec: - hostnames: - - www.example2.com - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: eg - rules: - - matches: - - path: - type: PathPrefix - value: /bar - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: example2 - backendRefs: - - name: backend2 - port: 3000 -``` - -The following xDS configuration is created from the above example resources: - -```yaml -configs: -... -dynamic_listeners: - - name: default-eg-http - ... - default_filter_chain: - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - '@type': >- - type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: http - rds: - config_source: - ... - route_config_name: default-eg-http - http_filters: - - name: envoy.filters.http.jwt_authn - typed_config: - '@type': >- - type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication - providers: - default-example1-example1: - issuer: https://www.example1.com - audiences: - - foo.com - remote_jwks: - http_uri: - uri: https://foo.com/jwt/public-key/jwks.json - cluster: default-example1-example1-jwt - default-example2-example2: - issuer: https://www.example2.com - audiences: - - bar.com - remote_jwks: - http_uri: - uri: https://bar.com/jwt/public-key/jwks.json - cluster: default-example2-example2-jwt - rules: - - match: - exact: /foo - requires: - provider_name: default-example1-example1 - - match: - exact: /bar - requires: - provider_name: default-example2-example2 - - name: envoy.filters.http.router - typed_config: - '@type': >- - type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -dynamic_route_configs: - - route_config: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - name: default-eg-http - virtual_hosts: - - name: default-eg-http - domains: - - '*' - routes: - - match: - prefix: /foo - headers: - - name: ':authority' - string_match: - exact: www.example1.com - route: - cluster: default-backend-rule-0-match-0-www.example1.com - - match: - prefix: /bar - headers: - - name: ':authority' - string_match: - exact: www.example2.com - route: - cluster: default-backend2-rule-0-match-0-www.example2.com -dynamic_active_clusters: - - cluster: - name: default-backend-rule-0-match-0-www.example.com - ... - endpoints: - - locality: {} - lb_endpoints: - - endpoint: - address: - socket_address: - address: $BACKEND_SERVICE1_IP - port_value: 3000 - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - name: default-backend-rule-1-match-0-www.example.com - ... - endpoints: - - locality: {} - lb_endpoints: - - endpoint: - address: - socket_address: - address: $BACKEND_SERVICE2_IP - port_value: 3000 -... -``` - -__Note:__ The JWT provider cluster and route is omitted from the above example for brevity. - -### Implementation Outline - -* Update the Kubernetes provider to get/watch AuthenticationFilter resources that are referenced by managed HTTPRoutes. - Add the referenced AuthenticationFilter object to the resource map and publish it. -* Update the resource translator to include the AuthenticationFilter API in HTTPRoute processing. -* Update the xDS translator to translate an AuthenticationFilter into xDS resources. The translator should perform the - following: - * Convert a list of JWT rules from the xds IR into an Envoy JWT filter config. - * Create a JWT authentication HTTP filter. - * Build the HTTP Connection Manager (HCM) HTTP filters. - * Build the HCM. - * When building the Listener, create an HCM for each filter-chain. - -## Adding Authentication Types - -Additional authentication types can be added in the future through the `AuthenticationFilterType` API. For -example, to add the `Foo` authentication type: - -Define the `Foo` authentication provider: - -```go -package v1alpha1 - -// FooAuthenticationFilterProvider defines the "Foo" authentication filter provider type. -type FooAuthenticationFilterProvider struct { - // TODO: Define fields of the Foo authentication filter provider type. -} -``` - -Add the `FooAuthenticationFilterProvider` type to `AuthenticationFilterSpec`: - -```go -package v1alpha1 - -type AuthenticationFilterSpec struct { - ... - - // Foo defines the Foo authentication type. For additional - // details, see: - // - // - // - // +optional - Foo *FooAuthenticationFilterProvider -} -``` - -Lastly, add the type to the `AuthenticationType` enum: - -```go -// AuthenticationType is a type of authentication provider. -// +kubebuilder:validation:Enum=JWT,FOO -type AuthenticationFilterType string - -const ( - // JwtAuthenticationProviderType is the JWT authentication provider type. - FooAuthenticationFilterProviderType AuthenticationFilterType = "FOO" -) -``` - -The AuthenticationFilter API should support additional authentication types in the future, for example: -- OAuth2 -- OIDC - -## Outstanding Questions - -- If Envoy Gateway owns the AuthenticationFilter API, is an xDS IR equivalent needed? -- Should local [JWKS][] be implemented before remote [JWKS][]? -- How should Envoy obtain the trusted CA for a remote [JWKS][]? -- Should HTTPS be the only supported scheme for remote [JWKS][]? -- Should OR'ing JWT providers be supported? -- Should Authentication provide status? -- Are the API field validation rules acceptable? - -[Issue 336]: https://github.com/envoyproxy/gateway/issues/336 -[Gateway API]: https://gateway-api.sigs.k8s.io/ -[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels -[JWT]: https://jwt.io/ -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[extension points]: https://gateway-api.sigs.k8s.io/concepts/api-overview/?h=extension#extension-points -[HTTPRouteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter -[JWKS]: https://www.rfc-editor.org/rfc/rfc7517 -[JWT authentication filter]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn -[PR 773]: https://github.com/envoyproxy/gateway/pull/733 diff --git a/docs/v0.4.0/design/roadmap.md b/docs/v0.4.0/design/roadmap.md deleted file mode 100644 index 65f88de1214..00000000000 --- a/docs/v0.4.0/design/roadmap.md +++ /dev/null @@ -1,88 +0,0 @@ -# Roadmap - -This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of -the project. - -## Contributing to the Roadmap - -- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use - case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update - this document accordingly. -- To help with an existing roadmap item, comment on or assign yourself to the associated issue. -- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A - maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be - discussed in an issue or a community meeting before implementing it. - -If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. -Look for issues with the `help wanted` label to get started. - -## Details - -Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific -roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by -contributing to the project. - -`Last Updated: April 2023` - -### [v0.2.0][v0.2.0]: Establish a Solid Foundation - -- Complete the core Envoy Gateway implementation- [Issue #60][60]. -- Establish initial testing, e2e, integration, etc- [Issue #64][64]. -- Establish user and developer project documentation- [Issue #17][17]. -- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. -- Setup a CI/CD pipeline- [Issue #63][63]. - -### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms - -- Support extended Gateway API fields [Issue #707][707]. -- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. -- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. -- Rate Limiting [Issue #670][670]. -- Authentication [Issue #336][336]. - -### [v0.4.0][v0.4.0]: Customizing Envoy Gateway - -- Extending Envoy Gateway control plane [Issue #20][20] -- Helm based installation for Envoy Gateway [Issue #650][650] -- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] -- Configuring xDS Bootstrap [Issue #31][31] - -### [v0.5.0][v0.5.0]: Observability and Scale - -- Observability for control plane and data plane [Issue #701][701]. -- Compute and document Envoy Gateway performance [Issue #1365][1365]. -- Allow users to configure xDS Resources [Issue #24][24]. - -### [v0.6.0][v0.6.0]: Preparation for GA - -- Envoy Gateway meets readiness criteria [Issue #1160][1160]. - -[issue]: https://github.com/envoyproxy/gateway/issues -[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing -[pr]: https://github.com/envoyproxy/gateway/compare -[milestones]: https://github.com/envoyproxy/gateway/milestones -[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 -[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 -[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 -[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 -[v0.6.0]: https://github.com/envoyproxy/gateway/milestone/15 -[17]: https://github.com/envoyproxy/gateway/issues/17 -[20]: https://github.com/envoyproxy/gateway/issues/20 -[24]: https://github.com/envoyproxy/gateway/issues/24 -[31]: https://github.com/envoyproxy/gateway/issues/31 -[60]: https://github.com/envoyproxy/gateway/issues/60 -[63]: https://github.com/envoyproxy/gateway/issues/63 -[64]: https://github.com/envoyproxy/gateway/issues/64 -[65]: https://github.com/envoyproxy/gateway/issues/65 -[336]: https://github.com/envoyproxy/gateway/issues/336 -[641]: https://github.com/envoyproxy/gateway/issues/641 -[642]: https://github.com/envoyproxy/gateway/issues/642 -[648]: https://github.com/envoyproxy/gateway/issues/648 -[650]: https://github.com/envoyproxy/gateway/issues/650 -[643]: https://github.com/envoyproxy/gateway/issues/643 -[670]: https://github.com/envoyproxy/gateway/issues/670 -[675]: https://github.com/envoyproxy/gateway/issues/675 -[701]: https://github.com/envoyproxy/gateway/issues/701 -[707]: https://github.com/envoyproxy/gateway/issues/707 -[1160]: https://github.com/envoyproxy/gateway/issues/1160 -[1365]: https://github.com/envoyproxy/gateway/issues/1365 diff --git a/docs/v0.4.0/design/system-design.md b/docs/v0.4.0/design/system-design.md deleted file mode 100644 index 86114be37fa..00000000000 --- a/docs/v0.4.0/design/system-design.md +++ /dev/null @@ -1,171 +0,0 @@ -# System Design - -## Goals - -* Define the system components needed to satisfy the requirements of Envoy Gateway. - -## Non-Goals - -* Create a detailed design and interface specification for each system component. - -## Terminology - -* Control Plane- A collection of inter-related software components for providing application gateway and routing - functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. - These services are detailed in the [components](#components) section. -* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. - -## Architecture - -![Architecture](../images/architecture.png) - -## Configuration - -Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through -Kubernetes resources, primarily [Gateway API][gw_api] objects. - -### Static Configuration - -Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, -configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the -configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. - -### Dynamic Configuration - -Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using -reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is -defined as Kubernetes resources that provide the following services: - -* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is - expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be - referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, - e.g. expose Envoy network endpoints using a `ClusterIP` service instead of a `LoadBalancer` service. -* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP - requests for "www.example.com" to a backend service running a web server. This configuration is expressed through - [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. - Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] - reference. - -## Components - -Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described -in the [Watching Components Design][wcd]. - -### Provider - -A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve -services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap -via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A -provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). - -#### Kubernetes Provider - -* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the - [dynamic configuration](#dynamic-configuration). -* Manages the data plane through Kubernetes API CRUD operations. -* Uses Kubernetes for Service discovery. -* Uses etcd (via Kubernetes API) to persist data. - -#### File Provider - -* Uses a file watcher to watch files in a directory that define the data plane configuration. -* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. -* Uses the host's DNS for Service discovery. -* If needed, the local filesystem is used to persist data. - -### Resource Watcher - -The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The -mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes -provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator -as output. - -### Resource Translator - -The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate -Representation (IR). It is responsible for: - -* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. -* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. - -__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. - -### Intermediate Representation (IR) - -The Intermediate Representation defines internal data models that external resources are translated into. This allows -Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR -used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. - -* Infra IR- Used as the internal definition of the managed data plane infrastructure. -* xDS IR- Used as the internal definition of the managed data plane xDS configuration. - -### xDS Translator - -The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. - -### xDS Server - -The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server -Protocol and is responsible for using xDS to configure the data plane. - -### Infra Manager - -The Infra Manager is a provider-specific component responsible for managing the following infrastructure: - -* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, - Service, etc. resources to run Envoy in a Kubernetes cluster. -* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require - external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning - and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to - users through the [Custom Route Filters][crf] extension. - -The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. - -## Design Decisions - -* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with - `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy - Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. - `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy - infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. -* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. - * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. - * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners - in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are - met: - * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies - either the “HTTPS” or “TLS” Protocol. - * Each Listener within the group specifies a unique "Hostname". - * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no - other Listener matches. - * Envoy Gateway does __not__ merge listeners across multiple Gateways. -* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. - * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. -* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. - * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. -* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. - -The draft for this document is [here][draft_design]. - -[gw_api]: https://gateway-api.sigs.k8s.io -[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass -[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway -[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute -[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute -[go_cp]: https://github.com/envoyproxy/go-control-plane -[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting -[rls]: https://github.com/envoyproxy/ratelimit -[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit -[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional -[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts -[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners -[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route -[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional -[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster -[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit -[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ -[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference -[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ -[ wcd ]: ./watching.md -[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 -[roadmap]: roadmap.md diff --git a/docs/v0.4.0/design/tcp-udp-design.md b/docs/v0.4.0/design/tcp-udp-design.md deleted file mode 100644 index 276221b897b..00000000000 --- a/docs/v0.4.0/design/tcp-udp-design.md +++ /dev/null @@ -1,47 +0,0 @@ -# TCP and UDP Proxy Design - -Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP -and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the -design decision. - -Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) - and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) -, so ideally, Envoy Gateway should also be able to work in these two modes: - -## Non-transparent Proxy Mode -For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the -TCP traffic from the downstream to the upstream. - -For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when -proxying the UDP datagrams to the upstream. - -In this mode, the upstream will see Envoy's IP address and port. - -## Transparent Proxy Mode -For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies -the TCP traffic from the downstream to the upstream. - -For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address -when proxying the UDP datagrams to the upstream. - -In this mode, the upstream will see the original downstream IP address and Envoy's mac address. - -Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward -the port number. - -## The Implications of Transparent Proxy Mode - -### Escalated Privilege -Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated -CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. - -### Routing -The upstream can see the original source IP, but the original port number won't be passed, so the return -traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back -to the right port number of the downstream, which requires routing at the upstream side to be set up. -In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. - -## The Design Decision (For Now) - -The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and -port of the deployed Envoy instance instead of the client. diff --git a/docs/v0.4.0/design/watching.md b/docs/v0.4.0/design/watching.md deleted file mode 100644 index b8477a30e2d..00000000000 --- a/docs/v0.4.0/design/watching.md +++ /dev/null @@ -1,117 +0,0 @@ -# Watching Components Design - -Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch -external resources, and "publish" what they see for other components to consume; others watch what another publishes and -act on it (such as the resource translator watches what the providers publish, and then publishes its own results that -are watched by another component). Some of these internally published results are consumed by multiple components. - -To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the -standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub -pattern. - -## Pub - -Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" -tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if -we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by -the IR translator: - - ```go - type ResourceTable struct { - // gateway classes are cluster-scoped; no namespace - GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] - - // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. - Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] - - HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] - } - ``` - -The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; -updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` -method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher -doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just -`.Store` its data and `watchable` will do the right thing. - -## Sub - -Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or -`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: - - ```go - func(ctx context.Context) error { - for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { - fullState := irInput{ - GatewayClasses: k8sTable.GatewayClasses.LoadAll(), - Gateways: k8sTable.Gateways.LoadAll(), - HTTPRoutes: snapshot.State, - } - translate(irInput) - } - } - ``` - -Or, to watch multiple maps in the same loop: - - ```go - func worker(ctx context.Context) error { - classCh := k8sTable.GatewayClasses.Subscribe(ctx) - gwCh := k8sTable.Gateways.Subscribe(ctx) - routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) - for ctx.Err() == nil { - var arg irInput - select { - case snapshot := <-classCh: - arg.GatewayClasses = snapshot.State - case snapshot := <-gwCh: - arg.Gateways = snapshot.State - case snapshot := <-routeCh: - arg.Routes = snapshot.State - } - if arg.GateWayClasses == nil { - arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() - } - if arg.GateWays == nil { - arg.Gateways = k8sTable.GateWays.LoadAll() - } - if arg.HTTPRoutes == nil { - arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() - } - translate(irInput) - } - } - ``` - -From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; -but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a -handy way to know when to run, `.Load` and friends can be used without subscribing. - -There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but -it's probably wise to just have one publisher for each map. - -The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when -`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple -mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in -to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of -each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need -to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. - -If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` -entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you -must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to -help with this. - -## Other Notes - -The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the -map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb -`chan`. - -A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types -be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and -so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can -generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate -methods for. - -[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/docs/v0.4.0/design_docs.rst b/docs/v0.4.0/design_docs.rst deleted file mode 100644 index f1ad756e21b..00000000000 --- a/docs/v0.4.0/design_docs.rst +++ /dev/null @@ -1,18 +0,0 @@ -Design Docs -=========== - -Learn about the internal details of Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - design/system-design - design/gatewayapi-translator - design/watching - design/config-api - design/tcp-udp-design - design/egctl - design/rate-limit - design/request-authentication - design/bootstrap - design/extending-envoy-gateway diff --git a/docs/v0.4.0/dev/CODEOWNERS.md b/docs/v0.4.0/dev/CODEOWNERS.md deleted file mode 100644 index d4229b6b23f..00000000000 --- a/docs/v0.4.0/dev/CODEOWNERS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Maintainers - -## The following maintainers, listed in alphabetical order, own everything - -- @AliceProxy -- @arkodg -- @skriss -- @Xunzhuo -- @youngnick -- @zirain - -## Emeritus Maintainers - -- @danehans -- @alexgervais diff --git a/docs/v0.4.0/dev/CODE_OF_CONDUCT.md b/docs/v0.4.0/dev/CODE_OF_CONDUCT.md deleted file mode 100644 index a0a295770f3..00000000000 --- a/docs/v0.4.0/dev/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,3 +0,0 @@ -# Community Code of Conduct - -Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/docs/v0.4.0/dev/CONTRIBUTING.md b/docs/v0.4.0/dev/CONTRIBUTING.md deleted file mode 100644 index 04d95bb675d..00000000000 --- a/docs/v0.4.0/dev/CONTRIBUTING.md +++ /dev/null @@ -1,186 +0,0 @@ -# Contributing - -We welcome contributions from the community. Please carefully review the [project goals](GOALS.md) -and following guidelines to streamline your contributions. - -## Communication - -* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no - one else is working on it and ask you to open a GitHub issue. -* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or - changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to - agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process - for major features is also important so that [affiliations with commit access](CODEOWNERS.md) can - come to agreement on the design. If it's appropriate to write a design document, the document must - be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable - location. -* Small patches and bug fixes don't need prior communication. - -## Inclusivity - -The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere -to the following guidelines for all code, APIs, and documentation: - -* The following words and phrases are not allowed: - * *Whitelist*: use allowlist instead. - * *Blacklist*: use denylist or blocklist instead. - * *Master*: use primary instead. - * *Slave*: use secondary or replica instead. -* Documentation should be written in an inclusive style. The [Google developer - documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent - reference on this topic. -* The above policy is not considered definitive and may be amended in the future as industry best - practices evolve. Additional comments on this topic may be provided by maintainers during code - review. - -## Submitting a PR - -* Fork the repo. -* Hack -* DCO sign-off each commit. This can be done with `git commit -s`. -* Submit your PR. -* Tests will automatically run for you. -* We will **not** merge any PR that is not passing tests. -* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage - build. If your PR cannot have 100% coverage for some reason please clearly explain why when you - open it. -* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/docs) folder of the repo as - well as the [changelog](../releases). -* All code comments and documentation are expected to have proper English grammar and punctuation. - If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try - to find some help but there are no guarantees. -* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary - and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. - Examples: - * "docs: fix grammar error" - * "feat(translator): add new feature" - * "fix: fix xx bug" - * "chore: change ci & build tools etc" -* Your PR commit message will be used as the commit message when your PR is merged. You should - update this field if your PR diverges during review. -* Your PR description should have details on what the PR does. If it fixes an existing issue it - should end with "Fixes #XXX". -* If your PR is co-authored or based on an earlier PR from another contributor, - please attribute them with `Co-authored-by: name `. See - GitHub's [multiple author - guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) - for further details. -* When all tests are passing and all other conditions described herein are satisfied, a maintainer - will be assigned to review and merge the PR. -* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits - are new commits and/or merges. We squash and merge so the number of commits you have in the PR - doesn't matter. -* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. - We reserve the right to close PRs that are not making progress. This is generally defined as no - changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. - Closing stale PRs helps us to keep on top of all the work currently in flight. - -## Maintainer PR Review Policy - -* See [CODEOWNERS.md](CODEOWNERS.md) for the current list of maintainers. -* A maintainer representing a different affiliation from the PR owner is required to review and - approve the PR. -* When the project matures, it is expected that a "domain expert" for the code the PR touches should - review the PR. This person does not require commit access, just domain knowledge. -* The above rules may be waived for PRs which only update docs or comments, or trivial changes to - tests and tools (where trivial is decided by the maintainer in question). -* If there is a question on who should review a PR please discuss in Slack. -* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. -* Please make sure that the PR title, commit message, and description are updated if the PR changes - significantly during review. -* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge - title with the original title, and the commit body with every individual commit from the PR. - The maintainer doing the merge should make sure the title follows the guidelines above and should - overwrite the body with the original commit message from the PR (cleaning it up if necessary) - while preserving the PR author's final DCO sign-off. - -## Decision making - -This is a new and complex project, and we need to make a lot of decisions very quickly. -To this end, we've settled on this process for making (possibly contentious) decisions: - -* For decisions that need a record, we create an issue. -* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. -* Maintainers can cast binding votes on that comment by reacting or replying in another comment. -* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. -* Voting will be resolved by simple majority. -* In the event of deadlocks, the question will be put to steering instead. - -## DCO: Sign your work - -The sign-off is a simple line at the end of the explanation for the -patch, which certifies that you wrote it or otherwise have the right to -pass it on as an open-source patch. The rules are pretty simple: if you -can certify the below (from -[developercertificate.org](https://developercertificate.org/)): - -``` -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -using your real name (sorry, no pseudonyms or anonymous contributions.) - -You can add the sign-off when creating the git commit via `git commit -s`. - -If you want this to be automatic you can set up some aliases: - -```bash -git config --add alias.amend "commit -s --amend" -git config --add alias.c "commit -s" -``` - -## Fixing DCO - -If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best -practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) -the commit history to a single commit, append the DCO sign-off as described above, and [force -push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in -your history: - -```bash -git rebase -i HEAD^^ -(interactive squash + DCO append) -git push origin -f -``` - -Note, that in general rewriting history in this way is a hindrance to the review process and this -should only be done to correct a DCO mistake. diff --git a/docs/v0.4.0/dev/DOCS.md b/docs/v0.4.0/dev/DOCS.md deleted file mode 100644 index fb49b9d55dd..00000000000 --- a/docs/v0.4.0/dev/DOCS.md +++ /dev/null @@ -1,63 +0,0 @@ -# Working on the Envoy Gateway Docs - -The documentation for the Envoy Gateway lives in the `docs/` directory. Any -individual document can be written using either [reStructuredText] or [Markdown], -you can choose the format that you're most comfortable with when working on the -documentation. - -## Documentation Structure - -We supported the versioned Docs now, the directory name under docs represents -the version of docs. The root of the latest site is in `docs/latest/index.rst`. -This is probably where to start if you're trying to understand how things fit together. - -Note that the new contents should be added to `docs/latest` and will be cut off at -the next release. The contents under `docs/v0.2.0` are auto-generated, -and usually do not need to make changes to them, unless if you find the current release pages have -some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` -and `docs/v0.2.0`. - -It's important to note that a given document _must_ have a reference in some -`.. toctree::` section for the document to be reachable. Not everything needs -to be in `docs/index.rst`'s `toctree` though. - -You can access the website which represents the current release in default, -and you can access the website which contains the latest version changes in -[Here][latest-website] or at the footer of the pages. - -## Documentation Workflow - -To work with the docs, just edit reStructuredText or Markdown files in `docs`, -then run - -```bash -make docs -``` - -This will create `docs/html` with the built HTML pages. You can view the docs -either simply by pointing a web browser at the `file://` path to your -`docs/html`, or by firing up a static webserver from that directory, e.g. - -``` shell -make docs-serve -``` - -If you want to generate a new release version of the docs, like `v0.3.0`, then run - -```bash -make docs-release TAG=v0.3.0 -``` - -This will update the VERSION file at the project root, which records current release version, -and it will be used in the pages version context and binary version output. Also, this will generate -new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` -in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. - -## Publishing Docs - -Whenever docs are pushed to `main`, CI will publish the built docs to GitHub -Pages. For more details, see `.github/workflows/docs.yaml`. - -[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -[Markdown]: https://daringfireball.net/projects/markdown/syntax -[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/docs/v0.4.0/dev/GOALS.md b/docs/v0.4.0/dev/GOALS.md deleted file mode 120000 index f2174592394..00000000000 --- a/docs/v0.4.0/dev/GOALS.md +++ /dev/null @@ -1 +0,0 @@ -../../../GOALS.md \ No newline at end of file diff --git a/docs/v0.4.0/dev/README.md b/docs/v0.4.0/dev/README.md deleted file mode 100644 index 2d4f13e9e80..00000000000 --- a/docs/v0.4.0/dev/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# Developer Guide - -Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. - -## Prerequisites - -### go - -* Version: 1.20 -* Installation Guide: https://go.dev/doc/install - -### make - -* Recommended Version: 4.0 or later -* Installation Guide: https://www.gnu.org/software/make - -### docker - -* Optional when you want to build a Docker image or run `make` inside Docker. -* Recommended Version: 20.10.16 -* Installation Guide: https://docs.docker.com/engine/install - -### python3 - -* Need a `python3` program -* Must have a functioning `venv` module; this is part of the standard - library, but some distributions (such as Debian and Ubuntu) replace - it with a stub and require you to install a `python3-venv` package - separately. - -## Quickstart - -* Run `make help` to see all the available targets to build, test and run Envoy Gateway. - -### Building - -* Run `make build` to build all the binaries. -* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. -* Run `make build BINS="egctl"` to build the egctl binary. - -__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. - -### Testing - -* Run `make test` to run the golang tests. - -### Running Linters - -* Run `make lint` to make sure your code passes all the linter checks. - -### Building and Pushing the Image - -* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. -* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. - -__Note:__ Replace `IMAGE` with your registry's image name. - -### Deploying Envoy Gateway for Test/Dev - -* Run `make create-cluster` to create a [Kind][] cluster. - -#### Option 1: Use the Latest [gateway-dev][] Image - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` - to use a different image tag. - -#### Option 2: Use a Custom Image - -* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. -* Run `make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. - -### Deploying Envoy Gateway in Kubernetes - -* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to - the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or - tag. -* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. - -__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. - -### Demo Setup - -* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource -(similar to steps outlined in the [Quickstart][] docs) and test the configuration. -* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. - -### Run Gateway API Conformance Tests - -The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the -Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run -`TAG=latest make run-conformance` to run the conformance tests. - -#### On a Linux Host - -* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] - image, and run Gateway API conformance tests. - -#### On a Mac Host - -Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following -workarounds to run conformance tests: - -* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run - `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image - to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to - uninstall Envoy Gateway. -* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. - -__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` -is unspecified, the short SHA of your current branch is used. - -### Debugging the Envoy Config - -An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port -(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. - -Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: - -```shell -export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward the admin interface port: - -```shell -kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 -``` - -Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. - -There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. - -### JWT Testing - -An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] -user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs -verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching -settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure -and is hosted in the repo. - -[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[make]: https://www.gnu.org/software/make/ -[Github Actions]: https://docs.github.com/en/actions -[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows -[Kind]: https://kind.sigs.k8s.io/ -[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ -[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ -[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ -[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags -[mac_connect]: https://github.com/chipmk/docker-mac-net-connect -[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface -[jwt]: https://tools.ietf.org/html/rfc7519 -[jwks]: https://tools.ietf.org/html/rfc7517 -[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html -[JWT Debugger]: https://jwt.io/ -[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/docs/v0.4.0/dev/releasing.md b/docs/v0.4.0/dev/releasing.md deleted file mode 100644 index f0004caf336..00000000000 --- a/docs/v0.4.0/dev/releasing.md +++ /dev/null @@ -1,195 +0,0 @@ -# Release Process - -This document guides maintainers through the process of creating an Envoy Gateway release. - -- [Release Candidate](#release-candidate) -- [Minor Release](#minor-release) -- [Announce the Release](#announce-the-release) - -## Release Candidate - -The following steps should be used for creating a release candidate. - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export RELEASE_CANDIDATE_NUMBER=1 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. -3. Sign, commit, and push your changes to your fork. -4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and - the [Build and Test][] has successfully completed. -5. Create a new release branch from `main`. The release branch should be named - `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. Push the branch to the Envoy Gateway repo. - - ```shell - git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] - for additional details on updating the image tag. -8. Sign, commit, and push your changes to your fork. -9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not - proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. -10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' - ``` - -11. Push the tag to the Envoy Gateway repository. - - ```shell - git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} - ``` - -12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -13. Confirm that the [release workflow][] completed successfully. -14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -15. Confirm that the [release][] was created. -16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test - the quickstart steps using the release candidate by manually updating the links. -17. [Generate][] the GitHub changelog. -18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. -19. If you find any bugs in this process, please create an issue. - -## Minor Release - -The following steps should be used for creating a minor release. - -### Prerequisites - -- Permissions to push to the Envoy Gateway repository. -- A release branch that has been cut from the corresponding release candidate. Refer to the - [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. - -Set environment variables for use in subsequent steps: - -```shell -export MAJOR_VERSION=0 -export MINOR_VERSION=3 -export GITHUB_REMOTE=origin -``` - -1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. - - 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release - notes should be an accumulation of the release candidate release notes and any changes since the release - candidate. - 2. Create a release announcement. Refer to [PR #635] as an example release announcement. - 3. Generate the versioned release docs: - - ``` shell - make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -3. Sign, commit, and push your changes to your fork. -4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged - and the [Build and Test][] has completed for your final PR. - -5. Checkout the release branch. - - ```shell - git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -6. If the tip of the release branch does not match the tip of `main`, perform the following: - - 1. Create a topic branch from the release branch. - 2. Cherry-pick the commits from `main` that differ from the release branch. - 3. Run tests locally, e.g. `make lint`. - 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. - 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. - 6. Do not proceed until the PR has merged and CI passes for the merged PR. - 7. If you are still on your topic branch, change to the release branch: - - ```shell - git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - - 8. Ensure your local release branch is up-to-date: - - ```shell - git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} - ``` - -7. Tag the head of your release branch with the release tag. For example: - - ```shell - git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' - ``` - - __Note:__ The tag version differs from the release branch by including the `.0` patch version. - -8. Push the tag to the Envoy Gateway repository. - - ```shell - git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. -10. Confirm that the [release workflow][] completed successfully. -11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. -12. Confirm that the [release][] was created. -13. Confirm that the steps in the [Quickstart Guide][] work as expected. -14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: - - ```console - # Release Announcement - - Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] - (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. - ``` - -If you find any bugs in this process, please create an issue. - -## Announce the Release - -It's important that the world knows about the release. Use the following steps to announce the release. - -1. Set the release information in the Envoy Gateway Slack channel. For example: - - ```shell - Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 - ``` - -2. Send a message to the Envoy Gateway Slack channel. For example: - - ```shell - On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway - v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. - Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs - to start using Envoy Gateway. - ... - ``` - - Link to the GitHub release and release announcement page that highlights the release. - -[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes -[Pull Request]: https://github.com/envoyproxy/gateway/pulls -[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md -[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml -[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml -[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml -[image]: https://hub.docker.com/r/envoyproxy/gateway/tags -[release]: https://github.com/envoyproxy/gateway/releases -[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes -[PR #635]: https://github.com/envoyproxy/gateway/pull/635 -[PR #958]: https://github.com/envoyproxy/gateway/pull/958 -[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/docs/v0.4.0/dev_docs.rst b/docs/v0.4.0/dev_docs.rst deleted file mode 100644 index e546e14c1ad..00000000000 --- a/docs/v0.4.0/dev_docs.rst +++ /dev/null @@ -1,15 +0,0 @@ -Developer Docs -============== - -Learn how to contribute to Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - dev/GOALS - dev/CODE_OF_CONDUCT - dev/CODEOWNERS - dev/CONTRIBUTING - dev/README - dev/DOCS - dev/releasing diff --git a/docs/v0.4.0/get_involved.rst b/docs/v0.4.0/get_involved.rst deleted file mode 100644 index f17febd5651..00000000000 --- a/docs/v0.4.0/get_involved.rst +++ /dev/null @@ -1,9 +0,0 @@ -Getting Involved -================ - -We welcome contributions from the community. Please carefully review the -`project goals `_ -and the -`code of conduct `_ -before diving in. - diff --git a/docs/v0.4.0/images/architecture.png b/docs/v0.4.0/images/architecture.png deleted file mode 100644 index 1d4131fbea7..00000000000 Binary files a/docs/v0.4.0/images/architecture.png and /dev/null differ diff --git a/docs/v0.4.0/images/extension-example.png b/docs/v0.4.0/images/extension-example.png deleted file mode 100644 index a77aec0406b..00000000000 Binary files a/docs/v0.4.0/images/extension-example.png and /dev/null differ diff --git a/docs/v0.4.0/index.rst b/docs/v0.4.0/index.rst deleted file mode 100644 index dbe107b61d8..00000000000 --- a/docs/v0.4.0/index.rst +++ /dev/null @@ -1,34 +0,0 @@ -`Envoy Gateway `_ -==================== - -Release: |version| - -.. image:: https://img.shields.io/badge/slack-join-orange.svg - :target: https://envoyproxy.slack.com/archives/C03E6NHLESV - :alt: Join the Envoy Slack - -Envoy Gateway is an open source project for managing `Envoy Proxy`_ as a standalone or Kubernetes-based application -gateway. `Gateway API`_ resources are used to dynamically provision and configure the managed Envoy Proxies. Whether -you are interested in using or contributing to Envoy Gateway, the following resources will help you get started: - -.. toctree:: - :maxdepth: 1 - - intro/compatibility - user_docs - design_docs - dev_docs - api_docs - releases - roadmap - about_docs - get_involved - presentations - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved`. - -.. _Envoy Proxy: https://www.envoyproxy.io/ -.. _Gateway API: https://gateway-api.sigs.k8s.io/ diff --git a/docs/v0.4.0/intro/compatibility.rst b/docs/v0.4.0/intro/compatibility.rst deleted file mode 100644 index 68722f8a762..00000000000 --- a/docs/v0.4.0/intro/compatibility.rst +++ /dev/null @@ -1,25 +0,0 @@ -Compatibility Matrix -==================== - -Envoy Gateway relies on the Envoy Proxy and the Gateway API, and runs -within a Kubernetes cluster. Not all versions of each of these products -can function together for Envoy Gateway. Supported version combinations -are listed below; **bold** type indicates the versions of the Envoy Proxy -and the Gateway API actually compiled into each Envoy Gateway release. - -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| Envoy Gateway version | Envoy Proxy version | Rate Limit version | Gateway API version | Kubernetes version | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| v0.4.0 | **v1.26-latest** | **542a6047** | **v0.6.2** | v1.25, v1.26, v1.27 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| v0.3.0 | **v1.25-latest** | **f28024e3** | **v0.6.1** | v1.24, v1.25, v1.26 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| v0.2.0 | **v1.23-latest** | | **v0.5.1** | v1.24 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ -| latest | **dev-latest** | **master** | **v0.6.2** | v1.25, v1.26, v1.27 | -+--------------------------+---------------------+---------------------+---------------------+----------------------------+ - -.. note:: - - This project is under active development. Many, many features are not - complete. We would love for you to :doc:`get involved<../get_involved>`. diff --git a/docs/v0.4.0/presentations.md b/docs/v0.4.0/presentations.md deleted file mode 100644 index bc681b8c268..00000000000 --- a/docs/v0.4.0/presentations.md +++ /dev/null @@ -1,12 +0,0 @@ -# Presentations - -This page contains a list of presentations about the Envoy Proxy project. - -| Conference | Title | Speaker | Recording | Slides | -|-----------------|------------------------------|------------------------------|--------------------------------------------------------|--------| -| KubeCon NA 2022 | Envoy Gateway Project Update | Daneyon Hansen & Alice Wasko | [YouTube](https://www.youtube.com/watch?v=3MUOZc8XNCc) | [Slides](https://static.sched.com/hosted_files/envoyconna22/2f/Envoy_Gateway_Project_Update_EnvoyCon_NA_2022.pptx) | -| KubeCon EU 2023 | Envoy Gateway Project Update | Alice Wasko | [YouTube](https://www.youtube.com/watch?v=4vnJxt9sVho) | [Slides](https://static.sched.com/hosted_files/kccnceu2023/58/Kubecon_EU_2023_Envoy_Gateway_Update.pptx) | - - - - diff --git a/docs/v0.4.0/releases.rst b/docs/v0.4.0/releases.rst deleted file mode 100644 index 42ce2b7d7b8..00000000000 --- a/docs/v0.4.0/releases.rst +++ /dev/null @@ -1,12 +0,0 @@ -Releases -======== - -Learn more about Envoy Gateway releases. - -.. toctree:: - :maxdepth: 1 - - releases/README - releases/v0.2 - releases/v0.3 - releases/v0.4 \ No newline at end of file diff --git a/docs/v0.4.0/releases/README.md b/docs/v0.4.0/releases/README.md deleted file mode 100644 index 2ca374ca69d..00000000000 --- a/docs/v0.4.0/releases/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Release Details - -This document provides details for Envoy Gateway releases. Envoy Gateway follows the Semantic Versioning [v2.0.0 spec][] -for release versioning. Since Envoy Gateway is a new project, minor releases are the only defined releases. Envoy -Gateway maintainers will establish additional release details, e.g. patch releases, at a future date. - -## Stable Releases - -Stable releases of Envoy Gateway include: - -* Minor Releases- A new release branch and corresponding tag are created from the `main` branch. A minor release - is supported for 6 months following the release date. As the project matures, Envoy Gateway maintainers will reassess - the support timeframe. - -Minor releases happen quarterly and follow the schedule below. - -## Release Management - -Minor releases are handled by a designated Envoy Gateway maintainer. This maintainer is considered the Release Manager -for the release. The details for creating a release are outlined in the [release guide][]. The Release Manager is -responsible for coordinating the overall release. This includes identifying issues to be fixed in the release, -communications with the Envoy Gateway community, and the mechanics of the release. - -| Quarter | Release Manager | -|:-------:|:--------------------------------------------------------------:| -| 2022 Q4 | Daneyon Hansen ([danehans](https://github.com/danehans)) | -| 2023 Q1 | Xunzhuo Liu ([Xunzhuo](https://github.com/Xunzhuo)) | -| 2023 Q2 | Alice Wasko ([AliceProxy](https://github.com/AliceProxy)) | - -## Release Schedule - -In order to align with the Envoy Proxy [release schedule][], Envoy Gateway releases are produced on a fixed schedule -(the 22nd day of each quarter), with an acceptable delay of up to 2 weeks, and a hard deadline of 3 weeks. - -| Version | Expected | Actual | Difference | End of Life | -|:-------:|:-----------:|:-----------:|:----------:|:-----------:| -| 0.2.0 | 2022/10/22 | 2022/10/20 | -2 day | 2023/4/20 | -| 0.3.0 | 2023/01/22 | 2023/02/09 | +17 day | 2023/08/09 | -| 0.3.0 | 2023/04/22 | 2023/04/24 | +2 day | 2023/10/24 | - -[v2.0.0 spec]: https://semver.org/spec/v2.0.0.html -[release guide]: ../dev/releasing.md -[release schedule]: https://github.com/envoyproxy/envoy/blob/main/RELEASES.md#major-release-schedule diff --git a/docs/v0.4.0/releases/v0.2.md b/docs/v0.4.0/releases/v0.2.md deleted file mode 100644 index a0dc0e885de..00000000000 --- a/docs/v0.4.0/releases/v0.2.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.2 -linktitle: v0.2 -subtitle: Major Update -description: Envoy Gateway v0.2 release announcement. -publishdate: 2022-10-20 -release: v0.2.0 -skip_list: true -aliases: -- /releases/v0.2 -- /releases/v0.2.0 ---- -# Envoy Gateway Release v0.2 - -We are pleased to announce the release of Envoy Gateway v0.2! - -This is the first functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Kubernetes Support - -Run Envoy Gateway in a Kubernetes cluster. Checkout the [quickstart guide][] to get started with Envoy Gateway in a few -simple steps. - -### Gateway API Support - -Envoy Gateway supports Gateway API resources for running and configuring a managed fleet of Envoy proxies. Envoy Gateway -passes Gateway API core [conformance tests][] and supports GatewayClass, Gateway, HTTPRoute, and TLSRoute resources. See -the [documentation][docs] for additional details on how to use Envoy Gateway for your edge proxy and API gateway needs. - -## Envoy Gateway at EnvoyCon NA - -Envoy Gateway will be at [EnvoyCon NA][] this October in Detroit. Don't miss [our talk][] to learn more about the -release and future direction of the project. - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.2.0.yaml -[matrix]: https://gateway.envoyproxy.io/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.2.0 -[conformance tests]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=conformance -[quickstart guide]: https://gateway.envoyproxy.io/user/quickstart.html -[EnvoyCon NA]: https://events.linuxfoundation.org/envoycon-north-america/program/schedule/ -[our talk]: https://sched.co/1AO5S diff --git a/docs/v0.4.0/releases/v0.3.md b/docs/v0.4.0/releases/v0.3.md deleted file mode 100644 index 96d6d6d49eb..00000000000 --- a/docs/v0.4.0/releases/v0.3.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.3 -linktitle: v0.3 -subtitle: Major Update -description: Envoy Gateway v0.3 release announcement. -publishdate: 2023-02-09 -release: v0.3.0 -skip_list: true -aliases: -- /releases/v0.3 -- /releases/v0.3.0 ---- -# Envoy Gateway Release v0.3 - -We are pleased to announce the release of Envoy Gateway v0.3! - -This is the second functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Add Support for extended Gateway API fields - -+ Added Support for HTTPRoute URLRewrite Filter -+ Added Support for HTTPRoute RequestMirror Filter -+ Added Support for HTTPRoute ResponseHeaderModifier Filter - -### Add Support for experimental Gateway APIs - -+ Added Support for the TCPRoute API -+ Added Support for the UDPRoute API -+ Added Support for the GRPCRoute API - -### Add Support for Rate Limiting - -+ Added Support for Global Rate Limiting - -### Add Support for Authentication - -+ Added Support for Request Authentication - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.3.0.yaml -[matrix]: https://gateway.envoyproxy.io/v0.3.0/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/v0.3.0/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.3.0 diff --git a/docs/v0.4.0/releases/v0.4.md b/docs/v0.4.0/releases/v0.4.md deleted file mode 100644 index 81a9bc3a0ed..00000000000 --- a/docs/v0.4.0/releases/v0.4.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Announcing Envoy Gateway v0.4 -linktitle: v0.4 -subtitle: Major Update -description: Envoy Gateway v0.4 release announcement. -publishdate: 2023-04-24 -release: v0.4.0 -skip_list: true -aliases: -- /releases/v0.4 -- /releases/v0.4.0 ---- -# Envoy Gateway Release v0.4 - -We are pleased to announce the release of Envoy Gateway v0.4! - -This is the third functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for -helping publish the release. - -| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | -|-------------------|--------------|--------------------------------|--------------| - -## What's New - -The release adds a ton of features and functionality. Here are some highlights: - -### Upgrade Gateway API Dependency - -+ Upgraded to Gateway API v0.6.2 - -### Add Helm Support - -+ Installation of Envoy Gateway can now be done through helm - -### Add egctl CLI Tool - -+ Added egctl Support for Dry Runs of Gateway API Config -+ Added egctl Support for Dumping Envoy Proxy xDS Resources - -### Add Support for extending Envoy Gateway - -+ Added Initial Framework for Building an Extension on top of Envoy Gateway - -### Ratelimiting - -+ Added Support for Ratelimiting Based On IP Subnet - -### API Updates - -+ Added Support for Custom Envoy Proxy Bootstrap Config -+ Added Support for Configuring the Envoy Proxy Image and Service -+ Added Support for Configuring Annotations, Resources, and Securitycontext Settings on Ratelimit Infra and Envoy Proxy -+ Added Support for Using Multiple Certificates on a Single Fully Qualified Domain Name -+ Envoy Proxy Pod and Container SecurityContext is now Configurable -+ Added Support for Service Method Match in GRPCRoute -+ Added EDS Support - -[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.4.0.yaml -[matrix]: https://gateway.envoyproxy.io/v0.4.0/intro/compatibility.html -[docs]: https://gateway.envoyproxy.io/v0.4.0/index.html -[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.4.0 diff --git a/docs/v0.4.0/roadmap.rst b/docs/v0.4.0/roadmap.rst deleted file mode 100644 index 711b6245503..00000000000 --- a/docs/v0.4.0/roadmap.rst +++ /dev/null @@ -1,9 +0,0 @@ -Roadmap -======= - -Learn about the future direction of Envoy Gateway. - -.. toctree:: - :maxdepth: 2 - - design/roadmap diff --git a/docs/v0.4.0/user/authn.md b/docs/v0.4.0/user/authn.md deleted file mode 100644 index 1a7dba62197..00000000000 --- a/docs/v0.4.0/user/authn.md +++ /dev/null @@ -1,94 +0,0 @@ -# Request Authentication - -This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks -if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only -supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. - -## Installation - -Follow the steps from the [Quickstart](quickstart.md) guide to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Configuration - -Allow requests with a valid JWT by creating an [AuthenticationFilter][] and referencing it from the example HTTPRoute. - -```shell -kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.4.0/examples/kubernetes/authn/jwt.yaml -``` - -The HTTPRoute is now updated to authenticate requests for `/foo` and allow unauthenticated requests to `/bar`. The -`/foo` route rule references an AuthenticationFilter that provides the JWT authentication configuration. - -Verify the HTTPRoute configuration and status: - -```shell -kubectl get httproute/backend -o yaml -``` - -The AuthenticationFilter is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] -provider for authenticating the JWT. - -Verify the AuthenticationFilter configuration: - -```shell -kubectl get authenticationfilter/jwt-example -o yaml -``` - -## Testing - -Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](quickstart.md) guide is set. If not, follow the -Quickstart instructions to set the variable. - -```shell -echo $GATEWAY_HOST -``` - -Verify that requests to `/foo` are denied without a JWT: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo -``` - -A `401` HTTP response code should be returned. - -Get the JWT used for testing request authentication: - -```shell -TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - -``` - -__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's -header. - -Verify that a request to `/foo` with a valid JWT is allowed: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo -``` - -A `200` HTTP response code should be returned. - -Verify that requests to `/bar` are allowed __without__ a JWT: - -```shell -curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar -``` - -## Clean-Up - -Follow the steps from the [Quickstart](quickstart.md) guide to uninstall Envoy Gateway and the example manifest. - -Delete the AuthenticationFilter: - -```shell -kubectl delete authenticationfilter/jwt-example -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. - -[jwt]: https://tools.ietf.org/html/rfc7519 -[AuthenticationFilter]: https://gateway.envoyproxy.io/v0.4.0/api/extension_types.html#authenticationfilter -[jwks]: https://tools.ietf.org/html/rfc7517 diff --git a/docs/v0.4.0/user/customize-envoyproxy.md b/docs/v0.4.0/user/customize-envoyproxy.md deleted file mode 100644 index 04b7062f450..00000000000 --- a/docs/v0.4.0/user/customize-envoyproxy.md +++ /dev/null @@ -1,251 +0,0 @@ -# Customize EnvoyProxy - -Envoy Gateway provides a [EnvoyProxy][] CRD that can be linked to the ParametersRef -in GatewayClass y cluster admins to customize the managed EnvoyProxy Deployment and -Service. To learn more about GatewayClass and ParametersRef, please refer to [Gateway API documentation][]. - -## Installation - -Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Add GatewayClass ParametersRef - -First, you need to add ParametersRef in GatewayClass, and refer to EnvoyProxy Config: - -```shell -cat < GET /get HTTP/1.1 -> Host: www.marketing.example.com -> User-Agent: curl/7.86.0 -> Accept: */* -> -Handling connection for 8888 -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Thu, 20 Apr 2023 19:19:42 GMT -< content-length: 521 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "www.marketing.example.com", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.86.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Internal": [ - "true" - ], - "X-Forwarded-For": [ - "10.1.0.157" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "c637977c-458a-48ae-92b3-f8c429849322" - ] - }, - "namespace": "marketing", - "ingress": "", - "service": "", - "pod": "backend-74888f465f-bcs8f" -* Connection #0 to host localhost left intact -``` - -* Lets deploy Envoy Gateway in the `product` namespace - -``` -helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.4.0 -n product --create-namespace -``` - -Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. - -```shell -cat < GET /get HTTP/1.1 -> Host: www.product.example.com -> User-Agent: curl/7.86.0 -> Accept: */* -> -Handling connection for 8889 -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Thu, 20 Apr 2023 19:20:17 GMT -< content-length: 517 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "www.product.example.com", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.86.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Internal": [ - "true" - ], - "X-Forwarded-For": [ - "10.1.0.156" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "39196453-2250-4331-b756-54003b2853c2" - ] - }, - "namespace": "product", - "ingress": "", - "service": "", - "pod": "backend-74888f465f-64fjs" -* Connection #0 to host localhost left intact -``` - -With the below command you can ensure that you are no able to access the marketing team's backend exposed using the `www.marketing.example.com` hostname -and the product team's data plane. - -```shell -curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get -``` - -``` -* Trying 127.0.0.1:8889... -* Connected to localhost (127.0.0.1) port 8889 (#0) -> GET /get HTTP/1.1 -> Host: www.marketing.example.com -> User-Agent: curl/7.86.0 -> Accept: */* -> -Handling connection for 8889 -* Mark bundle as not supporting multiuse -< HTTP/1.1 404 Not Found -< date: Thu, 20 Apr 2023 19:22:13 GMT -< server: envoy -< content-length: 0 -< -* Connection #0 to host localhost left intact -``` diff --git a/docs/v0.4.0/user/egctl.md b/docs/v0.4.0/user/egctl.md deleted file mode 100644 index 08621e40f47..00000000000 --- a/docs/v0.4.0/user/egctl.md +++ /dev/null @@ -1,804 +0,0 @@ -# egctl - -`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. - -## Installing egctl - -This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. - -### From The Envoy Gateway Project - -The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. - -### From the Binary Releases - -Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. - -1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) -2. Unpack it (tar -zxvf egctl_v0.4.0_linux_amd64.tar.gz) -3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) - -From there, you should be able to run: `egctl help`. - -### From Script - -`egctl` now has an installer script that will automatically grab the v0.4.0 release version of egctl and install it locally. - -You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. - -```shell -curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh - -chmod +x get-egctl.sh - -# get help info of the -bash get-egctl.sh --help - -# install the v0.4.0 development version of egctl -bash VERSION=v0.4.0 get-egctl.sh -``` - -Yes, you can just use the below command if you want to live on the edge. - -```shell -curl https://gateway.envoyproxy.io/get-egctl.sh | VERSION=v0.4.0 bash -``` - -## egctl experimental translate - -This subcommand allows users to translate from an input configuration type to an output configuration type. - -In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS -resources. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something", - "foo" - ], -... -``` - -## Setting Request Headers - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - "headers": { - "Accept": [ - "*/*" - ], - "Set-Header": [ - "foo" - ], -... -``` - -## Removing Request Headers - -Headers can be removed from a request by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it -will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if -the request already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "Add-Header": [ - "something" - ], -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: X-Foo: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< x-foo: value1 -< add-header: foo -< -... - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "X-Foo: value1" - ] -... -``` - -## Setting Response Headers - -Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it -will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header -if the response already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: set-header: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< set-header: foo -< - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "set-header": value1" - ] -... -``` - -## Removing Response Headers - -Headers can be removed from a response by simply supplying a list of header names. - -Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it -will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header -if the response already contains it, setting a header will cause the value to be replaced by the value configured in the -filter. - -```shell -cat < GET /get HTTP/1.1 -> Host: headers.example -> User-Agent: curl/7.81.0 -> Accept: */* -> X-Echo-Set-Header: remove-header: value1 -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< - - "headers": { - "Accept": [ - "*/*" - ], - "X-Echo-Set-Header": [ - "remove-header": value1" - ] -... -``` - -## Combining Filters - -Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-79665566f5-s589f" -... -``` - -## Multiple backendRefs - -If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is -configured. - -First, create a second instance of the example app from the quickstart: - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> add-header: something -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< content-length: 474 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -... - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-75bcd4c969-lsxpz" -... -``` - -## Weighted backendRefs - -If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` -field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is -assumed to be `1`. - -The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of -requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the -HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. - -The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the -backend-2 service. - -```shell -cat < GET /get HTTP/1.1 -> Host: backends.example -> User-Agent: curl/7.81.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 500 Internal Server Error -< server: envoy -< content-length: 0 -< -``` - -[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ -[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/docs/v0.4.0/user/http-urlrewrite.md b/docs/v0.4.0/user/http-urlrewrite.md deleted file mode 100644 index 88e29c3269c..00000000000 --- a/docs/v0.4.0/user/http-urlrewrite.md +++ /dev/null @@ -1,295 +0,0 @@ -# HTTP URL Rewrite - -[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be -used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. - -## Prerequisites - -Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -## Rewrite URL Prefix Path - -You can configure to rewrite the prefix in the url like below. In this example, any curls to -`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. - -```shell -cat < GET /get/origin/path HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> - -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:03:28 GMT -< content-length: 503 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/replace/origin/path", - "host": "path.rewrite.example", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Original-Path": [ - "/get/origin/path" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "fd84b842-9937-4fb5-83c7-61470d854b90" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. - -## Rewrite URL Full Path - -You can configure to rewrite the fullpath in the url like below. In this example, any request sent to -`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to -`http://${GATEWAY_HOST}/force/replace/fullpath`. - -```shell -cat < GET /get/origin/path/extra HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:09:31 GMT -< content-length: 512 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/force/replace/fullpath", - "host": "path.rewrite.example", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Envoy-Original-Path": [ - "/get/origin/path/extra" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "8ab774d6-9ffa-4faa-abbb-f45b0db00895" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is -`/force/replace/fullpath`. - -## Rewrite Host Name - -You can configure to rewrite the hostname like below. In this example, any requests sent to -`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. - -```shell -cat < GET /get HTTP/1.1 -> Host: path.rewrite.example -> User-Agent: curl/7.85.0 -> Accept: */* -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 200 OK -< content-type: application/json -< x-content-type-options: nosniff -< date: Wed, 21 Dec 2022 11:15:15 GMT -< content-length: 481 -< x-envoy-upstream-service-time: 0 -< server: envoy -< -{ - "path": "/get", - "host": "envoygateway.io", - "method": "GET", - "proto": "HTTP/1.1", - "headers": { - "Accept": [ - "*/*" - ], - "User-Agent": [ - "curl/7.85.0" - ], - "X-Envoy-Expected-Rq-Timeout-Ms": [ - "15000" - ], - "X-Forwarded-Host": [ - "path.rewrite.example" - ], - "X-Forwarded-Proto": [ - "http" - ], - "X-Request-Id": [ - "39aa447c-97b9-45a3-a675-9fb266ab1af0" - ] - }, - "namespace": "default", - "ingress": "", - "service": "", - "pod": "backend-6fdd4b9bd8-8vlc5" -... -``` - -You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. - -[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPURLRewriteFilter diff --git a/docs/v0.4.0/user/quickstart.md b/docs/v0.4.0/user/quickstart.md deleted file mode 100644 index c7d1ea099e4..00000000000 --- a/docs/v0.4.0/user/quickstart.md +++ /dev/null @@ -1,97 +0,0 @@ -# Quickstart - -This guide will help you get started with Envoy Gateway in a few simple steps. - -## Prerequisites - -A Kubernetes cluster. - -__Note:__ Refer to the [Compatibility Matrix](../intro/compatibility.rst) for supported Kubernetes versions. - -## Installation - -Install the Gateway API CRDs and Envoy Gateway: - -```shell -helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.4.0 -n envoy-gateway-system --create-namespace -``` - -Wait for Envoy Gateway to become available: - -```shell -kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -``` - -Install the GatewayClass, Gateway, HTTPRoute and example app: - -```shell -kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.4.0/quickstart.yaml -n default -``` - -**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for -traffic on port 80 on its globally-routable IP address, to make it easy to use -browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is -using a privileged port (<1024), it will map this internally to an -unprivileged port, so that Envoy Gateway doesn't need additional privileges. -It's important to be aware of this mapping, since you may need to take it into -consideration when debugging. - -[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/v0.4.0/quickstart.yaml - -## Testing the Configuration - -Get the name of the Envoy service created the by the example Gateway: - -```shell -export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') -``` - -Port forward to the Envoy service: - -```shell -kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & -``` - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://localhost:8888/get -``` - -### External LoadBalancer Support - -You can also test the same functionality by sending traffic to the External IP. To get the external IP of the -Envoy service, run: - -```shell -export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -``` - -In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace -`ip` in the above command with `hostname`. - -Curl the example app through Envoy proxy: - -```shell -curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get -``` - -## Clean-Up - -Use the steps in this section to uninstall everything from the quickstart guide. - -Delete the GatewayClass, Gateway, HTTPRoute and Example App: - -```shell -kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.4.0/quickstart.yaml --ignore-not-found=true -``` - -Delete the Gateway API CRDs and Envoy Gateway: - -```shell -helm uninstall eg -n envoy-gateway-system -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. diff --git a/docs/v0.4.0/user/rate-limit.md b/docs/v0.4.0/user/rate-limit.md deleted file mode 100644 index aaaca706473..00000000000 --- a/docs/v0.4.0/user/rate-limit.md +++ /dev/null @@ -1,631 +0,0 @@ -# Rate limit - -Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. - -Here are some reasons why you may want to implements Rate limits - -* To prevent malicious activity such as DDoS attacks. -* To prevent applications and its resources (such as a database) from getting overloaded. -* To create API limits based on user entitlements. - -Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied -i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit -if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. - -Envoy Gateway introduces a new CRD called [RateLimitFilter][] that allows the user to describe their rate limit intent. This instantiated resource -can be linked to a [HTTPRoute][] resource using an [ExtensionRef][] filter. - -## Prerequisites - -### Install Envoy Gateway - -* Follow the steps from the [Quickstart Guide](quickstart.md) to install Envoy Gateway and the example manifest. -Before proceeding, you should be able to query the example backend using HTTP. - -### Install Redis - -* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. -Lets install a Redis deployment in the `redis-system` namespce. - -```shell -cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com -; (1 server found) -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 -;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 -;; WARNING: recursion requested but not available - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -; COOKIE: 24fb86eba96ebf62 (echoed) -;; QUESTION SECTION: -;foo.bar.com. IN A - -;; ADDITIONAL SECTION: -foo.bar.com. 0 IN A 10.244.0.19 -_udp.foo.bar.com. 0 IN SRV 0 0 42376 . - -;; Query time: 1 msec -;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) -;; WHEN: Fri Jan 13 10:20:34 UTC 2023 -;; MSG SIZE rcvd: 114 -``` - -## Clean-Up - -Follow the steps from the [Quickstart Guide](quickstart.md) to uninstall Envoy Gateway. - -Delete the CoreDNS example manifest and the UDPRoute: - -```shell -kubectl delete deploy/coredns -kubectl delete service/coredns -kubectl delete cm/coredns -kubectl delete udproute/coredns -``` - -## Next Steps - -Checkout the [Developer Guide](../dev/README.md) to get involved in the project. - -[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute -[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/v0.4.0/configuration/listeners/udp_filters/udp_proxy diff --git a/docs/v0.4.0/user_docs.rst b/docs/v0.4.0/user_docs.rst deleted file mode 100644 index c7d56731696..00000000000 --- a/docs/v0.4.0/user_docs.rst +++ /dev/null @@ -1,26 +0,0 @@ -User Guides -=========== - -Learn how to deploy, use, and operate Envoy Gateway. - -.. toctree:: - :maxdepth: 1 - - user/quickstart - user/http-routing - user/http-redirect - user/http-urlrewrite - user/http-traffic-splitting - user/http-request-headers - user/http-response-headers - user/secure-gateways - user/tls-passthrough - user/tcp-routing - user/udp-routing - user/grpc-routing - user/authn - user/rate-limit - user/egctl - user/customize-envoyproxy - user/deployment-mode - user/gatewayapi-support diff --git a/examples/grafana/dashboards/envoy-clusters.json b/examples/grafana/dashboards/envoy-clusters.json new file mode 100644 index 00000000000..ade4b919a22 --- /dev/null +++ b/examples/grafana/dashboards/envoy-clusters.json @@ -0,0 +1,1645 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Envoy proxy monitoring Dashboard with cluster and service level templates. ", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 11021, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 3, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#d44a3a", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 1 + }, + { + "color": "#299c46", + "value": 2 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 9, + "links": [], + "maxDataPoints": 100, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(envoy_server_live{})", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Live servers", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 5, + "y": 0 + }, + "id": 12, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "avg(envoy_server_uptime)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Avg uptime per node", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 9, + "y": 0 + }, + "id": 11, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "SUM(envoy_server_memory_allocated{})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Allocated Memory", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 14, + "y": 0 + }, + "id": 13, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "SUM(envoy_server_memory_heap_size)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Heap Size", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 18, + "y": 0 + }, + "id": 19, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "(sum(envoy_cluster_membership_healthy{envoy_cluster_name=~\"$cluster\"}) - sum(envoy_cluster_membership_total{envoy_cluster_name=~\"$cluster\"}))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Unhealthy Clusters", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "text": "NOT WELL" + }, + "1": { + "text": "OK" + } + }, + "type": "value" + }, + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#d44a3a", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 0 + }, + { + "color": "#299c46", + "value": 1 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 21, + "y": 0 + }, + "id": 20, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "(sum(envoy_cluster_membership_total{envoy_cluster_name=~\"$cluster\"})-sum(envoy_cluster_membership_healthy{envoy_cluster_name=~\"$cluster\"})) == bool 0", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Cluster State", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 2, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(envoy_cluster_upstream_cx_active{envoy_cluster_name=~\"$cluster\"}) by (envoy_cluster_name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{envoy_cluster_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Total active connections", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "httproute/default/backend/rule/0" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 4, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum(irate(envoy_cluster_upstream_rq_total{envoy_cluster_name=~\"$cluster\"}[5m])) by (envoy_cluster_name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{envoy_cluster_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Total requests", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 15, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(irate(envoy_cluster_upstream_cx_rx_bytes_total{envoy_cluster_name=~\"$cluster\"}[5m])) by (envoy_cluster_name)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{envoy_cluster_name}} - in", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(irate(envoy_cluster_upstream_cx_tx_bytes_total{envoy_cluster_name=~\"$cluster\"}[5m])) by (envoy_cluster_name)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{envoy_cluster_name}} - out", + "range": true, + "refId": "B" + } + ], + "title": "Upstream Network Traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 17, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum(irate(envoy_http_downstream_cx_rx_bytes_total{envoy_http_conn_manager_prefix=~\"http\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{service}} - in", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum(irate(envoy_http_downstream_cx_tx_bytes_total{envoy_http_conn_manager_prefix=~\"http\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{service}} - out", + "range": true, + "refId": "B" + } + ], + "title": "Downstream Network Traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(envoy_cluster_upstream_rq_time_bucket{envoy_cluster_name=~\"$cluster\"}[5m])) by (le, envoy_cluster_name))", + "instant": false, + "legendFormat": "{{envoy_cluster_name}} 99%", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.9, sum(rate(envoy_cluster_upstream_rq_time_bucket{envoy_cluster_name=~\"$cluster\"}[5m])) by (le, envoy_cluster_name))", + "hide": false, + "instant": false, + "legendFormat": "{{envoy_cluster_name}} 90%", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(envoy_cluster_upstream_rq_time_bucket{envoy_cluster_name=~\"$cluster\"}[5m])) by (le, envoy_cluster_name))", + "hide": false, + "instant": false, + "legendFormat": "{{envoy_cluster_name}} 50%", + "range": true, + "refId": "C" + } + ], + "title": "Upstream Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 19 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_response_code_class=~\"2\", envoy_cluster_name=~\"$cluster\"}[5m])) by (envoy_cluster_name)", + "instant": false, + "legendFormat": "{{envoy_cluster_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 2xx Responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 28, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_cluster_name=~\"$cluster\",envoy_response_code_class=~\"4\"}[1m])) by (envoy_cluster_name)", + "instant": false, + "legendFormat": "{{envoy_cluster_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 4xx Responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 7, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(envoy_cluster_membership_healthy{envoy_cluster_name=~\"$cluster\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "healthy", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(envoy_cluster_membership_total{envoy_cluster_name=~\"$cluster\",service=~\"$service\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "B" + } + ], + "title": "Downstream members", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 27 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_cluster_name=~\"$cluster\",envoy_response_code_class=~\"5\"}[5m])) by (envoy_cluster_name)", + "instant": false, + "legendFormat": "{{envoy_cluster_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 5xx Responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 27 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_cluster_name=~\"$cluster\",envoy_response_code_class=~\"3\"}[5m])) by (envoy_cluster_name)", + "instant": false, + "legendFormat": "{{envoy_cluster_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 3xx Responses", + "type": "timeseries" + } + ], + "refresh": "30s", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "httproute/default/backend/rule/0", + "value": "httproute/default/backend/rule/0" + }, + "datasource": { + "uid": "$datasource" + }, + "definition": "label_values(envoy_cluster_name)", + "hide": 0, + "includeAll": true, + "label": "Cluster", + "multi": false, + "name": "cluster", + "options": [], + "query": { + "query": "label_values(envoy_cluster_name)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Envoy Clusters-11021", + "uid": "8WkEOMnANKE6PW5hhpVv", + "version": 1, + "weekStart": "" +} diff --git a/examples/grafana/dashboards/envoy-global.json b/examples/grafana/dashboards/envoy-global.json new file mode 100644 index 00000000000..76862defe19 --- /dev/null +++ b/examples/grafana/dashboards/envoy-global.json @@ -0,0 +1,2968 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Envoy proxy monitoring Dashboard with service level templates.", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 11022, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 3, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#d44a3a", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 1 + }, + { + "color": "#299c46", + "value": 2 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 37, + "links": [], + "maxDataPoints": 100, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum(envoy_server_live)", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Live servers", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 5, + "y": 0 + }, + "id": 39, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "avg by(pod) (envoy_server_uptime{namespace=~\"$Namespace\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Avg uptime per node", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 10, + "y": 0 + }, + "id": 43, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by(pod) (envoy_server_memory_heap_size{namespace=~\"$Namespace\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Heap Size", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 15, + "y": 0 + }, + "id": 41, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum by(pod) (envoy_server_memory_allocated{namespace=~\"$Namespace\"})", + "hide": false, + "instant": false, + "range": true, + "refId": "B" + } + ], + "title": "Allocated Memory", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 7 + }, + "id": 24, + "panels": [], + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "DownStream", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 8 + }, + "id": 3, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_http_downstream_rq_total[5m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Envoy HTTP Downstream Rq total", + "range": true, + "refId": "A" + } + ], + "title": "Downstream RPS", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 8 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_http_downstream_cx_total{namespace=~\"$Namespace\"}[5m]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Downstream CPS", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 8 + }, + "id": 16, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.9, sum by(le) (rate(envoy_http_downstream_rq_time_bucket[5m])))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{service}} 90%", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le) (rate(envoy_http_downstream_rq_time_bucket[5m])))", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "{{service}} 50% ", + "range": true, + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "histogram_quantile(0.99, sum by(le) (rate(envoy_http_downstream_rq_time_bucket[5m])))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{service}} 99%", + "range": true, + "refId": "C" + } + ], + "title": "Downstream Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 8 + }, + "id": 4, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by(namespace) (envoy_listener_downstream_cx_active{namespace=~\"$Namespace\"})", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{namespace}}", + "refId": "A" + } + ], + "title": "Downstream Total Connections", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 16 + }, + "id": 32, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_tcp_downstream_cx_total{namespace=~\"$Namespace\"}[5m]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "TCP Downstream CPS", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 16 + }, + "id": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_tcp_downstream_cx_rx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "TCP Downstream Bytes Rx/second", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 16 + }, + "id": 33, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_tcp_downstream_cx_tx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "TCP Downstream Bytes Tx/Second", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 22, + "panels": [], + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "UpStream", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "Displays the number of Requests per Second being performed against each Upstream.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 25 + }, + "id": 2, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_cluster_upstream_rq_total{namespace=~\"$Namespace\"}[5m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Upstream RPS", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 25 + }, + "id": 14, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_cx_total{namespace=~\"$Namespace\",}[5m])) by (namespace)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Upstream CPS", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 25 + }, + "id": 10, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(envoy_cluster_upstream_rq_time_bucket{namespace=~\"$Namespace\"}[5m])) by (le, namespace))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{namespace}} 99%", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.9, sum(rate(envoy_cluster_upstream_rq_time_bucket{namespace=~\"$Namespace\"}[5m])) by (le, namespace))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{namespace}} 90%", + "range": true, + "refId": "C" + }, + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(envoy_cluster_upstream_rq_time_bucket{namespace=~\"$Namespace\"}[5m])) by (le, namespace))", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "{{namespace}} 50% ", + "range": true, + "refId": "B" + } + ], + "title": "Upstream Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 15, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(envoy_cluster_upstream_cx_active{namespace=~\"$Namespace\"}) by (namespace)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream Total Connections", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 33 + }, + "id": 34, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_rx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream Bytes Rx/Second", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 33 + }, + "id": 35, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_rx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Upstream Bytes Tx/Second", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 28, + "panels": [], + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Upstream Response Codes", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 42 + }, + "id": 5, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_response_code_class=~\"2\"}[5m]))", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "Value", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 2xx Responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 42 + }, + "id": 11, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_response_code_class=~\"3\"}[5m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "value", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 3xx Responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 42 + }, + "id": 12, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_response_code_class=\"4\"}[5m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Value", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 4xx Responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 42 + }, + "id": 13, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum(rate(envoy_cluster_upstream_rq_xx{envoy_response_code_class=\"5\"}[5m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Value", + "range": true, + "refId": "A" + } + ], + "title": "Upstream 5xx Responses", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 50 + }, + "id": 26, + "panels": [], + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Total", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 51 + }, + "id": 17, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "avg(envoy_cluster_membership_healthy{namespace=~\"$Namespace\"}) by (namespace) / avg(envoy_cluster_membership_total{namespace=~\"$Namespace\"}) by (namespace)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Endpoint Percentage Health", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 51 + }, + "id": 19, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (envoy_cluster_membership_total{namespace=~\"$Namespace\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Total Endpoints", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 51 + }, + "id": 18, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "builder", + "expr": "sum by(namespace) (envoy_cluster_membership_healthy{namespace=~\"$Namespace\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Healthy Endpoints", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 51 + }, + "id": 20, + "links": [], + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "editorMode": "code", + "expr": "sum by(namespace) (envoy_cluster_membership_total{namespace=~\"$Namespace\"}) - sum by(namespace) (envoy_cluster_membership_healthy{namespace=~\"$Namespace\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Unhealthy Endpoints", + "type": "timeseries" + } + ], + "refresh": "10s", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "uid": "$datasource" + }, + "definition": "", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Namespace", + "options": [], + "query": "label_values(envoy_cluster_upstream_rq_time_bucket,namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Envoy Global-11022", + "uid": "heHhNSFf6Na8vIZWRs8H", + "version": 1, + "weekStart": "" +} diff --git a/examples/grafana/dashboards/envoy-pod-resource.json b/examples/grafana/dashboards/envoy-pod-resource.json new file mode 100644 index 00000000000..6a6520591c5 --- /dev/null +++ b/examples/grafana/dashboards/envoy-pod-resource.json @@ -0,0 +1,229 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 6, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum by(pod) (container_memory_working_set_bytes{container=~\"envoy\"}/1000000)", + "instant": false, + "range": true, + "refId": "A" + } + ], + "title": "Memory Working Set Envoy Pods(mb)", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "sum by(pod) (rate(container_cpu_usage_seconds_total{container=\"envoy\"}[5m]))", + "instant": false, + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage Envoy Pods", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Envoy Pod Memory and CPU Usage", + "uid": "f2279235-80b7-4c85-84f4-f25a3bf3eac0", + "version": 2, + "weekStart": "" +} diff --git a/examples/grafana/helm-values.yaml b/examples/grafana/helm-values.yaml new file mode 100644 index 00000000000..49000b76798 --- /dev/null +++ b/examples/grafana/helm-values.yaml @@ -0,0 +1,12 @@ +datasources: + datasources.yaml: + apiVersion: 1 + datasources: + - name: Prometheus + type: prometheus + url: http://prometheus-server + +adminPassword: admin + +service: + type: LoadBalancer diff --git a/examples/kubernetes/accesslog/disable-accesslog.yaml b/examples/kubernetes/accesslog/disable-accesslog.yaml index ebb1f7bd7af..97d4b36332d 100644 --- a/examples/kubernetes/accesslog/disable-accesslog.yaml +++ b/examples/kubernetes/accesslog/disable-accesslog.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: disable-accesslog namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: disable-accesslog @@ -18,4 +18,4 @@ metadata: spec: telemetry: accessLog: - disabled: true + disable: true diff --git a/examples/kubernetes/accesslog/json-accesslog.yaml b/examples/kubernetes/accesslog/json-accesslog.yaml index 3e3a2d72d14..bd50fb75c68 100644 --- a/examples/kubernetes/accesslog/json-accesslog.yaml +++ b/examples/kubernetes/accesslog/json-accesslog.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: json-access-logging namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: json-access-logging diff --git a/examples/kubernetes/accesslog/multi-sinks.yaml b/examples/kubernetes/accesslog/multi-sinks.yaml index d1da255731e..ae2cf3b55d5 100644 --- a/examples/kubernetes/accesslog/multi-sinks.yaml +++ b/examples/kubernetes/accesslog/multi-sinks.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: json-access-logging namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: multi-sinks diff --git a/examples/kubernetes/accesslog/otel-accesslog.yaml b/examples/kubernetes/accesslog/otel-accesslog.yaml index d9e1ce43e36..d4a8f4948de 100644 --- a/examples/kubernetes/accesslog/otel-accesslog.yaml +++ b/examples/kubernetes/accesslog/otel-accesslog.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: otel-access-logging namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: otel-access-logging diff --git a/examples/kubernetes/accesslog/text-accesslog.yaml b/examples/kubernetes/accesslog/text-accesslog.yaml index 5642fc370e8..d2eb0dec76e 100644 --- a/examples/kubernetes/accesslog/text-accesslog.yaml +++ b/examples/kubernetes/accesslog/text-accesslog.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: text-access-logging namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: text-access-logging diff --git a/examples/kubernetes/application.yaml b/examples/kubernetes/application.yaml new file mode 100644 index 00000000000..27df9d63dd5 --- /dev/null +++ b/examples/kubernetes/application.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: backend +--- +apiVersion: v1 +kind: Service +metadata: + name: backend + labels: + app: backend + service: backend +spec: + ports: + - name: http + port: 3000 + targetPort: 3000 + selector: + app: backend +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend +spec: + replicas: 1 + selector: + matchLabels: + app: backend + version: v1 + template: + metadata: + labels: + app: backend + version: v1 + spec: + serviceAccountName: backend + containers: + - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e + imagePullPolicy: IfNotPresent + name: backend + ports: + - containerPort: 3000 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace diff --git a/examples/kubernetes/authn/jwt.yaml b/examples/kubernetes/authn/jwt.yaml deleted file mode 100644 index db113716a96..00000000000 --- a/examples/kubernetes/authn/jwt.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: jwt-example -spec: - type: JWT - jwtProviders: - - name: example - remoteJWKS: - uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: backend -spec: - parentRefs: - - name: eg - hostnames: - - "www.example.com" - rules: - - backendRefs: - - group: "" - kind: Service - name: backend - port: 3000 - weight: 1 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: jwt-example - type: ExtensionRef - matches: - - path: - type: PathPrefix - value: /foo - - backendRefs: - - group: "" - kind: Service - name: backend - port: 3000 - weight: 1 - matches: - - path: - type: PathPrefix - value: /bar diff --git a/examples/kubernetes/envoy-proxy-config.yaml b/examples/kubernetes/envoy-proxy-config.yaml index de4846b0345..dbddea0d356 100644 --- a/examples/kubernetes/envoy-proxy-config.yaml +++ b/examples/kubernetes/envoy-proxy-config.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: config namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: config diff --git a/examples/kubernetes/grpc-routing.yaml b/examples/kubernetes/grpc-routing.yaml index c1e922d11a1..5e33a168358 100644 --- a/examples/kubernetes/grpc-routing.yaml +++ b/examples/kubernetes/grpc-routing.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: example-gateway-class @@ -7,7 +7,7 @@ metadata: spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway @@ -39,7 +39,7 @@ spec: spec: containers: - name: grpcsrv - image: quay.io/mhausenblas/yages:0.1.0 + image: ghcr.io/projectcontour/yages:v0.1.0 ports: - containerPort: 9000 protocol: TCP diff --git a/examples/kubernetes/http-routing.yaml b/examples/kubernetes/http-routing.yaml index a4d406cdfe1..bbfc6cec8e0 100644 --- a/examples/kubernetes/http-routing.yaml +++ b/examples/kubernetes/http-routing.yaml @@ -2,7 +2,7 @@ # This includes a GatewayClass, Gateway, Services and Deployments that are used as backends # for routing traffic. kind: GatewayClass -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 metadata: name: example-gateway-class labels: @@ -10,7 +10,7 @@ metadata: spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway @@ -70,7 +70,7 @@ spec: requests: cpu: 10m --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route @@ -133,7 +133,7 @@ spec: requests: cpu: 10m --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: foo-route @@ -247,7 +247,7 @@ spec: requests: cpu: 10m --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bar-route diff --git a/examples/kubernetes/jwt/grpc-jwt.yaml b/examples/kubernetes/jwt/grpc-jwt.yaml new file mode 100644 index 00000000000..c992f524fe8 --- /dev/null +++ b/examples/kubernetes/jwt/grpc-jwt.yaml @@ -0,0 +1,33 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-example +spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: yages + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: GRPCRoute +metadata: + name: yages + labels: + example: grpc-routing +spec: + parentRefs: + - name: example-gateway + hostnames: + - "grpc-example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: yages + port: 9000 + weight: 1 diff --git a/examples/kubernetes/authn/jwks.json b/examples/kubernetes/jwt/jwks.json similarity index 100% rename from examples/kubernetes/authn/jwks.json rename to examples/kubernetes/jwt/jwks.json diff --git a/examples/kubernetes/jwt/jwt.yaml b/examples/kubernetes/jwt/jwt.yaml new file mode 100644 index 00000000000..46381b13a2f --- /dev/null +++ b/examples/kubernetes/jwt/jwt.yaml @@ -0,0 +1,56 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-example +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: foo + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: foo +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: /foo +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: bar +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: /bar diff --git a/examples/kubernetes/authn/test.jwt b/examples/kubernetes/jwt/test.jwt similarity index 100% rename from examples/kubernetes/authn/test.jwt rename to examples/kubernetes/jwt/test.jwt diff --git a/examples/kubernetes/authn/with-different-claim.jwt b/examples/kubernetes/jwt/with-different-claim.jwt similarity index 100% rename from examples/kubernetes/authn/with-different-claim.jwt rename to examples/kubernetes/jwt/with-different-claim.jwt diff --git a/examples/kubernetes/metric/disable-prometheus.yaml b/examples/kubernetes/metric/disable-prometheus.yaml new file mode 100644 index 00000000000..ea611a5278c --- /dev/null +++ b/examples/kubernetes/metric/disable-prometheus.yaml @@ -0,0 +1,22 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + group: gateway.envoyproxy.io + kind: EnvoyProxy + name: prometheus + namespace: envoy-gateway-system +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: prometheus + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + prometheus: + disable: true diff --git a/examples/kubernetes/metric/otel-sink.yaml b/examples/kubernetes/metric/otel-sink.yaml index fec87b50fb3..e123f3d65f1 100644 --- a/examples/kubernetes/metric/otel-sink.yaml +++ b/examples/kubernetes/metric/otel-sink.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: otel-sink namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: otel-sink diff --git a/examples/kubernetes/metric/prometheus.yaml b/examples/kubernetes/metric/prometheus.yaml deleted file mode 100644 index 33a525c0766..00000000000 --- a/examples/kubernetes/metric/prometheus.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: eg -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parametersRef: - group: config.gateway.envoyproxy.io - kind: EnvoyProxy - name: prometheus - namespace: envoy-gateway-system ---- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 -kind: EnvoyProxy -metadata: - name: prometheus - namespace: envoy-gateway-system -spec: - telemetry: - metrics: - prometheus: {} diff --git a/examples/kubernetes/multicluster-service.yaml b/examples/kubernetes/multicluster-service.yaml new file mode 100644 index 00000000000..4bbc81131a1 --- /dev/null +++ b/examples/kubernetes/multicluster-service.yaml @@ -0,0 +1,71 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: custom-proxy-config + namespace: envoy-gateway-system +spec: + provider: + kubernetes: + envoyService: + type: ClusterIP + type: Kubernetes +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + group: gateway.envoyproxy.io + kind: EnvoyProxy + name: custom-proxy-config + namespace: envoy-gateway-system +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: backend-default-cluster2 + namespace: submariner-operator + port: 3000 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + namespace: submariner-operator + name: referencegrant-1 +spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: default + to: + - group: multicluster.x-k8s.io + kind: ServiceImport diff --git a/examples/kubernetes/quickstart.yaml b/examples/kubernetes/quickstart.yaml index 032216678e5..e70a4df5996 100644 --- a/examples/kubernetes/quickstart.yaml +++ b/examples/kubernetes/quickstart.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -69,7 +69,7 @@ spec: fieldRef: fieldPath: metadata.namespace --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/examples/kubernetes/tcp-routing.yaml b/examples/kubernetes/tcp-routing.yaml index 546b500c74a..cc203bd1cf3 100644 --- a/examples/kubernetes/tcp-routing.yaml +++ b/examples/kubernetes/tcp-routing.yaml @@ -1,11 +1,11 @@ kind: GatewayClass -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: tcp-gateway diff --git a/examples/kubernetes/tls-termination.yaml b/examples/kubernetes/tls-termination.yaml index ab69765ef1f..0ffc34c4637 100644 --- a/examples/kubernetes/tls-termination.yaml +++ b/examples/kubernetes/tls-termination.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg diff --git a/examples/kubernetes/tracing/default.yaml b/examples/kubernetes/tracing/default.yaml index 7c58e4fff7b..ea6caf143e8 100644 --- a/examples/kubernetes/tracing/default.yaml +++ b/examples/kubernetes/tracing/default.yaml @@ -1,16 +1,16 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: tracing namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: tracing @@ -23,6 +23,7 @@ spec: provider: host: otel-collector.monitoring.svc.cluster.local port: 4317 + type: OpenTelemetry customTags: # This is an example of using a literal as a tag value key1: diff --git a/examples/redis/redis.yaml b/examples/redis/redis.yaml index 74f6f6788de..1a6d779265d 100644 --- a/examples/redis/redis.yaml +++ b/examples/redis/redis.yaml @@ -56,7 +56,7 @@ metadata: namespace: envoy-gateway-system data: envoy-gateway.yaml: | - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway provider: type: Kubernetes diff --git a/go.mod b/go.mod index d9c60475df1..a8236d7e112 100644 --- a/go.mod +++ b/go.mod @@ -1,44 +1,63 @@ module github.com/envoyproxy/gateway -go 1.20 +go 1.21 require ( github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 github.com/davecgh/go-spew v1.1.1 - github.com/envoyproxy/go-control-plane v0.11.1 + github.com/envoyproxy/go-control-plane v0.11.2-0.20231116045842-b54b6db2c2a8 github.com/envoyproxy/ratelimit v1.4.1-0.20230427142404-e2a87f41d3a7 - github.com/evanphx/json-patch/v5 v5.6.0 + github.com/evanphx/json-patch/v5 v5.7.0 github.com/go-logfmt/logfmt v0.6.0 - github.com/go-logr/logr v1.2.4 - github.com/go-logr/zapr v1.2.4 + github.com/go-logr/logr v1.3.0 + github.com/go-logr/zapr v1.3.0 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.3 - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/grafana/tempo v1.5.0 + github.com/miekg/dns v1.1.57 github.com/pkg/errors v0.9.1 - github.com/prometheus/common v0.44.0 - github.com/spf13/cobra v1.7.0 + github.com/prometheus/common v0.45.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 github.com/tetratelabs/multierror v1.1.1 github.com/tsaarni/certyaml v0.9.2 + go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 + go.opentelemetry.io/otel/exporters/prometheus v0.44.0 + go.opentelemetry.io/otel/metric v1.21.0 + go.opentelemetry.io/otel/sdk/metric v1.21.0 go.opentelemetry.io/proto/otlp v1.0.0 - go.uber.org/zap v1.24.0 - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e - google.golang.org/grpc v1.57.0 + go.uber.org/zap v1.26.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + google.golang.org/grpc v1.60.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.27.4 - k8s.io/apiextensions-apiserver v0.27.4 - k8s.io/apimachinery v0.27.4 - k8s.io/cli-runtime v0.27.4 - k8s.io/client-go v0.27.4 - k8s.io/kubectl v0.27.4 - k8s.io/utils v0.0.0-20230209194617-a36077c30491 - sigs.k8s.io/controller-runtime v0.15.0 - sigs.k8s.io/gateway-api v0.7.1 - sigs.k8s.io/yaml v1.3.0 + k8s.io/api v0.28.4 + k8s.io/apiextensions-apiserver v0.28.3 + k8s.io/apimachinery v0.28.4 + k8s.io/cli-runtime v0.28.4 + k8s.io/client-go v0.28.4 + k8s.io/kubectl v0.28.4 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b + sigs.k8s.io/controller-runtime v0.16.3 + sigs.k8s.io/gateway-api v1.0.0 + sigs.k8s.io/yaml v1.4.0 +) + +require ( + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + go.opentelemetry.io/otel/sdk v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/tools v0.14.0 // indirect ) require ( @@ -48,25 +67,25 @@ require ( github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.1 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect + github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -74,8 +93,7 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lyft/gostats v0.4.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -84,34 +102,34 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.1 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/tsaarni/x500dn v1.0.0 // indirect - github.com/xlab/treeprint v1.1.0 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/component-base v0.27.4 // indirect + k8s.io/component-base v0.28.4 // indirect k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.13.2 // indirect - sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/mcs-api v0.1.0 + sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect ) diff --git a/go.sum b/go.sum index 259e16e4071..92e701d96ad 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,48 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= @@ -22,57 +53,160 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/datawire/dlib v1.3.0 h1:KkmyXU1kwm3oPBk1ypR70YbcOlEXWzEbx5RE0iRXTGk= +github.com/datawire/dlib v1.3.0/go.mod h1:NiGDmetmbkBvtznpWSx6C0vA0s0LK9aHna3LJDqjruk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/go-control-plane v0.11.2-0.20231116045842-b54b6db2c2a8 h1:HvQxuGnVQ7zCIz2B90WuTfOcJhoLW1d3u1NQ8j0T9BU= +github.com/envoyproxy/go-control-plane v0.11.2-0.20231116045842-b54b6db2c2a8/go.mod h1:3X10o7QcAVxP4y/hnTLgkXLwuZV2DxAEh6uaYD5PoxI= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.1 h1:kt9FtLiooDc0vbwTLhdg3dyNX1K9Qwa1EK9LcD4jVUQ= -github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/envoyproxy/ratelimit v1.4.1-0.20230427142404-e2a87f41d3a7 h1:yz9/p/8QVPuEjPqRfZDXJmRaURKpKkxCZXUhl22i+cU= github.com/envoyproxy/ratelimit v1.4.1-0.20230427142404-e2a87f41d3a7/go.mod h1:NmJBO+gDMvSQWvcSWq8wmlgkDmHHAkx1SCxEGva5hKU= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= +github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= -github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -82,54 +216,100 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/tempo v1.5.0 h1:JSwulLVtXvUw2MyuUPcvRg3MJiwTUs5XWnbG6fOKatc= github.com/grafana/tempo v1.5.0/go.mod h1:IB52YU6zkGL+3t0eNrY8kAExx0lLa4LH20wGu3c4wD8= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -137,12 +317,30 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lyft/gostats v0.4.1 h1:oR6p4HRCGxt0nUntmZIWmYMgyothBi3eZH2A71vRjsc= github.com/lyft/gostats v0.4.1/go.mod h1:Tpx2xRzz4t+T2Tx0xdVgIoBdR2UMVz+dKnE3X01XSd8= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= @@ -150,47 +348,112 @@ github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbD github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -198,189 +461,341 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/telepresenceio/telepresence/rpc/v2 v2.6.8 h1:q5V85LBT9bA/c4YPa/kMvJGyKZDgBPJTftlAMqJx7j4= +github.com/telepresenceio/telepresence/rpc/v2 v2.6.8/go.mod h1:VlgfRoXaW6Tl8IZbHmMWhITne8HY09/wOFtABHGj3ic= github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 h1:GMw3nEaOVyi+tNiGko5kAeRtoiEIpXNHmISyZ7fpw14= github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7/go.mod h1:ihJ97e2gsd8GuzFF/I3B1qcik3XZLpXjumQifXi8Slg= github.com/tetratelabs/multierror v1.1.1 h1:84JfJvfmnYC5y4877Ag3c4PkCUmQwq6sGI6iRfBATKI= github.com/tetratelabs/multierror v1.1.1/go.mod h1:hAyQ/XifPOPdb0x3GsowbjsjCPgT9Fsxjety9TxmuGY= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tsaarni/certyaml v0.9.2 h1:LoRTuajwjJ1CHAJiMv5cpOtwQ05207Oqe6cT9D7WDaQ= github.com/tsaarni/certyaml v0.9.2/go.mod h1:s+ErAC1wZ32r1ihSULvR7HXedKKN5HZasdb8Cj8gT9E= github.com/tsaarni/x500dn v1.0.0 h1:LvaWTkqRpse4VHBhB5uwf3wytokK4vF9IOyNAEyiA+U= github.com/tsaarni/x500dn v1.0.0/go.mod h1:QaHa3EcUKC4dfCAZmj8+ZRGLKukWgpGv9H3oOCsAbcE= -github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as= +go.opentelemetry.io/otel/exporters/prometheus v0.44.0 h1:08qeJgaPC0YEBu2PQMbqU3rogTlyzpjhCI2b58Yn00w= +go.opentelemetry.io/otel/exporters/prometheus v0.44.0/go.mod h1:ERL2uIeBtg4TxZdojHUwzZfIFlUIjZtxubT5p4h1Gjg= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= +go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= -gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= +google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= -k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y= -k8s.io/apiextensions-apiserver v0.27.4 h1:ie1yZG4nY/wvFMIR2hXBeSVq+HfNzib60FjnBYtPGSs= -k8s.io/apiextensions-apiserver v0.27.4/go.mod h1:KHZaDr5H9IbGEnSskEUp/DsdXe1hMQ7uzpQcYUFt2bM= -k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= -k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/cli-runtime v0.27.4 h1:Zb0eci+58eHZNnoHhjRFc7W88s8dlG12VtIl3Nv2Hto= -k8s.io/cli-runtime v0.27.4/go.mod h1:k9Z1xiZq2xNplQmehpDquLgc+rE+pubpO1cK4al4Mlw= -k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= -k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= -k8s.io/component-base v0.27.4 h1:Wqc0jMKEDGjKXdae8hBXeskRP//vu1m6ypC+gwErj4c= -k8s.io/component-base v0.27.4/go.mod h1:hoiEETnLc0ioLv6WPeDt8vD34DDeB35MfQnxCARq3kY= +k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= +k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= +k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= +k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= +k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= +k8s.io/cli-runtime v0.28.4 h1:IW3aqSNFXiGDllJF4KVYM90YX4cXPGxuCxCVqCD8X+Q= +k8s.io/cli-runtime v0.28.4/go.mod h1:MLGRB7LWTIYyYR3d/DOgtUC8ihsAPA3P8K8FDNIqJ0k= +k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= +k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo= +k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/kubectl v0.27.4 h1:RV1TQLIbtL34+vIM+W7HaS3KfAbqvy9lWn6pWB9els4= -k8s.io/kubectl v0.27.4/go.mod h1:qtc1s3BouB9KixJkriZMQqTsXMc+OAni6FeKAhq7q14= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= -sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= -sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ= -sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ= +k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= +sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= +sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= -sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= -sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= -sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/kind v0.8.1/go.mod h1:oNKTxUVPYkV9lWzY6CVMNluVq8cBsyq+UgPJdvA3uu4= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= +sigs.k8s.io/mcs-api v0.1.0 h1:edDbg0oRGfXw8TmZjKYep06LcJLv/qcYLidejnUp0PM= +sigs.k8s.io/mcs-api v0.1.0/go.mod h1:gGiAryeFNB4GBsq2LBmVqSgKoobLxt+p7ii/WG5QYYw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/admin/server.go b/internal/admin/server.go new file mode 100644 index 00000000000..9c035b43816 --- /dev/null +++ b/internal/admin/server.go @@ -0,0 +1,67 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package admin + +import ( + "net/http" + "net/http/pprof" + "time" + + "github.com/davecgh/go-spew/spew" + + "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/logging" +) + +var ( + adminLogger = logging.DefaultLogger(v1alpha1.LogLevelInfo).WithName("admin") +) + +func Init(cfg *config.Server) error { + if cfg.EnvoyGateway.GetEnvoyGatewayAdmin().EnableDumpConfig { + spewConfig := spew.NewDefaultConfig() + spewConfig.DisableMethods = true + spewConfig.Dump(cfg) + } + + return start(cfg) +} + +func start(cfg *config.Server) error { + handlers := http.NewServeMux() + address := cfg.EnvoyGateway.GetEnvoyGatewayAdminAddress() + enablePprof := cfg.EnvoyGateway.GetEnvoyGatewayAdmin().EnablePprof + + adminLogger.Info("starting admin server", "address", address, "enablePprof", enablePprof) + + if enablePprof { + // Serve pprof endpoints to aid in live debugging. + handlers.HandleFunc("/debug/pprof/", pprof.Index) + handlers.HandleFunc("/debug/pprof/profile", pprof.Profile) + handlers.HandleFunc("/debug/pprof/trace", pprof.Trace) + handlers.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + handlers.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + } + + adminServer := &http.Server{ + Handler: handlers, + Addr: address, + ReadTimeout: 5 * time.Second, + ReadHeaderTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 15 * time.Second, + } + + // Listen And Serve Admin Server. + go func() { + if err := adminServer.ListenAndServe(); err != nil { + cfg.Logger.Error(err, "start admin server failed") + } + }() + + return nil +} diff --git a/internal/admin/server_test.go b/internal/admin/server_test.go new file mode 100644 index 00000000000..199ac95853b --- /dev/null +++ b/internal/admin/server_test.go @@ -0,0 +1,25 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package admin + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/envoygateway/config" +) + +func TestInitAdminServer(t *testing.T) { + svrConfig := &config.Server{ + EnvoyGateway: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{}, + }, + } + err := Init(svrConfig) + assert.NoError(t, err) +} diff --git a/internal/cmd/certgen.go b/internal/cmd/certgen.go index 71ec551a7b6..83dafca8440 100644 --- a/internal/cmd/certgen.go +++ b/internal/cmd/certgen.go @@ -66,7 +66,8 @@ func outputCerts(ctx context.Context, cli client.Client, cfg *config.Server, cer if cfg.EnvoyGateway != nil && cfg.EnvoyGateway.Provider != nil && cfg.EnvoyGateway.Provider.Kubernetes != nil && - cfg.EnvoyGateway.Provider.Kubernetes.OverwriteControlPlaneCerts { + cfg.EnvoyGateway.Provider.Kubernetes.OverwriteControlPlaneCerts != nil && + *cfg.EnvoyGateway.Provider.Kubernetes.OverwriteControlPlaneCerts { updateSecrets = true } secrets, err := kubernetes.CreateOrUpdateSecrets(ctx, cli, kubernetes.CertsToSecret(cfg.Namespace, certs), updateSecrets) diff --git a/internal/cmd/egctl/envoy_stats.go b/internal/cmd/egctl/envoy_stats.go new file mode 100644 index 00000000000..50e3ddb2b9b --- /dev/null +++ b/internal/cmd/egctl/envoy_stats.go @@ -0,0 +1,205 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package egctl + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "sync" + + "github.com/spf13/cobra" + "github.com/tetratelabs/multierror" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/yaml" + + "github.com/envoyproxy/gateway/internal/kubernetes" +) + +const ( + prometheusOutput = "prom" +) + +var ( + statsType, outputFormat string +) + +func newEnvoyStatsCmd() *cobra.Command { + var podName, podNamespace string + + statsConfigCmd := &cobra.Command{ + Use: "envoy-proxy -n ", + Short: "Retrieves Envoy metrics in the specified pod", + Long: `Retrieve Envoy emitted metrics for the specified pod.`, + Example: ` # Retrieve Envoy emitted metrics for the specified pod. + egctl experimental stats -n + + # Retrieve Envoy server metrics in prometheus format + egctl experimental stats envoy-proxy -n --output prom + + # Retrieve Envoy cluster metrics + egctl experimental stats envoy-proxy -n --type clusters +`, + Aliases: []string{"ep"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 && len(labelSelectors) == 0 { + cmd.Println(cmd.UsageString()) + return fmt.Errorf("stats requires pod name or label selector") + } + return nil + }, + RunE: func(c *cobra.Command, args []string) error { + stats := map[string]string{} + kubeClient, err := getCLIClient() + if err != nil { + return err + } + if len(args) != 0 { + podName = args[0] + } + pods, err := fetchRunningEnvoyPods(kubeClient, types.NamespacedName{Namespace: podNamespace, Name: podName}, labelSelectors, allNamespaces) + if err != nil { + return err + } + var errs error + var wg sync.WaitGroup + switch statsType { + case "", "server": + wg.Add(len(pods)) + for _, pod := range pods { + go func(pod types.NamespacedName) { + stats[pod.Namespace+"/"+pod.Name], err = setupEnvoyServerStatsConfig(kubeClient, pod.Name, pod.Namespace, outputFormat) + if err != nil { + errs = multierror.Append(errs, err) + } + wg.Done() + }(pod) + } + + wg.Wait() + case "cluster", "clusters": + wg.Add(len(pods)) + for _, pod := range pods { + go func(pod types.NamespacedName) { + stats[pod.Namespace+"/"+pod.Name], err = setupEnvoyClusterStatsConfig(kubeClient, pod.Name, pod.Namespace, outputFormat) + if err != nil { + errs = multierror.Append(errs, err) + } + wg.Done() + }(pod) + } + wg.Wait() + default: + return fmt.Errorf("unknown stats type %s", statsType) + } + + if errs != nil { + return errs + } + + switch outputFormat { + case jsonOutput: + statsBytes, err := json.Marshal(stats) + if err != nil { + return err + } + _, _ = fmt.Fprint(c.OutOrStdout(), string(statsBytes)) + // convert the json output to yaml + case yamlOutput: + var out []byte + statsBytes, err := json.Marshal(stats) + if err != nil { + return err + } + if out, err = yaml.JSONToYAML(statsBytes); err != nil { + return err + } + _, _ = fmt.Fprint(c.OutOrStdout(), string(out)) + default: + for namespacedName, stat := range stats { + _, _ = fmt.Fprint(c.OutOrStdout(), namespacedName+":\n") + _, _ = fmt.Fprint(c.OutOrStdout(), stat) + } + } + + return nil + }, + } + statsConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", prometheusOutput, "Output format: one of json|yaml|prom") + statsConfigCmd.PersistentFlags().StringVarP(&statsType, "type", "t", "server", "Where to grab the stats: one of server|clusters") + statsConfigCmd.PersistentFlags().StringArrayVarP(&labelSelectors, "labels", "l", nil, "Labels to select the envoy proxy pod.") + statsConfigCmd.PersistentFlags().StringVarP(&podNamespace, "namespace", "n", "envoy-gateway-system", "Namespace where envoy proxy pod are installed.") + + return statsConfigCmd +} + +func setupEnvoyServerStatsConfig(kubeClient kubernetes.CLIClient, podName, podNamespace string, outputFormat string) (string, error) { + path := "stats" + if outputFormat == jsonOutput || outputFormat == yamlOutput { + // for yaml output we will convert the json to yaml when printed + path += "?format=json" + } else { + path += "/prometheus" + } + + fw, err := portForwarder(kubeClient, types.NamespacedName{Namespace: podNamespace, Name: podName}) + if err != nil { + return "", fmt.Errorf("failed to initialize pod-forwarding for %s/%s: %v", podNamespace, podName, err) + } + err = fw.Start() + if err != nil { + return "", fmt.Errorf("failed to start port forwarding for pod %s/%s: %v", podNamespace, podName, err) + } + defer fw.Stop() + + result, err := statsRequest(fw.Address(), path) + if err != nil { + return "", fmt.Errorf("failed to get stats on envoy for pod %s/%s: %v", podNamespace, podName, err) + } + return string(result), nil +} + +func setupEnvoyClusterStatsConfig(kubeClient kubernetes.CLIClient, podName, podNamespace string, outputFormat string) (string, error) { + path := "clusters" + if outputFormat == jsonOutput || outputFormat == yamlOutput { + // for yaml output we will convert the json to yaml when printed + path += "?format=json" + } + fw, err := portForwarder(kubeClient, types.NamespacedName{Namespace: podNamespace, Name: podName}) + if err != nil { + return "", fmt.Errorf("failed to initialize pod-forwarding for %s/%s: %v", podNamespace, podName, err) + } + err = fw.Start() + if err != nil { + return "", fmt.Errorf("failed to start port forwarding for pod %s/%s: %v", podNamespace, podName, err) + } + defer fw.Stop() + + result, err := statsRequest(fw.Address(), path) + if err != nil { + return "", fmt.Errorf("failed to get stats on envoy for pod %s/%s: %v", podNamespace, podName, err) + } + return string(result), nil +} + +func statsRequest(address string, path string) ([]byte, error) { + url := fmt.Sprintf("http://%s/%s", address, path) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer func() { + _ = resp.Body.Close() + }() + + return io.ReadAll(resp.Body) +} diff --git a/internal/cmd/egctl/experimental.go b/internal/cmd/egctl/experimental.go index a0e16f2ae33..7fb744fb8de 100644 --- a/internal/cmd/egctl/experimental.go +++ b/internal/cmd/egctl/experimental.go @@ -23,6 +23,7 @@ func newExperimentalCommand() *cobra.Command { } experimentalCommand.AddCommand(NewTranslateCommand()) + experimentalCommand.AddCommand(statsCommand()) return experimentalCommand } diff --git a/internal/cmd/egctl/stats.go b/internal/cmd/egctl/stats.go new file mode 100644 index 00000000000..a1633872178 --- /dev/null +++ b/internal/cmd/egctl/stats.go @@ -0,0 +1,22 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package egctl + +import ( + "github.com/spf13/cobra" +) + +func statsCommand() *cobra.Command { + c := &cobra.Command{ + Use: "stats", + Long: "Retrieve statistics from envoy proxy.", + Short: "Retrieve stats from envoy proxy.", + } + + c.AddCommand(newEnvoyStatsCmd()) + + return c +} diff --git a/internal/cmd/egctl/testdata/config/in/in.all.json b/internal/cmd/egctl/testdata/config/in/in.all.json index 2ed81bb5bf3..637838e8b1e 100644 --- a/internal/cmd/egctl/testdata/config/in/in.all.json +++ b/internal/cmd/egctl/testdata/config/in/in.all.json @@ -2203,20 +2203,12 @@ { "name": "default-eg-http", "domains": [ - "*" + "www.example.com" ], "routes": [ { "match": { - "prefix": "/", - "headers": [ - { - "name": ":authority", - "string_match": { - "exact": "www.example.com" - } - } - ] + "prefix": "/" }, "route": { "cluster": "default-backend-rule-0-match-0-www.example.com" diff --git a/internal/cmd/egctl/testdata/config/out/out.all.json b/internal/cmd/egctl/testdata/config/out/out.all.json index 8ee1554f3a6..d3b40aa711c 100644 --- a/internal/cmd/egctl/testdata/config/out/out.all.json +++ b/internal/cmd/egctl/testdata/config/out/out.all.json @@ -2204,20 +2204,12 @@ "virtualHosts": [ { "domains": [ - "*" + "www.example.com" ], "name": "default-eg-http", "routes": [ { "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.example.com" - } - } - ], "prefix": "/" }, "route": { diff --git a/internal/cmd/egctl/testdata/config/out/out.all.yaml b/internal/cmd/egctl/testdata/config/out/out.all.yaml index c4421489b10..61384130867 100644 --- a/internal/cmd/egctl/testdata/config/out/out.all.yaml +++ b/internal/cmd/egctl/testdata/config/out/out.all.yaml @@ -1248,14 +1248,10 @@ default: name: default-eg-http virtualHosts: - domains: - - '*' + - www.example.com name: default-eg-http routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com prefix: / route: cluster: default-backend-rule-0-match-0-www.example.com diff --git a/internal/cmd/egctl/testdata/config/out/out.route.json b/internal/cmd/egctl/testdata/config/out/out.route.json index 90f142df68f..a805fb099c8 100644 --- a/internal/cmd/egctl/testdata/config/out/out.route.json +++ b/internal/cmd/egctl/testdata/config/out/out.route.json @@ -11,20 +11,12 @@ "virtualHosts": [ { "domains": [ - "*" + "www.example.com" ], "name": "default-eg-http", "routes": [ { "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.example.com" - } - } - ], "prefix": "/" }, "route": { diff --git a/internal/cmd/egctl/testdata/config/out/out.route.yaml b/internal/cmd/egctl/testdata/config/out/out.route.yaml index cf0e5b1bb49..ad05c4f2311 100644 --- a/internal/cmd/egctl/testdata/config/out/out.route.yaml +++ b/internal/cmd/egctl/testdata/config/out/out.route.yaml @@ -8,14 +8,10 @@ default: name: default-eg-http virtualHosts: - domains: - - '*' + - www.example.com name: default-eg-http routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com prefix: / route: cluster: default-backend-rule-0-match-0-www.example.com diff --git a/internal/cmd/egctl/testdata/translate/in/authn-single-route-single-match-to-xds.yaml b/internal/cmd/egctl/testdata/translate/in/authn-single-route-single-match-to-xds.yaml deleted file mode 100644 index 50856df6366..00000000000 --- a/internal/cmd/egctl/testdata/translate/in/authn-single-route-single-match-to-xds.yaml +++ /dev/null @@ -1,108 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: eg -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: eg -spec: - gatewayClassName: eg - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: backend ---- -apiVersion: v1 -kind: Service -metadata: - name: backend - labels: - app: backend - service: backend -spec: - ports: - - name: http - port: 3000 - targetPort: 3000 - selector: - app: backend ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: backend -spec: - replicas: 1 - selector: - matchLabels: - app: backend - version: v1 - template: - metadata: - labels: - app: backend - version: v1 - spec: - serviceAccountName: backend - containers: - - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e - imagePullPolicy: IfNotPresent - name: backend - ports: - - containerPort: 3000 - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter -metadata: - name: jwt-example -spec: - type: JWT - jwtProviders: - - name: example - remoteJWKS: - uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: backend -spec: - parentRefs: - - name: eg - hostnames: - - "www.example.com" - rules: - - backendRefs: - - group: "" - kind: Service - name: backend - port: 3000 - weight: 1 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: jwt-example - type: ExtensionRef - matches: - - path: - type: PathPrefix - value: /foo diff --git a/internal/cmd/egctl/testdata/translate/in/default-resources.yaml b/internal/cmd/egctl/testdata/translate/in/default-resources.yaml index 4eb7e3711c3..1cdb52f993a 100644 --- a/internal/cmd/egctl/testdata/translate/in/default-resources.yaml +++ b/internal/cmd/egctl/testdata/translate/in/default-resources.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -91,7 +91,7 @@ spec: port: 3000 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/echo-gateway-api.yaml b/internal/cmd/egctl/testdata/translate/in/echo-gateway-api.yaml index 032216678e5..f0e412d92b0 100644 --- a/internal/cmd/egctl/testdata/translate/in/echo-gateway-api.yaml +++ b/internal/cmd/egctl/testdata/translate/in/echo-gateway-api.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -29,6 +29,7 @@ metadata: app: backend service: backend spec: + clusterIP: 7.7.7.7 ports: - name: http port: 3000 @@ -69,7 +70,7 @@ spec: fieldRef: fieldPath: metadata.namespace --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml b/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml index 4851a468410..e020bc9d429 100644 --- a/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml +++ b/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -32,6 +32,7 @@ metadata: app: backend service: backend spec: + clusterIP: 7.7.7.7 ports: - name: http port: 3000 @@ -73,7 +74,7 @@ spec: fieldRef: fieldPath: metadata.namespace --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/from-gateway-api-to-xds.yaml b/internal/cmd/egctl/testdata/translate/in/from-gateway-api-to-xds.yaml index 348cf15c998..b13096a2e3a 100644 --- a/internal/cmd/egctl/testdata/translate/in/from-gateway-api-to-xds.yaml +++ b/internal/cmd/egctl/testdata/translate/in/from-gateway-api-to-xds.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -123,7 +123,7 @@ spec: port: 3000 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/invalid-envoyproxy.yaml b/internal/cmd/egctl/testdata/translate/in/invalid-envoyproxy.yaml index 0900c467672..9e31a94aa6a 100644 --- a/internal/cmd/egctl/testdata/translate/in/invalid-envoyproxy.yaml +++ b/internal/cmd/egctl/testdata/translate/in/invalid-envoyproxy.yaml @@ -1,34 +1,36 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: example namespace: default spec: - bootstrap: | - admin: - access_log: - - name: envoy.access_loggers.file - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socket_address: - address: 127.0.0.1 - port_value: 19000 + bootstrap: + type: Replace + value: | + admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: example namespace: default --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -146,7 +148,7 @@ spec: port: 3000 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/jwt-single-route-single-match-to-xds.yaml b/internal/cmd/egctl/testdata/translate/in/jwt-single-route-single-match-to-xds.yaml new file mode 100644 index 00000000000..14efe586c4e --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/in/jwt-single-route-single-match-to-xds.yaml @@ -0,0 +1,107 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: backend +--- +apiVersion: v1 +kind: Service +metadata: + name: backend + labels: + app: backend + service: backend +spec: + clusterIP: 7.7.7.7 + ports: + - name: http + port: 3000 + targetPort: 3000 + selector: + app: backend +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend +spec: + replicas: 1 + selector: + matchLabels: + app: backend + version: v1 + template: + metadata: + labels: + app: backend + version: v1 + spec: + serviceAccountName: backend + containers: + - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e + imagePullPolicy: IfNotPresent + name: backend + ports: + - containerPort: 3000 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-example +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: backend + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: /foo diff --git a/internal/cmd/egctl/testdata/translate/in/multiple-xds.yaml b/internal/cmd/egctl/testdata/translate/in/multiple-xds.yaml index 8a332388c9c..4f6b54faceb 100644 --- a/internal/cmd/egctl/testdata/translate/in/multiple-xds.yaml +++ b/internal/cmd/egctl/testdata/translate/in/multiple-xds.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -41,7 +41,7 @@ spec: selector: app: backend --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend @@ -63,7 +63,7 @@ spec: type: PathPrefix value: / --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg2 @@ -75,7 +75,7 @@ spec: protocol: HTTP port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/no-gateway-class-resources.yaml b/internal/cmd/egctl/testdata/translate/in/no-gateway-class-resources.yaml index 25f2e5fe292..529fae44af2 100644 --- a/internal/cmd/egctl/testdata/translate/in/no-gateway-class-resources.yaml +++ b/internal/cmd/egctl/testdata/translate/in/no-gateway-class-resources.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -9,7 +9,7 @@ spec: protocol: HTTP port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/quickstart.yaml b/internal/cmd/egctl/testdata/translate/in/quickstart.yaml index 032216678e5..f0e412d92b0 100644 --- a/internal/cmd/egctl/testdata/translate/in/quickstart.yaml +++ b/internal/cmd/egctl/testdata/translate/in/quickstart.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -29,6 +29,7 @@ metadata: app: backend service: backend spec: + clusterIP: 7.7.7.7 ports: - name: http port: 3000 @@ -69,7 +70,7 @@ spec: fieldRef: fieldPath: metadata.namespace --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/rate-limit-filter-single-route-single-match-to-xds.yaml b/internal/cmd/egctl/testdata/translate/in/rate-limit-filter-single-route-single-match-to-xds.yaml deleted file mode 100644 index b494734916b..00000000000 --- a/internal/cmd/egctl/testdata/translate/in/rate-limit-filter-single-route-single-match-to-xds.yaml +++ /dev/null @@ -1,112 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: eg -spec: - controllerName: gateway.envoyproxy.io/gatewayclass-controller ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: eg -spec: - gatewayClassName: eg - listeners: - - name: http - protocol: HTTP - port: 80 ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: backend ---- -apiVersion: v1 -kind: Service -metadata: - name: backend - labels: - app: backend - service: backend -spec: - ports: - - name: http - port: 3000 - targetPort: 3000 - selector: - app: backend ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: backend -spec: - replicas: 1 - selector: - matchLabels: - app: backend - version: v1 - template: - metadata: - labels: - app: backend - version: v1 - spec: - serviceAccountName: backend - containers: - - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e - imagePullPolicy: IfNotPresent - name: backend - ports: - - containerPort: 3000 - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace ---- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter -metadata: - name: ratelimit-specific-user -spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - name: x-user-id - value: one - limit: - requests: 3 - unit: Hour ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: http-ratelimit -spec: - parentRefs: - - name: eg - hostnames: - - "ratelimit.example" - rules: - - matches: - - path: - type: PathPrefix - value: / - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-specific-user - backendRefs: - - group: "" - kind: Service - name: backend - port: 3000 diff --git a/internal/cmd/egctl/testdata/translate/in/rejected-http-route.yaml b/internal/cmd/egctl/testdata/translate/in/rejected-http-route.yaml index ea4f4e98a3f..6979c77c15e 100644 --- a/internal/cmd/egctl/testdata/translate/in/rejected-http-route.yaml +++ b/internal/cmd/egctl/testdata/translate/in/rejected-http-route.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -16,7 +16,7 @@ spec: protocol: TLS port: 8443 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/in/valid-envoyproxy.yaml b/internal/cmd/egctl/testdata/translate/in/valid-envoyproxy.yaml index 67f55ea8562..bb9ca9478f9 100644 --- a/internal/cmd/egctl/testdata/translate/in/valid-envoyproxy.yaml +++ b/internal/cmd/egctl/testdata/translate/in/valid-envoyproxy.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: example @@ -11,19 +11,19 @@ spec: annotations: custom1: svc-annotation1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: example namespace: default --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg @@ -141,7 +141,7 @@ spec: port: 3000 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.all.json b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.all.json deleted file mode 100644 index 344e7b8e61c..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.all.json +++ /dev/null @@ -1,454 +0,0 @@ -{ - "xds": { - "envoy-gateway-system/eg": { - "configs": [ - { - "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump", - "bootstrap": { - "admin": { - "accessLog": [ - { - "name": "envoy.access_loggers.file", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", - "path": "/dev/null" - } - } - ], - "address": { - "socketAddress": { - "address": "127.0.0.1", - "portValue": 19000 - } - } - }, - "dynamicResources": { - "adsConfig": { - "apiType": "DELTA_GRPC", - "grpcServices": [ - { - "envoyGrpc": { - "clusterName": "xds_cluster" - } - } - ], - "setNodeOnFirstMessageOnly": true, - "transportApiVersion": "V3" - }, - "cdsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "ldsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - } - }, - "layeredRuntime": { - "layers": [ - { - "name": "runtime-0", - "rtdsLayer": { - "name": "runtime-0", - "rtdsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - } - } - } - ] - }, - "staticResources": { - "clusters": [ - { - "connectTimeout": "10s", - "loadAssignment": { - "clusterName": "xds_cluster", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "address": "envoy-gateway", - "portValue": 18000 - } - } - } - } - ] - } - ] - }, - "name": "xds_cluster", - "transportSocket": { - "name": "envoy.transport_sockets.tls", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", - "commonTlsContext": { - "tlsCertificateSdsSecretConfigs": [ - { - "name": "xds_certificate", - "sdsConfig": { - "pathConfigSource": { - "path": "/sds/xds-certificate.json" - }, - "resourceApiVersion": "V3" - } - } - ], - "tlsParams": { - "tlsMaximumProtocolVersion": "TLSv1_3" - }, - "validationContextSdsSecretConfig": { - "name": "xds_trusted_ca", - "sdsConfig": { - "pathConfigSource": { - "path": "/sds/xds-trusted-ca.json" - }, - "resourceApiVersion": "V3" - } - } - } - } - }, - "type": "STRICT_DNS", - "typedExtensionProtocolOptions": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicitHttpConfig": { - "http2ProtocolOptions": {} - } - } - } - } - ], - "listeners": [ - { - "address": { - "socketAddress": { - "address": "0.0.0.0", - "portValue": 19001 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "httpFilters": [ - { - "name": "envoy.filters.http.health_check", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck", - "headers": [ - { - "name": ":path", - "stringMatch": { - "exact": "/ready" - } - } - ], - "passThroughMode": false - } - }, - { - "name": "envoy.filters.http.router", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - } - ], - "routeConfig": { - "name": "local_route" - }, - "statPrefix": "eg-ready-http" - } - } - ] - } - ], - "name": "envoy-gateway-proxy-ready-0.0.0.0-19001" - } - ] - } - } - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump", - "dynamicEndpointConfigs": [ - { - "endpointConfig": { - "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "envoy-gateway-system/backend/rule/0/match/0-www.example.com", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "portValue": 3000 - } - } - }, - "loadBalancingWeight": 1 - } - ], - "loadBalancingWeight": 1, - "locality": {} - } - ] - } - } - ] - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump", - "dynamicActiveClusters": [ - { - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "commonLbConfig": { - "localityWeightedLbConfig": {} - }, - "connectTimeout": "10s", - "dnsLookupFamily": "V4_ONLY", - "edsClusterConfig": { - "edsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "serviceName": "envoy-gateway-system/backend/rule/0/match/0-www.example.com" - }, - "name": "envoy-gateway-system/backend/rule/0/match/0-www.example.com", - "outlierDetection": {}, - "perConnectionBufferLimitBytes": 32768, - "type": "EDS" - } - }, - { - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "commonLbConfig": { - "localityWeightedLbConfig": {} - }, - "connectTimeout": "10s", - "dnsLookupFamily": "V4_ONLY", - "dnsRefreshRate": "30s", - "loadAssignment": { - "clusterName": "raw_githubusercontent_com_443", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "address": "raw.githubusercontent.com", - "portValue": 443 - } - } - } - } - ], - "loadBalancingWeight": 1, - "locality": {} - } - ] - }, - "name": "raw_githubusercontent_com_443", - "outlierDetection": {}, - "perConnectionBufferLimitBytes": 32768, - "respectDnsTtl": true, - "transportSocket": { - "name": "envoy.transport_sockets.tls", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", - "commonTlsContext": { - "validationContext": { - "trustedCa": { - "filename": "/etc/ssl/certs/ca-certificates.crt" - } - } - } - } - }, - "type": "STRICT_DNS" - } - } - ] - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump", - "dynamicListeners": [ - { - "activeState": { - "listener": { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "accessLog": [ - { - "filter": { - "responseFlagFilter": { - "flags": [ - "NR" - ] - } - }, - "name": "envoy.access_loggers.file", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", - "logFormat": { - "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" - } - }, - "path": "/dev/stdout" - } - } - ], - "address": { - "socketAddress": { - "address": "0.0.0.0", - "portValue": 10080 - } - }, - "defaultFilterChain": { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "accessLog": [ - { - "name": "envoy.access_loggers.file", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", - "logFormat": { - "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" - } - }, - "path": "/dev/stdout" - } - } - ], - "commonHttpProtocolOptions": { - "headersWithUnderscoresAction": "REJECT_REQUEST" - }, - "http2ProtocolOptions": { - "initialConnectionWindowSize": 1048576, - "initialStreamWindowSize": 65536, - "maxConcurrentStreams": 100 - }, - "httpFilters": [ - { - "name": "envoy.filters.http.jwt_authn", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication", - "providers": { - "envoy-gateway-system/backend/rule/0/match/0-www.example.com-example": { - "remoteJwks": { - "cacheDuration": "300s", - "httpUri": { - "cluster": "raw_githubusercontent_com_443", - "timeout": "5s", - "uri": "https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json" - } - } - } - }, - "requirementMap": { - "envoy-gateway-system/backend/rule/0/match/0-www.example.com": { - "providerName": "envoy-gateway-system/backend/rule/0/match/0-www.example.com-example" - } - } - } - }, - { - "name": "envoy.filters.http.router", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - } - ], - "mergeSlashes": true, - "normalizePath": true, - "pathWithEscapedSlashesAction": "UNESCAPE_AND_REDIRECT", - "rds": { - "configSource": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "routeConfigName": "envoy-gateway-system/eg/http" - }, - "statPrefix": "http", - "upgradeConfigs": [ - { - "upgradeType": "websocket" - } - ], - "useRemoteAddress": true - } - } - ] - }, - "name": "envoy-gateway-system/eg/http", - "perConnectionBufferLimitBytes": 32768 - } - } - } - ] - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump", - "dynamicRouteConfigs": [ - { - "routeConfig": { - "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "ignorePortInHostMatching": true, - "name": "envoy-gateway-system/eg/http", - "virtualHosts": [ - { - "domains": [ - "*" - ], - "name": "envoy-gateway-system/eg/http", - "routes": [ - { - "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.example.com" - } - } - ], - "pathSeparatedPrefix": "/foo" - }, - "name": "envoy-gateway-system/backend/rule/0/match/0-www.example.com", - "route": { - "cluster": "envoy-gateway-system/backend/rule/0/match/0-www.example.com" - }, - "typedPerFilterConfig": { - "envoy.filters.http.jwt_authn": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig", - "requirementName": "envoy-gateway-system/backend/rule/0/match/0-www.example.com" - } - } - } - ] - } - ] - } - } - ] - } - ] - } - } -} \ No newline at end of file diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.all.yaml b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.all.yaml deleted file mode 100644 index 040df2cf7f6..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.all.yaml +++ /dev/null @@ -1,261 +0,0 @@ -xds: - envoy-gateway-system/eg: - configs: - - '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump - bootstrap: - admin: - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socketAddress: - address: 127.0.0.1 - portValue: 19000 - dynamicResources: - adsConfig: - apiType: DELTA_GRPC - grpcServices: - - envoyGrpc: - clusterName: xds_cluster - setNodeOnFirstMessageOnly: true - transportApiVersion: V3 - cdsConfig: - ads: {} - resourceApiVersion: V3 - ldsConfig: - ads: {} - resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 - staticResources: - clusters: - - connectTimeout: 10s - loadAssignment: - clusterName: xds_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-gateway - portValue: 18000 - name: xds_cluster - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificateSdsSecretConfigs: - - name: xds_certificate - sdsConfig: - pathConfigSource: - path: /sds/xds-certificate.json - resourceApiVersion: V3 - tlsParams: - tlsMaximumProtocolVersion: TLSv1_3 - validationContextSdsSecretConfig: - name: xds_trusted_ca - sdsConfig: - pathConfigSource: - path: /sds/xds-trusted-ca.json - resourceApiVersion: V3 - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} - listeners: - - address: - socketAddress: - address: 0.0.0.0 - portValue: 19001 - filterChains: - - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - httpFilters: - - name: envoy.filters.http.health_check - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck - headers: - - name: :path - stringMatch: - exact: /ready - passThroughMode: false - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - routeConfig: - name: local_route - statPrefix: eg-ready-http - name: envoy-gateway-proxy-ready-0.0.0.0-19001 - - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump - dynamicEndpointConfigs: - - endpointConfig: - '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: envoy-gateway-system/backend/rule/0/match/0-www.example.com - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - portValue: 3000 - loadBalancingWeight: 1 - loadBalancingWeight: 1 - locality: {} - - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump - dynamicActiveClusters: - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: envoy-gateway-system/backend/rule/0/match/0-www.example.com - name: envoy-gateway-system/backend/rule/0/match/0-www.example.com - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: raw_githubusercontent_com_443 - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: raw.githubusercontent.com - portValue: 443 - loadBalancingWeight: 1 - locality: {} - name: raw_githubusercontent_com_443 - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: STRICT_DNS - - '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump - dynamicListeners: - - activeState: - listener: - '@type': type.googleapis.com/envoy.config.listener.v3.Listener - accessLog: - - filter: - responseFlagFilter: - flags: - - NR - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.jwt_authn - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication - providers: - envoy-gateway-system/backend/rule/0/match/0-www.example.com-example: - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: raw_githubusercontent_com_443 - timeout: 5s - uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json - requirementMap: - envoy-gateway-system/backend/rule/0/match/0-www.example.com: - providerName: envoy-gateway-system/backend/rule/0/match/0-www.example.com-example - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: envoy-gateway-system/eg/http - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: envoy-gateway-system/eg/http - perConnectionBufferLimitBytes: 32768 - - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump - dynamicRouteConfigs: - - routeConfig: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - ignorePortInHostMatching: true - name: envoy-gateway-system/eg/http - virtualHosts: - - domains: - - '*' - name: envoy-gateway-system/eg/http - routes: - - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com - pathSeparatedPrefix: /foo - name: envoy-gateway-system/backend/rule/0/match/0-www.example.com - route: - cluster: envoy-gateway-system/backend/rule/0/match/0-www.example.com - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: envoy-gateway-system/backend/rule/0/match/0-www.example.com diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.bootstrap.yaml b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.bootstrap.yaml deleted file mode 100644 index fb207fdf33c..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.bootstrap.yaml +++ /dev/null @@ -1,100 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump - bootstrap: - admin: - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socketAddress: - address: 127.0.0.1 - portValue: 19000 - dynamicResources: - adsConfig: - apiType: DELTA_GRPC - grpcServices: - - envoyGrpc: - clusterName: xds_cluster - setNodeOnFirstMessageOnly: true - transportApiVersion: V3 - cdsConfig: - ads: {} - resourceApiVersion: V3 - ldsConfig: - ads: {} - resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 - staticResources: - clusters: - - connectTimeout: 10s - loadAssignment: - clusterName: xds_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-gateway - portValue: 18000 - name: xds_cluster - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificateSdsSecretConfigs: - - name: xds_certificate - sdsConfig: - pathConfigSource: - path: /sds/xds-certificate.json - resourceApiVersion: V3 - tlsParams: - tlsMaximumProtocolVersion: TLSv1_3 - validationContextSdsSecretConfig: - name: xds_trusted_ca - sdsConfig: - pathConfigSource: - path: /sds/xds-trusted-ca.json - resourceApiVersion: V3 - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} - listeners: - - address: - socketAddress: - address: 0.0.0.0 - portValue: 19001 - filterChains: - - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - httpFilters: - - name: envoy.filters.http.health_check - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck - headers: - - name: :path - stringMatch: - exact: /ready - passThroughMode: false - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - routeConfig: - name: local_route - statPrefix: eg-ready-http - name: envoy-gateway-proxy-ready-0.0.0.0-19001 diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.cluster.yaml b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.cluster.yaml deleted file mode 100644 index fddbec4474f..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.cluster.yaml +++ /dev/null @@ -1,50 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump - dynamicActiveClusters: - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: envoy-gateway-system/backend/rule/0/match/0-www.example.com - name: envoy-gateway-system/backend/rule/0/match/0-www.example.com - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: raw_githubusercontent_com_443 - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: raw.githubusercontent.com - portValue: 443 - loadBalancingWeight: 1 - locality: {} - name: raw_githubusercontent_com_443 - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: STRICT_DNS diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.endpoint.yaml b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.endpoint.yaml deleted file mode 100644 index a579fde1893..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.endpoint.yaml +++ /dev/null @@ -1,16 +0,0 @@ -xds: - envoy-gateway-system-eg: - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump - dynamicEndpointConfigs: - - endpointConfig: - '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: envoy-gateway-system-backend-rule-0-match-0-www.example.com - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - portValue: 3000 - loadBalancingWeight: 1 - loadBalancingWeight: 1 - locality: {} diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.listener.yaml b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.listener.yaml deleted file mode 100644 index ccc28abb4b4..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.listener.yaml +++ /dev/null @@ -1,76 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump - dynamicListeners: - - activeState: - listener: - '@type': type.googleapis.com/envoy.config.listener.v3.Listener - accessLog: - - filter: - responseFlagFilter: - flags: - - NR - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.jwt_authn - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication - providers: - envoy-gateway-system/backend/rule/0/match/0-www.example.com-example: - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: raw_githubusercontent_com_443 - timeout: 5s - uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json - requirementMap: - envoy-gateway-system/backend/rule/0/match/0-www.example.com: - providerName: envoy-gateway-system/backend/rule/0/match/0-www.example.com-example - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: envoy-gateway-system/eg/http - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: envoy-gateway-system/eg/http - perConnectionBufferLimitBytes: 32768 diff --git a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.route.yaml b/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.route.yaml deleted file mode 100644 index ce59385a3d5..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/authn-single-route-single-match-to-xds.route.yaml +++ /dev/null @@ -1,26 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump - dynamicRouteConfigs: - - routeConfig: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - ignorePortInHostMatching: true - name: envoy-gateway-system/eg/http - virtualHosts: - - domains: - - '*' - name: envoy-gateway-system/eg/http - routes: - - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com - pathSeparatedPrefix: /foo - name: envoy-gateway-system/backend/rule/0/match/0-www.example.com - route: - cluster: envoy-gateway-system/backend/rule/0/match/0-www.example.com - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: envoy-gateway-system/backend/rule/0/match/0-www.example.com diff --git a/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml b/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml index d9ca7953e48..83d2c82e6d8 100644 --- a/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml +++ b/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml @@ -4,106 +4,126 @@ envoyProxy: name: default-envoy-proxy namespace: envoy-gateway-system spec: - bootstrap: | - admin: - access_log: - - name: envoy.access_loggers.file - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socket_address: - address: 127.0.0.1 - port_value: 19000 - dynamic_resources: - ads_config: - api_type: DELTA_GRPC - transport_api_version: V3 - grpc_services: - - envoy_grpc: - cluster_name: xds_cluster - set_node_on_first_message_only: true - lds_config: - ads: {} - resource_api_version: V3 - cds_config: - ads: {} - resource_api_version: V3 - static_resources: - listeners: - - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + bootstrap: + type: null + value: | + admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null address: socket_address: - address: 0.0.0.0 - port_value: 19001 - protocol: TCP - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager + address: 127.0.0.1 + port_value: 19000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + virtual_hosts: + - name: prometheus_stats + domains: + - "*" + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: prometheus_stats + connect_timeout: 0.250s + type: STATIC + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: prometheus_stats + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: eg-ready-http - route_config: - name: local_route - http_filters: - - name: envoy.filters.http.health_check - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck - pass_through_mode: false - headers: - - name: ":path" - string_match: - exact: /ready - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - connect_timeout: 10s - load_assignment: - cluster_name: xds_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-gateway - port_value: 18000 - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" - explicit_http_config: - http2_protocol_options: {} - name: xds_cluster - type: STRICT_DNS - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - common_tls_context: - tls_params: - tls_maximum_protocol_version: TLSv1_3 - tls_certificate_sds_secret_configs: - - name: xds_certificate - sds_config: - path_config_source: - path: "/sds/xds-certificate.json" - resource_api_version: V3 - validation_context_sds_secret_config: - name: xds_trusted_ca - sds_config: - path_config_source: - path: "/sds/xds-trusted-ca.json" - resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 logging: {} - telemetry: {} status: {} gatewayClass: metadata: @@ -113,7 +133,7 @@ gatewayClass: spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: default-envoy-proxy namespace: envoy-gateway-system @@ -172,6 +192,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io @@ -188,6 +213,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io @@ -204,6 +234,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls-passthrough supportedKinds: - group: gateway.networking.k8s.io @@ -220,6 +255,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -236,6 +276,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: grpc supportedKinds: - group: gateway.networking.k8s.io @@ -449,16 +494,20 @@ xds: ldsConfig: ads: {} resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 staticResources: clusters: + - connectTimeout: 0.250s + loadAssignment: + clusterName: prometheus_stats + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + name: prometheus_stats + type: STATIC - connectTimeout: 10s loadAssignment: clusterName: xds_cluster @@ -469,6 +518,8 @@ xds: socketAddress: address: envoy-gateway portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 name: xds_cluster transportSocket: name: envoy.transport_sockets.tls @@ -494,7 +545,10 @@ xds: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicitHttpConfig: - http2ProtocolOptions: {} + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s listeners: - address: socketAddress: @@ -519,13 +573,22 @@ xds: '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router routeConfig: name: local_route + virtualHosts: + - domains: + - '*' + name: prometheus_stats + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats statPrefix: eg-ready-http name: envoy-gateway-proxy-ready-0.0.0.0-19001 - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump dynamicEndpointConfigs: - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/backend/rule/0/match/0-www.example.com + clusterName: httproute/default/backend/rule/0 endpoints: - lbEndpoints: - endpoint: @@ -535,10 +598,11 @@ xds: portValue: 8000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: httproute/default/backend/rule/0/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/backend/rule/0/match/0-www.grpc-example.com + clusterName: grpcroute/default/backend/rule/0 endpoints: - lbEndpoints: - endpoint: @@ -548,10 +612,11 @@ xds: portValue: 9000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: grpcroute/default/backend/rule/0/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/tls-passthrough/backend + clusterName: tlsroute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -561,10 +626,11 @@ xds: portValue: 3000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tlsroute/default/backend/rule/-1/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/tcp/backend + clusterName: tcproute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -572,11 +638,13 @@ xds: socketAddress: address: 127.0.0.1 portValue: 3000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tcproute/default/backend/rule/-1/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/udp/backend + clusterName: udproute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -584,8 +652,10 @@ xds: socketAddress: address: 127.0.0.1 portValue: 3000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: udproute/default/backend/rule/-1/backend/0 - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump dynamicActiveClusters: - cluster: @@ -598,8 +668,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/backend/rule/0/match/0-www.example.com - name: default/backend/rule/0/match/0-www.example.com + serviceName: httproute/default/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -613,8 +684,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/backend/rule/0/match/0-www.grpc-example.com - name: default/backend/rule/0/match/0-www.grpc-example.com + serviceName: grpcroute/default/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: grpcroute/default/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -633,8 +705,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/tls-passthrough/backend - name: default/eg/tls-passthrough/backend + serviceName: tlsroute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: tlsroute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -648,8 +721,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/tcp/backend - name: default/eg/tcp/backend + serviceName: tcproute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: tcproute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -663,8 +737,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/udp/backend - name: default/eg/udp/backend + serviceName: udproute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: udproute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -684,7 +759,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -702,7 +777,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -742,7 +817,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -760,7 +835,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -806,7 +881,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -827,9 +902,9 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout - cluster: default/eg/tls-passthrough/backend + cluster: tlsroute/default/backend/rule/-1 statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector @@ -851,7 +926,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -869,9 +944,9 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout - cluster: default/eg/tcp/backend + cluster: tcproute/default/backend/rule/-1 statPrefix: tcp name: default/eg/tcp/backend perConnectionBufferLimitBytes: 32768 @@ -889,7 +964,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -907,7 +982,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout matcher: onNoMatch: @@ -915,7 +990,7 @@ xds: name: route typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route - cluster: default/eg/udp/backend + cluster: udproute/default/backend/rule/-1 statPrefix: service name: default/eg/udp/backend - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump @@ -926,33 +1001,25 @@ xds: name: default/eg/http virtualHosts: - domains: - - '*' - name: default/eg/http + - www.example.com + name: default/eg/http/www_example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com prefix: / - name: default/backend/rule/0/match/0-www.example.com + name: httproute/default/backend/rule/0/match/0/www_example_com route: - cluster: default/backend/rule/0/match/0-www.example.com + cluster: httproute/default/backend/rule/0 - routeConfig: '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration ignorePortInHostMatching: true name: default/eg/grpc virtualHosts: - domains: - - '*' - name: default/eg/grpc + - www.grpc-example.com + name: default/eg/grpc/www_grpc-example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.grpc-example.com path: /com.example.Things/DoThing - name: default/backend/rule/0/match/0-www.grpc-example.com + name: grpcroute/default/backend/rule/0/match/0/www_grpc-example_com route: - cluster: default/backend/rule/0/match/0-www.grpc-example.com + cluster: grpcroute/default/backend/rule/0 diff --git a/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.cluster.yaml b/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.cluster.yaml index 6c73701ff49..c61a2fdf774 100644 --- a/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.cluster.yaml +++ b/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.cluster.yaml @@ -37,6 +37,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -95,8 +100,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: envoy-gateway-system/backend/rule/0/match/0-www.example.com - name: envoy-gateway-system/backend/rule/0/match/0-www.example.com + serviceName: httproute/envoy-gateway-system/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway-system/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.route.json b/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.route.json index 909a625b783..41dfd6683e7 100644 --- a/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.route.json +++ b/internal/cmd/egctl/testdata/translate/out/echo-gateway-api.route.json @@ -66,6 +66,13 @@ "lastTransitionTime": null, "reason": "Accepted", "message": "Listener has been successfully translated" + }, + { + "type": "ResolvedRefs", + "status": "True", + "lastTransitionTime": null, + "reason": "ResolvedRefs", + "message": "Listener references have been resolved" } ] } diff --git a/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml b/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml index 3c4158540e4..fb054e518bb 100644 --- a/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml +++ b/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml @@ -27,16 +27,20 @@ xds: ldsConfig: ads: {} resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 staticResources: clusters: + - connectTimeout: 0.250s + loadAssignment: + clusterName: prometheus_stats + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + name: prometheus_stats + type: STATIC - connectTimeout: 10s loadAssignment: clusterName: xds_cluster @@ -47,6 +51,8 @@ xds: socketAddress: address: envoy-gateway portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 name: xds_cluster transportSocket: name: envoy.transport_sockets.tls @@ -72,7 +78,10 @@ xds: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicitHttpConfig: - http2ProtocolOptions: {} + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s listeners: - address: socketAddress: @@ -97,6 +106,15 @@ xds: '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router routeConfig: name: local_route + virtualHosts: + - domains: + - '*' + name: prometheus_stats + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats statPrefix: eg-ready-http name: envoy-gateway-proxy-ready-0.0.0.0-19001 - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump @@ -133,7 +151,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -151,7 +169,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -194,10 +212,3 @@ xds: '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration ignorePortInHostMatching: true name: default/eg/http - virtualHosts: - - domains: - - '*' - name: default/eg/http - rateLimits: - - actions: - - remoteAddress: {} diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.json b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.json index 7cb80a5ab9c..1e461964bbf 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.json +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.json @@ -44,22 +44,32 @@ "resourceApiVersion": "V3" } }, - "layeredRuntime": { - "layers": [ - { - "name": "runtime-0", - "rtdsLayer": { - "name": "runtime-0", - "rtdsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - } - } - } - ] - }, "staticResources": { "clusters": [ + { + "connectTimeout": "0.250s", + "loadAssignment": { + "clusterName": "prometheus_stats", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 19000 + } + } + } + } + ] + } + ] + }, + "name": "prometheus_stats", + "type": "STATIC" + }, { "connectTimeout": "10s", "loadAssignment": { @@ -75,9 +85,11 @@ "portValue": 18000 } } - } + }, + "loadBalancingWeight": 1 } - ] + ], + "loadBalancingWeight": 1 } ] }, @@ -118,7 +130,12 @@ "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", "explicitHttpConfig": { - "http2ProtocolOptions": {} + "http2ProtocolOptions": { + "connectionKeepalive": { + "interval": "30s", + "timeout": "5s" + } + } } } } @@ -163,7 +180,25 @@ } ], "routeConfig": { - "name": "local_route" + "name": "local_route", + "virtualHosts": [ + { + "domains": [ + "*" + ], + "name": "prometheus_stats", + "routes": [ + { + "match": { + "prefix": "/stats/prometheus" + }, + "route": { + "cluster": "prometheus_stats" + } + } + ] + } + ] }, "statPrefix": "eg-ready-http" } @@ -183,7 +218,7 @@ { "endpointConfig": { "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "default/backend/rule/0/match/0-www.example.com", + "clusterName": "httproute/default/backend/rule/0", "endpoints": [ { "lbEndpoints": [ @@ -200,7 +235,9 @@ } ], "loadBalancingWeight": 1, - "locality": {} + "locality": { + "region": "httproute/default/backend/rule/0/backend/0" + } } ] } @@ -208,7 +245,7 @@ { "endpointConfig": { "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "default/backend/rule/0/match/0-www.grpc-example.com", + "clusterName": "grpcroute/default/backend/rule/0", "endpoints": [ { "lbEndpoints": [ @@ -225,7 +262,9 @@ } ], "loadBalancingWeight": 1, - "locality": {} + "locality": { + "region": "grpcroute/default/backend/rule/0/backend/0" + } } ] } @@ -233,7 +272,7 @@ { "endpointConfig": { "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "default/eg/tls-passthrough/backend", + "clusterName": "tlsroute/default/backend/rule/-1", "endpoints": [ { "lbEndpoints": [ @@ -250,7 +289,9 @@ } ], "loadBalancingWeight": 1, - "locality": {} + "locality": { + "region": "tlsroute/default/backend/rule/-1/backend/0" + } } ] } @@ -258,7 +299,7 @@ { "endpointConfig": { "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "default/eg/tcp/backend", + "clusterName": "tcproute/default/backend/rule/-1", "endpoints": [ { "lbEndpoints": [ @@ -270,11 +311,14 @@ "portValue": 3000 } } - } + }, + "loadBalancingWeight": 1 } ], "loadBalancingWeight": 1, - "locality": {} + "locality": { + "region": "tcproute/default/backend/rule/-1/backend/0" + } } ] } @@ -282,7 +326,7 @@ { "endpointConfig": { "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "default/eg/udp/backend", + "clusterName": "udproute/default/backend/rule/-1", "endpoints": [ { "lbEndpoints": [ @@ -294,11 +338,14 @@ "portValue": 3000 } } - } + }, + "loadBalancingWeight": 1 } ], "loadBalancingWeight": 1, - "locality": {} + "locality": { + "region": "udproute/default/backend/rule/-1/backend/0" + } } ] } @@ -321,9 +368,10 @@ "ads": {}, "resourceApiVersion": "V3" }, - "serviceName": "default/backend/rule/0/match/0-www.example.com" + "serviceName": "httproute/default/backend/rule/0" }, - "name": "default/backend/rule/0/match/0-www.example.com", + "lbPolicy": "LEAST_REQUEST", + "name": "httproute/default/backend/rule/0", "outlierDetection": {}, "perConnectionBufferLimitBytes": 32768, "type": "EDS" @@ -342,9 +390,10 @@ "ads": {}, "resourceApiVersion": "V3" }, - "serviceName": "default/backend/rule/0/match/0-www.grpc-example.com" + "serviceName": "grpcroute/default/backend/rule/0" }, - "name": "default/backend/rule/0/match/0-www.grpc-example.com", + "lbPolicy": "LEAST_REQUEST", + "name": "grpcroute/default/backend/rule/0", "outlierDetection": {}, "perConnectionBufferLimitBytes": 32768, "type": "EDS", @@ -371,9 +420,10 @@ "ads": {}, "resourceApiVersion": "V3" }, - "serviceName": "default/eg/tls-passthrough/backend" + "serviceName": "tlsroute/default/backend/rule/-1" }, - "name": "default/eg/tls-passthrough/backend", + "lbPolicy": "LEAST_REQUEST", + "name": "tlsroute/default/backend/rule/-1", "outlierDetection": {}, "perConnectionBufferLimitBytes": 32768, "type": "EDS" @@ -392,9 +442,10 @@ "ads": {}, "resourceApiVersion": "V3" }, - "serviceName": "default/eg/tcp/backend" + "serviceName": "tcproute/default/backend/rule/-1" }, - "name": "default/eg/tcp/backend", + "lbPolicy": "LEAST_REQUEST", + "name": "tcproute/default/backend/rule/-1", "outlierDetection": {}, "perConnectionBufferLimitBytes": 32768, "type": "EDS" @@ -413,9 +464,10 @@ "ads": {}, "resourceApiVersion": "V3" }, - "serviceName": "default/eg/udp/backend" + "serviceName": "udproute/default/backend/rule/-1" }, - "name": "default/eg/udp/backend", + "lbPolicy": "LEAST_REQUEST", + "name": "udproute/default/backend/rule/-1", "outlierDetection": {}, "perConnectionBufferLimitBytes": 32768, "type": "EDS" @@ -444,7 +496,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -470,7 +522,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -537,7 +589,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -563,7 +615,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -639,7 +691,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -671,14 +723,14 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" } } ], - "cluster": "default/eg/tls-passthrough/backend", + "cluster": "tlsroute/default/backend/rule/-1", "statPrefix": "passthrough" } } @@ -716,7 +768,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -743,14 +795,14 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" } } ], - "cluster": "default/eg/tcp/backend", + "cluster": "tcproute/default/backend/rule/-1", "statPrefix": "tcp" } } @@ -780,7 +832,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -806,7 +858,7 @@ "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", "logFormat": { "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" } }, "path": "/dev/stdout" @@ -819,7 +871,7 @@ "name": "route", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route", - "cluster": "default/eg/udp/backend" + "cluster": "udproute/default/backend/rule/-1" } } } @@ -845,25 +897,17 @@ "virtualHosts": [ { "domains": [ - "*" + "www.example.com" ], - "name": "default/eg/http", + "name": "default/eg/http/www_example_com", "routes": [ { "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.example.com" - } - } - ], "prefix": "/" }, - "name": "default/backend/rule/0/match/0-www.example.com", + "name": "httproute/default/backend/rule/0/match/0/www_example_com", "route": { - "cluster": "default/backend/rule/0/match/0-www.example.com" + "cluster": "httproute/default/backend/rule/0" } } ] @@ -879,25 +923,17 @@ "virtualHosts": [ { "domains": [ - "*" + "www.grpc-example.com" ], - "name": "default/eg/grpc", + "name": "default/eg/grpc/www_grpc-example_com", "routes": [ { "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.grpc-example.com" - } - } - ], "path": "/com.example.Things/DoThing" }, - "name": "default/backend/rule/0/match/0-www.grpc-example.com", + "name": "grpcroute/default/backend/rule/0/match/0/www_grpc-example_com", "route": { - "cluster": "default/backend/rule/0/match/0-www.grpc-example.com" + "cluster": "grpcroute/default/backend/rule/0" } } ] diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.yaml b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.yaml index d4d4ed9031c..5c5b27e97f9 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.yaml +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.all.yaml @@ -27,16 +27,20 @@ xds: ldsConfig: ads: {} resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 staticResources: clusters: + - connectTimeout: 0.250s + loadAssignment: + clusterName: prometheus_stats + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + name: prometheus_stats + type: STATIC - connectTimeout: 10s loadAssignment: clusterName: xds_cluster @@ -47,6 +51,8 @@ xds: socketAddress: address: envoy-gateway portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 name: xds_cluster transportSocket: name: envoy.transport_sockets.tls @@ -72,7 +78,10 @@ xds: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicitHttpConfig: - http2ProtocolOptions: {} + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s listeners: - address: socketAddress: @@ -97,13 +106,22 @@ xds: '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router routeConfig: name: local_route + virtualHosts: + - domains: + - '*' + name: prometheus_stats + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats statPrefix: eg-ready-http name: envoy-gateway-proxy-ready-0.0.0.0-19001 - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump dynamicEndpointConfigs: - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/backend/rule/0/match/0-www.example.com + clusterName: httproute/default/backend/rule/0 endpoints: - lbEndpoints: - endpoint: @@ -113,10 +131,11 @@ xds: portValue: 3000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: httproute/default/backend/rule/0/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/backend/rule/0/match/0-www.grpc-example.com + clusterName: grpcroute/default/backend/rule/0 endpoints: - lbEndpoints: - endpoint: @@ -126,10 +145,11 @@ xds: portValue: 9000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: grpcroute/default/backend/rule/0/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/tls-passthrough/backend + clusterName: tlsroute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -139,10 +159,11 @@ xds: portValue: 3000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tlsroute/default/backend/rule/-1/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/tcp/backend + clusterName: tcproute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -150,11 +171,13 @@ xds: socketAddress: address: 1.1.1.1 portValue: 3000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tcproute/default/backend/rule/-1/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/udp/backend + clusterName: udproute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -162,8 +185,10 @@ xds: socketAddress: address: 1.1.1.1 portValue: 3000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: udproute/default/backend/rule/-1/backend/0 - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump dynamicActiveClusters: - cluster: @@ -176,8 +201,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/backend/rule/0/match/0-www.example.com - name: default/backend/rule/0/match/0-www.example.com + serviceName: httproute/default/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -191,8 +217,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/backend/rule/0/match/0-www.grpc-example.com - name: default/backend/rule/0/match/0-www.grpc-example.com + serviceName: grpcroute/default/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: grpcroute/default/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -211,8 +238,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/tls-passthrough/backend - name: default/eg/tls-passthrough/backend + serviceName: tlsroute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: tlsroute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -226,8 +254,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/tcp/backend - name: default/eg/tcp/backend + serviceName: tcproute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: tcproute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -241,8 +270,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/udp/backend - name: default/eg/udp/backend + serviceName: udproute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: udproute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -262,7 +292,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -280,7 +310,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -320,7 +350,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -338,7 +368,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -384,7 +414,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -405,9 +435,9 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout - cluster: default/eg/tls-passthrough/backend + cluster: tlsroute/default/backend/rule/-1 statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector @@ -429,7 +459,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -447,9 +477,9 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout - cluster: default/eg/tcp/backend + cluster: tcproute/default/backend/rule/-1 statPrefix: tcp name: default/eg/tcp/backend perConnectionBufferLimitBytes: 32768 @@ -467,7 +497,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -485,7 +515,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout matcher: onNoMatch: @@ -493,7 +523,7 @@ xds: name: route typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route - cluster: default/eg/udp/backend + cluster: udproute/default/backend/rule/-1 statPrefix: service name: default/eg/udp/backend - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump @@ -504,33 +534,25 @@ xds: name: default/eg/http virtualHosts: - domains: - - '*' - name: default/eg/http + - www.example.com + name: default/eg/http/www_example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com prefix: / - name: default/backend/rule/0/match/0-www.example.com + name: httproute/default/backend/rule/0/match/0/www_example_com route: - cluster: default/backend/rule/0/match/0-www.example.com + cluster: httproute/default/backend/rule/0 - routeConfig: '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration ignorePortInHostMatching: true name: default/eg/grpc virtualHosts: - domains: - - '*' - name: default/eg/grpc + - www.grpc-example.com + name: default/eg/grpc/www_grpc-example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.grpc-example.com path: /com.example.Things/DoThing - name: default/backend/rule/0/match/0-www.grpc-example.com + name: grpcroute/default/backend/rule/0/match/0/www_grpc-example_com route: - cluster: default/backend/rule/0/match/0-www.grpc-example.com + cluster: grpcroute/default/backend/rule/0 diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.bootstrap.yaml b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.bootstrap.yaml index 57e60fca3d6..27b7bc7bb2a 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.bootstrap.yaml +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.bootstrap.yaml @@ -26,16 +26,20 @@ xds: ldsConfig: ads: {} resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 staticResources: clusters: + - connectTimeout: 0.250s + loadAssignment: + clusterName: prometheus_stats + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + name: prometheus_stats + type: STATIC - connectTimeout: 10s loadAssignment: clusterName: xds_cluster @@ -46,6 +50,8 @@ xds: socketAddress: address: envoy-gateway portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 name: xds_cluster transportSocket: name: envoy.transport_sockets.tls @@ -71,7 +77,10 @@ xds: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicitHttpConfig: - http2ProtocolOptions: {} + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s listeners: - address: socketAddress: @@ -96,5 +105,14 @@ xds: '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router routeConfig: name: local_route + virtualHosts: + - domains: + - '*' + name: prometheus_stats + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats statPrefix: eg-ready-http name: envoy-gateway-proxy-ready-0.0.0.0-19001 diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.cluster.yaml b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.cluster.yaml index e5afb13ecb6..57fee13d4ac 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.cluster.yaml +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.cluster.yaml @@ -12,8 +12,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/backend/rule/0/match/0-www.example.com - name: default/backend/rule/0/match/0-www.example.com + serviceName: httproute/default/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -27,8 +28,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/backend/rule/0/match/0-www.grpc-example.com - name: default/backend/rule/0/match/0-www.grpc-example.com + serviceName: grpcroute/default/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: grpcroute/default/backend/rule/0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -47,8 +49,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/tls-passthrough/backend - name: default/eg/tls-passthrough/backend + serviceName: tlsroute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: tlsroute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -62,8 +65,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/tcp/backend - name: default/eg/tcp/backend + serviceName: tcproute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: tcproute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -77,8 +81,9 @@ xds: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: default/eg/udp/backend - name: default/eg/udp/backend + serviceName: udproute/default/backend/rule/-1 + lbPolicy: LEAST_REQUEST + name: udproute/default/backend/rule/-1 outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.endpoint.yaml b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.endpoint.yaml index a0577804bb7..76b49b6fa24 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.endpoint.yaml +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.endpoint.yaml @@ -4,7 +4,7 @@ xds: dynamicEndpointConfigs: - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/backend/rule/0/match/0-www.example.com + clusterName: httproute/default/backend/rule/0 endpoints: - lbEndpoints: - endpoint: @@ -14,10 +14,11 @@ xds: portValue: 3000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: httproute/default/backend/rule/0/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/backend/rule/0/match/0-www.grpc-example.com + clusterName: grpcroute/default/backend/rule/0 endpoints: - lbEndpoints: - endpoint: @@ -27,10 +28,11 @@ xds: portValue: 9000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: grpcroute/default/backend/rule/0/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/tls-passthrough/backend + clusterName: tlsroute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -40,10 +42,11 @@ xds: portValue: 3000 loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tlsroute/default/backend/rule/-1/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/tcp/backend + clusterName: tcproute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -51,11 +54,13 @@ xds: socketAddress: address: 1.1.1.1 portValue: 3000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tcproute/default/backend/rule/-1/backend/0 - endpointConfig: '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: default/eg/udp/backend + clusterName: udproute/default/backend/rule/-1 endpoints: - lbEndpoints: - endpoint: @@ -63,5 +68,7 @@ xds: socketAddress: address: 1.1.1.1 portValue: 3000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: udproute/default/backend/rule/-1/backend/0 diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.listener.yaml b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.listener.yaml index cbbac8a8d49..ae7fce949cf 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.listener.yaml +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.listener.yaml @@ -16,7 +16,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -34,7 +34,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -74,7 +74,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -92,7 +92,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout commonHttpProtocolOptions: headersWithUnderscoresAction: REJECT_REQUEST @@ -138,7 +138,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -159,9 +159,9 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout - cluster: default/eg/tls-passthrough/backend + cluster: tlsroute/default/backend/rule/-1 statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector @@ -183,7 +183,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -201,9 +201,9 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout - cluster: default/eg/tcp/backend + cluster: tcproute/default/backend/rule/-1 statPrefix: tcp name: default/eg/tcp/backend perConnectionBufferLimitBytes: 32768 @@ -221,7 +221,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout address: socketAddress: @@ -239,7 +239,7 @@ xds: logFormat: textFormatSource: inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} path: /dev/stdout matcher: onNoMatch: @@ -247,6 +247,6 @@ xds: name: route typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route - cluster: default/eg/udp/backend + cluster: udproute/default/backend/rule/-1 statPrefix: service name: default/eg/udp/backend diff --git a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.route.yaml b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.route.yaml index 26b90da985a..318efe2352f 100644 --- a/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.route.yaml +++ b/internal/cmd/egctl/testdata/translate/out/from-gateway-api-to-xds.route.yaml @@ -8,33 +8,25 @@ xds: name: default/eg/http virtualHosts: - domains: - - '*' - name: default/eg/http + - www.example.com + name: default/eg/http/www_example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com prefix: / - name: default/backend/rule/0/match/0-www.example.com + name: httproute/default/backend/rule/0/match/0/www_example_com route: - cluster: default/backend/rule/0/match/0-www.example.com + cluster: httproute/default/backend/rule/0 - routeConfig: '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration ignorePortInHostMatching: true name: default/eg/grpc virtualHosts: - domains: - - '*' - name: default/eg/grpc + - www.grpc-example.com + name: default/eg/grpc/www_grpc-example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.grpc-example.com path: /com.example.Things/DoThing - name: default/backend/rule/0/match/0-www.grpc-example.com + name: grpcroute/default/backend/rule/0/match/0/www_grpc-example_com route: - cluster: default/backend/rule/0/match/0-www.grpc-example.com + cluster: grpcroute/default/backend/rule/0 diff --git a/internal/cmd/egctl/testdata/translate/out/invalid-envoyproxy.all.yaml b/internal/cmd/egctl/testdata/translate/out/invalid-envoyproxy.all.yaml index 03bdd29171e..c7ad9cde133 100644 --- a/internal/cmd/egctl/testdata/translate/out/invalid-envoyproxy.all.yaml +++ b/internal/cmd/egctl/testdata/translate/out/invalid-envoyproxy.all.yaml @@ -4,19 +4,20 @@ envoyProxy: name: example namespace: default spec: - bootstrap: |- - admin: - access_log: - - name: envoy.access_loggers.file - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socket_address: - address: 127.0.0.1 - port_value: 19000 + bootstrap: + type: Replace + value: |- + admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 logging: {} - telemetry: {} status: {} gatewayClass: metadata: @@ -26,7 +27,7 @@ gatewayClass: spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: example namespace: default @@ -85,6 +86,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io @@ -101,6 +107,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io @@ -117,6 +128,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls-passthrough supportedKinds: - group: gateway.networking.k8s.io @@ -133,6 +149,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -149,6 +170,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: grpc supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.all.json b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.all.json new file mode 100644 index 00000000000..f3452b71909 --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.all.json @@ -0,0 +1,492 @@ +{ + "xds": { + "envoy-gateway-system/eg": { + "configs": [ + { + "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump", + "bootstrap": { + "admin": { + "accessLog": [ + { + "name": "envoy.access_loggers.file", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/dev/null" + } + } + ], + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 19000 + } + } + }, + "dynamicResources": { + "adsConfig": { + "apiType": "DELTA_GRPC", + "grpcServices": [ + { + "envoyGrpc": { + "clusterName": "xds_cluster" + } + } + ], + "setNodeOnFirstMessageOnly": true, + "transportApiVersion": "V3" + }, + "cdsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + }, + "ldsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "staticResources": { + "clusters": [ + { + "connectTimeout": "0.250s", + "loadAssignment": { + "clusterName": "prometheus_stats", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 19000 + } + } + } + } + ] + } + ] + }, + "name": "prometheus_stats", + "type": "STATIC" + }, + { + "connectTimeout": "10s", + "loadAssignment": { + "clusterName": "xds_cluster", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "envoy-gateway", + "portValue": 18000 + } + } + }, + "loadBalancingWeight": 1 + } + ], + "loadBalancingWeight": 1 + } + ] + }, + "name": "xds_cluster", + "transportSocket": { + "name": "envoy.transport_sockets.tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsCertificateSdsSecretConfigs": [ + { + "name": "xds_certificate", + "sdsConfig": { + "pathConfigSource": { + "path": "/sds/xds-certificate.json" + }, + "resourceApiVersion": "V3" + } + } + ], + "tlsParams": { + "tlsMaximumProtocolVersion": "TLSv1_3" + }, + "validationContextSdsSecretConfig": { + "name": "xds_trusted_ca", + "sdsConfig": { + "pathConfigSource": { + "path": "/sds/xds-trusted-ca.json" + }, + "resourceApiVersion": "V3" + } + } + } + } + }, + "type": "STRICT_DNS", + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "connectionKeepalive": { + "interval": "30s", + "timeout": "5s" + } + } + } + } + } + } + ], + "listeners": [ + { + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 19001 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "httpFilters": [ + { + "name": "envoy.filters.http.health_check", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck", + "headers": [ + { + "name": ":path", + "stringMatch": { + "exact": "/ready" + } + } + ], + "passThroughMode": false + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "routeConfig": { + "name": "local_route", + "virtualHosts": [ + { + "domains": [ + "*" + ], + "name": "prometheus_stats", + "routes": [ + { + "match": { + "prefix": "/stats/prometheus" + }, + "route": { + "cluster": "prometheus_stats" + } + } + ] + } + ] + }, + "statPrefix": "eg-ready-http" + } + } + ] + } + ], + "name": "envoy-gateway-proxy-ready-0.0.0.0-19001" + } + ] + } + } + }, + { + "@type": "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump", + "dynamicEndpointConfigs": [ + { + "endpointConfig": { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "httproute/envoy-gateway-system/backend/rule/0", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "7.7.7.7", + "portValue": 3000 + } + } + }, + "loadBalancingWeight": 1 + } + ], + "loadBalancingWeight": 1, + "locality": { + "region": "httproute/envoy-gateway-system/backend/rule/0/backend/0" + } + } + ] + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump", + "dynamicActiveClusters": [ + { + "cluster": { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "commonLbConfig": { + "localityWeightedLbConfig": {} + }, + "connectTimeout": "10s", + "dnsLookupFamily": "V4_ONLY", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + }, + "serviceName": "httproute/envoy-gateway-system/backend/rule/0" + }, + "lbPolicy": "LEAST_REQUEST", + "name": "httproute/envoy-gateway-system/backend/rule/0", + "outlierDetection": {}, + "perConnectionBufferLimitBytes": 32768, + "type": "EDS" + } + }, + { + "cluster": { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "commonLbConfig": { + "localityWeightedLbConfig": {} + }, + "connectTimeout": "10s", + "dnsLookupFamily": "V4_ONLY", + "dnsRefreshRate": "30s", + "lbPolicy": "LEAST_REQUEST", + "loadAssignment": { + "clusterName": "raw_githubusercontent_com_443", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "raw.githubusercontent.com", + "portValue": 443 + } + } + }, + "loadBalancingWeight": 1 + } + ], + "loadBalancingWeight": 1, + "locality": { + "region": "raw_githubusercontent_com_443/backend/0" + } + } + ] + }, + "name": "raw_githubusercontent_com_443", + "outlierDetection": {}, + "perConnectionBufferLimitBytes": 32768, + "respectDnsTtl": true, + "transportSocket": { + "name": "envoy.transport_sockets.tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "validationContext": { + "trustedCa": { + "filename": "/etc/ssl/certs/ca-certificates.crt" + } + } + } + } + }, + "type": "STRICT_DNS" + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump", + "dynamicListeners": [ + { + "activeState": { + "listener": { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "accessLog": [ + { + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "name": "envoy.access_loggers.file", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "logFormat": { + "textFormatSource": { + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" + } + }, + "path": "/dev/stdout" + } + } + ], + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 10080 + } + }, + "defaultFilterChain": { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "accessLog": [ + { + "name": "envoy.access_loggers.file", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "logFormat": { + "textFormatSource": { + "inlineString": "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\",\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\",\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\",\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\",\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\",\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\",\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\",\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\",\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\",\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\",\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\",\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\",\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\",\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\",\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" + } + }, + "path": "/dev/stdout" + } + } + ], + "commonHttpProtocolOptions": { + "headersWithUnderscoresAction": "REJECT_REQUEST" + }, + "http2ProtocolOptions": { + "initialConnectionWindowSize": 1048576, + "initialStreamWindowSize": 65536, + "maxConcurrentStreams": 100 + }, + "httpFilters": [ + { + "name": "envoy.filters.http.jwt_authn", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication", + "providers": { + "httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com/example": { + "forward": true, + "remoteJwks": { + "asyncFetch": {}, + "cacheDuration": "300s", + "httpUri": { + "cluster": "raw_githubusercontent_com_443", + "timeout": "5s", + "uri": "https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json" + }, + "retryPolicy": {} + } + } + }, + "requirementMap": { + "httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com": { + "providerName": "httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com/example" + } + } + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "mergeSlashes": true, + "normalizePath": true, + "pathWithEscapedSlashesAction": "UNESCAPE_AND_REDIRECT", + "rds": { + "configSource": { + "ads": {}, + "resourceApiVersion": "V3" + }, + "routeConfigName": "envoy-gateway-system/eg/http" + }, + "statPrefix": "http", + "upgradeConfigs": [ + { + "upgradeType": "websocket" + } + ], + "useRemoteAddress": true + } + } + ] + }, + "name": "envoy-gateway-system/eg/http", + "perConnectionBufferLimitBytes": 32768 + } + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump", + "dynamicRouteConfigs": [ + { + "routeConfig": { + "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "ignorePortInHostMatching": true, + "name": "envoy-gateway-system/eg/http", + "virtualHosts": [ + { + "domains": [ + "www.example.com" + ], + "name": "envoy-gateway-system/eg/http/www_example_com", + "routes": [ + { + "match": { + "pathSeparatedPrefix": "/foo" + }, + "name": "httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com", + "route": { + "cluster": "httproute/envoy-gateway-system/backend/rule/0" + }, + "typedPerFilterConfig": { + "envoy.filters.http.jwt_authn": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig", + "requirementName": "httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com" + } + } + } + ] + } + ] + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.all.yaml b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.all.yaml new file mode 100644 index 00000000000..6f26675e23f --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.all.yaml @@ -0,0 +1,284 @@ +xds: + envoy-gateway-system/eg: + configs: + - '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump + bootstrap: + admin: + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + dynamicResources: + adsConfig: + apiType: DELTA_GRPC + grpcServices: + - envoyGrpc: + clusterName: xds_cluster + setNodeOnFirstMessageOnly: true + transportApiVersion: V3 + cdsConfig: + ads: {} + resourceApiVersion: V3 + ldsConfig: + ads: {} + resourceApiVersion: V3 + staticResources: + clusters: + - connectTimeout: 0.250s + loadAssignment: + clusterName: prometheus_stats + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + name: prometheus_stats + type: STATIC + - connectTimeout: 10s + loadAssignment: + clusterName: xds_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-gateway + portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + name: xds_cluster + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificateSdsSecretConfigs: + - name: xds_certificate + sdsConfig: + pathConfigSource: + path: /sds/xds-certificate.json + resourceApiVersion: V3 + tlsParams: + tlsMaximumProtocolVersion: TLSv1_3 + validationContextSdsSecretConfig: + name: xds_trusted_ca + sdsConfig: + pathConfigSource: + path: /sds/xds-trusted-ca.json + resourceApiVersion: V3 + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s + listeners: + - address: + socketAddress: + address: 0.0.0.0 + portValue: 19001 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.health_check + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + headers: + - name: :path + stringMatch: + exact: /ready + passThroughMode: false + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + routeConfig: + name: local_route + virtualHosts: + - domains: + - '*' + name: prometheus_stats + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats + statPrefix: eg-ready-http + name: envoy-gateway-proxy-ready-0.0.0.0-19001 + - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump + dynamicEndpointConfigs: + - endpointConfig: + '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment + clusterName: httproute/envoy-gateway-system/backend/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 3000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway-system/backend/rule/0/backend/0 + - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump + dynamicActiveClusters: + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/envoy-gateway-system/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway-system/backend/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: raw_githubusercontent_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: raw.githubusercontent.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: raw_githubusercontent_com_443/backend/0 + name: raw_githubusercontent_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS + - '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump + dynamicListeners: + - activeState: + listener: + '@type': type.googleapis.com/envoy.config.listener.v3.Listener + accessLog: + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com/example: + forward: true + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: raw_githubusercontent_com_443 + timeout: 5s + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + retryPolicy: {} + requirementMap: + httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com: + providerName: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway-system/eg/http + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: envoy-gateway-system/eg/http + perConnectionBufferLimitBytes: 32768 + - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump + dynamicRouteConfigs: + - routeConfig: + '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration + ignorePortInHostMatching: true + name: envoy-gateway-system/eg/http + virtualHosts: + - domains: + - www.example.com + name: envoy-gateway-system/eg/http/www_example_com + routes: + - match: + pathSeparatedPrefix: /foo + name: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com + route: + cluster: httproute/envoy-gateway-system/backend/rule/0 + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.bootstrap.yaml b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.bootstrap.yaml new file mode 100644 index 00000000000..e92c54f0dbc --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.bootstrap.yaml @@ -0,0 +1,118 @@ +xds: + envoy-gateway-system/eg: + '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump + bootstrap: + admin: + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + dynamicResources: + adsConfig: + apiType: DELTA_GRPC + grpcServices: + - envoyGrpc: + clusterName: xds_cluster + setNodeOnFirstMessageOnly: true + transportApiVersion: V3 + cdsConfig: + ads: {} + resourceApiVersion: V3 + ldsConfig: + ads: {} + resourceApiVersion: V3 + staticResources: + clusters: + - connectTimeout: 0.250s + loadAssignment: + clusterName: prometheus_stats + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + name: prometheus_stats + type: STATIC + - connectTimeout: 10s + loadAssignment: + clusterName: xds_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-gateway + portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + name: xds_cluster + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificateSdsSecretConfigs: + - name: xds_certificate + sdsConfig: + pathConfigSource: + path: /sds/xds-certificate.json + resourceApiVersion: V3 + tlsParams: + tlsMaximumProtocolVersion: TLSv1_3 + validationContextSdsSecretConfig: + name: xds_trusted_ca + sdsConfig: + pathConfigSource: + path: /sds/xds-trusted-ca.json + resourceApiVersion: V3 + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s + listeners: + - address: + socketAddress: + address: 0.0.0.0 + portValue: 19001 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.health_check + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + headers: + - name: :path + stringMatch: + exact: /ready + passThroughMode: false + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + routeConfig: + name: local_route + virtualHosts: + - domains: + - '*' + name: prometheus_stats + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats + statPrefix: eg-ready-http + name: envoy-gateway-proxy-ready-0.0.0.0-19001 diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.cluster.yaml b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.cluster.yaml new file mode 100644 index 00000000000..47f144cbe20 --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.cluster.yaml @@ -0,0 +1,54 @@ +xds: + envoy-gateway-system/eg: + '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump + dynamicActiveClusters: + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/envoy-gateway-system/backend/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway-system/backend/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: raw_githubusercontent_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: raw.githubusercontent.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: raw_githubusercontent_com_443/backend/0 + name: raw_githubusercontent_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.endpoint.yaml b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.endpoint.yaml new file mode 100644 index 00000000000..d83fc305df4 --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.endpoint.yaml @@ -0,0 +1,17 @@ +xds: + envoy-gateway-system-eg: + '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump + dynamicEndpointConfigs: + - endpointConfig: + '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment + clusterName: envoy-gateway-system-backend-rule-0-match-0-www.example.com + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: "7.7.7.7" + portValue: 3000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.listener.yaml b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.listener.yaml new file mode 100644 index 00000000000..7c8a353b83c --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.listener.yaml @@ -0,0 +1,79 @@ +xds: + envoy-gateway-system/eg: + '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump + dynamicListeners: + - activeState: + listener: + '@type': type.googleapis.com/envoy.config.listener.v3.Listener + accessLog: + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com/example: + forward: true + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: raw_githubusercontent_com_443 + timeout: 5s + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + retryPolicy: {} + requirementMap: + httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com: + providerName: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway-system/eg/http + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: envoy-gateway-system/eg/http + perConnectionBufferLimitBytes: 32768 diff --git a/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.route.yaml b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.route.yaml new file mode 100644 index 00000000000..476fb3e582e --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/jwt-single-route-single-match-to-xds.route.yaml @@ -0,0 +1,22 @@ +xds: + envoy-gateway-system/eg: + '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump + dynamicRouteConfigs: + - routeConfig: + '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration + ignorePortInHostMatching: true + name: envoy-gateway-system/eg/http + virtualHosts: + - domains: + - www.example.com + name: envoy-gateway-system/eg/http/www_example_com + routes: + - match: + pathSeparatedPrefix: /foo + name: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com + route: + cluster: httproute/envoy-gateway-system/backend/rule/0 + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com diff --git a/internal/cmd/egctl/testdata/translate/out/multiple-xds.route.json b/internal/cmd/egctl/testdata/translate/out/multiple-xds.route.json index 5c173523db2..c2d43139edd 100644 --- a/internal/cmd/egctl/testdata/translate/out/multiple-xds.route.json +++ b/internal/cmd/egctl/testdata/translate/out/multiple-xds.route.json @@ -11,25 +11,17 @@ "virtualHosts": [ { "domains": [ - "*" + "www.example.com" ], - "name": "default/eg/http", + "name": "default/eg/http/www_example_com", "routes": [ { "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.example.com" - } - } - ], "prefix": "/" }, - "name": "default/backend/rule/0/match/0-www.example.com", + "name": "httproute/default/backend/rule/0/match/0/www_example_com", "route": { - "cluster": "default/backend/rule/0/match/0-www.example.com" + "cluster": "httproute/default/backend/rule/0" } } ] @@ -50,25 +42,17 @@ "virtualHosts": [ { "domains": [ - "*" + "www.example2.com" ], - "name": "default/eg2/http", + "name": "default/eg2/http/www_example2_com", "routes": [ { "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "www.example2.com" - } - } - ], - "prefix": "/v2/" + "pathSeparatedPrefix": "/v2" }, - "name": "default/backend/rule/0/match/0-www.example2.com", + "name": "httproute/default/backend/rule/0/match/0/www_example2_com", "route": { - "cluster": "default/backend/rule/0/match/0-www.example2.com" + "cluster": "httproute/default/backend/rule/0" } } ] diff --git a/internal/cmd/egctl/testdata/translate/out/quickstart.route.yaml b/internal/cmd/egctl/testdata/translate/out/quickstart.route.yaml index fca71e35033..12104d66b08 100644 --- a/internal/cmd/egctl/testdata/translate/out/quickstart.route.yaml +++ b/internal/cmd/egctl/testdata/translate/out/quickstart.route.yaml @@ -8,15 +8,11 @@ xds: name: envoy-gateway-system/eg/http virtualHosts: - domains: - - '*' - name: envoy-gateway-system/eg/http + - www.example.com + name: envoy-gateway-system/eg/http/www_example_com routes: - match: - headers: - - name: :authority - stringMatch: - exact: www.example.com prefix: / - name: envoy-gateway-system/backend/rule/0/match/0-www.example.com + name: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com route: - cluster: envoy-gateway-system/backend/rule/0/match/0-www.example.com + cluster: httproute/envoy-gateway-system/backend/rule/0 diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.all.json b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.all.json deleted file mode 100644 index 81e300b12f0..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.all.json +++ /dev/null @@ -1,480 +0,0 @@ -{ - "xds": { - "envoy-gateway-system/eg": { - "configs": [ - { - "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump", - "bootstrap": { - "admin": { - "accessLog": [ - { - "name": "envoy.access_loggers.file", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", - "path": "/dev/null" - } - } - ], - "address": { - "socketAddress": { - "address": "127.0.0.1", - "portValue": 19000 - } - } - }, - "dynamicResources": { - "adsConfig": { - "apiType": "DELTA_GRPC", - "grpcServices": [ - { - "envoyGrpc": { - "clusterName": "xds_cluster" - } - } - ], - "setNodeOnFirstMessageOnly": true, - "transportApiVersion": "V3" - }, - "cdsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "ldsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - } - }, - "layeredRuntime": { - "layers": [ - { - "name": "runtime-0", - "rtdsLayer": { - "name": "runtime-0", - "rtdsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - } - } - } - ] - }, - "staticResources": { - "clusters": [ - { - "connectTimeout": "10s", - "loadAssignment": { - "clusterName": "xds_cluster", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "address": "envoy-gateway", - "portValue": 18000 - } - } - } - } - ] - } - ] - }, - "name": "xds_cluster", - "transportSocket": { - "name": "envoy.transport_sockets.tls", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", - "commonTlsContext": { - "tlsCertificateSdsSecretConfigs": [ - { - "name": "xds_certificate", - "sdsConfig": { - "pathConfigSource": { - "path": "/sds/xds-certificate.json" - }, - "resourceApiVersion": "V3" - } - } - ], - "tlsParams": { - "tlsMaximumProtocolVersion": "TLSv1_3" - }, - "validationContextSdsSecretConfig": { - "name": "xds_trusted_ca", - "sdsConfig": { - "pathConfigSource": { - "path": "/sds/xds-trusted-ca.json" - }, - "resourceApiVersion": "V3" - } - } - } - } - }, - "type": "STRICT_DNS", - "typedExtensionProtocolOptions": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicitHttpConfig": { - "http2ProtocolOptions": {} - } - } - } - } - ], - "listeners": [ - { - "address": { - "socketAddress": { - "address": "0.0.0.0", - "portValue": 19001 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "httpFilters": [ - { - "name": "envoy.filters.http.health_check", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck", - "headers": [ - { - "name": ":path", - "stringMatch": { - "exact": "/ready" - } - } - ], - "passThroughMode": false - } - }, - { - "name": "envoy.filters.http.router", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - } - ], - "routeConfig": { - "name": "local_route" - }, - "statPrefix": "eg-ready-http" - } - } - ] - } - ], - "name": "envoy-gateway-proxy-ready-0.0.0.0-19001" - } - ] - } - } - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump", - "dynamicEndpointConfigs": [ - { - "endpointConfig": { - "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "portValue": 3000 - } - } - }, - "loadBalancingWeight": 1 - } - ], - "loadBalancingWeight": 1, - "locality": {} - } - ] - } - } - ] - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump", - "dynamicActiveClusters": [ - { - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "commonLbConfig": { - "localityWeightedLbConfig": {} - }, - "connectTimeout": "10s", - "dnsLookupFamily": "V4_ONLY", - "edsClusterConfig": { - "edsConfig": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "serviceName": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example" - }, - "name": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example", - "outlierDetection": {}, - "perConnectionBufferLimitBytes": 32768, - "type": "EDS" - } - }, - { - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "commonLbConfig": { - "localityWeightedLbConfig": {} - }, - "connectTimeout": "10s", - "dnsLookupFamily": "V4_ONLY", - "dnsRefreshRate": "30s", - "loadAssignment": { - "clusterName": "ratelimit_cluster", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "address": "envoy-ratelimit.envoy-gateway.svc.cluster.local", - "portValue": 8081 - } - } - } - } - ], - "loadBalancingWeight": 1, - "locality": {} - } - ] - }, - "name": "ratelimit_cluster", - "outlierDetection": {}, - "perConnectionBufferLimitBytes": 32768, - "respectDnsTtl": true, - "transportSocket": { - "name": "envoy.transport_sockets.tls", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", - "commonTlsContext": { - "tlsCertificates": [ - { - "certificateChain": { - "filename": "/certs/tls.crt" - }, - "privateKey": { - "filename": "/certs/tls.key" - } - } - ], - "validationContext": { - "trustedCa": { - "filename": "/certs/ca.crt" - } - } - } - } - }, - "type": "STRICT_DNS", - "typedExtensionProtocolOptions": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicitHttpConfig": { - "http2ProtocolOptions": {} - } - } - } - } - } - ] - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump", - "dynamicListeners": [ - { - "activeState": { - "listener": { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "accessLog": [ - { - "filter": { - "responseFlagFilter": { - "flags": [ - "NR" - ] - } - }, - "name": "envoy.access_loggers.file", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", - "logFormat": { - "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" - } - }, - "path": "/dev/stdout" - } - } - ], - "address": { - "socketAddress": { - "address": "0.0.0.0", - "portValue": 10080 - } - }, - "defaultFilterChain": { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "accessLog": [ - { - "name": "envoy.access_loggers.file", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", - "logFormat": { - "textFormatSource": { - "inlineString": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" - } - }, - "path": "/dev/stdout" - } - } - ], - "commonHttpProtocolOptions": { - "headersWithUnderscoresAction": "REJECT_REQUEST" - }, - "http2ProtocolOptions": { - "initialConnectionWindowSize": 1048576, - "initialStreamWindowSize": 65536, - "maxConcurrentStreams": 100 - }, - "httpFilters": [ - { - "name": "envoy.filters.http.ratelimit", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit", - "domain": "envoy-gateway-system/eg/http", - "enableXRatelimitHeaders": "DRAFT_VERSION_03", - "rateLimitService": { - "grpcService": { - "envoyGrpc": { - "clusterName": "ratelimit_cluster" - } - }, - "transportApiVersion": "V3" - } - } - }, - { - "name": "envoy.filters.http.router", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - } - ], - "mergeSlashes": true, - "normalizePath": true, - "pathWithEscapedSlashesAction": "UNESCAPE_AND_REDIRECT", - "rds": { - "configSource": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "routeConfigName": "envoy-gateway-system/eg/http" - }, - "statPrefix": "http", - "upgradeConfigs": [ - { - "upgradeType": "websocket" - } - ], - "useRemoteAddress": true - } - } - ] - }, - "name": "envoy-gateway-system/eg/http", - "perConnectionBufferLimitBytes": 32768 - } - } - } - ] - }, - { - "@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump", - "dynamicRouteConfigs": [ - { - "routeConfig": { - "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "ignorePortInHostMatching": true, - "name": "envoy-gateway-system/eg/http", - "virtualHosts": [ - { - "domains": [ - "*" - ], - "name": "envoy-gateway-system/eg/http", - "routes": [ - { - "match": { - "headers": [ - { - "name": ":authority", - "stringMatch": { - "exact": "ratelimit.example" - } - } - ], - "prefix": "/" - }, - "name": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example", - "route": { - "cluster": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example", - "rateLimits": [ - { - "actions": [ - { - "headerValueMatch": { - "descriptorKey": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example-key-rule-0-match-0", - "descriptorValue": "envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example-value-rule-0-match-0", - "expectMatch": true, - "headers": [ - { - "name": "x-user-id", - "stringMatch": { - "exact": "one" - } - } - ] - } - } - ] - } - ] - } - } - ] - } - ] - } - } - ] - } - ] - } - } -} \ No newline at end of file diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.all.yaml b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.all.yaml deleted file mode 100644 index 6eb4954ec73..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.all.yaml +++ /dev/null @@ -1,273 +0,0 @@ -xds: - envoy-gateway-system/eg: - configs: - - '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump - bootstrap: - admin: - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socketAddress: - address: 127.0.0.1 - portValue: 19000 - dynamicResources: - adsConfig: - apiType: DELTA_GRPC - grpcServices: - - envoyGrpc: - clusterName: xds_cluster - setNodeOnFirstMessageOnly: true - transportApiVersion: V3 - cdsConfig: - ads: {} - resourceApiVersion: V3 - ldsConfig: - ads: {} - resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 - staticResources: - clusters: - - connectTimeout: 10s - loadAssignment: - clusterName: xds_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-gateway - portValue: 18000 - name: xds_cluster - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificateSdsSecretConfigs: - - name: xds_certificate - sdsConfig: - pathConfigSource: - path: /sds/xds-certificate.json - resourceApiVersion: V3 - tlsParams: - tlsMaximumProtocolVersion: TLSv1_3 - validationContextSdsSecretConfig: - name: xds_trusted_ca - sdsConfig: - pathConfigSource: - path: /sds/xds-trusted-ca.json - resourceApiVersion: V3 - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} - listeners: - - address: - socketAddress: - address: 0.0.0.0 - portValue: 19001 - filterChains: - - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - httpFilters: - - name: envoy.filters.http.health_check - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck - headers: - - name: :path - stringMatch: - exact: /ready - passThroughMode: false - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - routeConfig: - name: local_route - statPrefix: eg-ready-http - name: envoy-gateway-proxy-ready-0.0.0.0-19001 - - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump - dynamicEndpointConfigs: - - endpointConfig: - '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - portValue: 3000 - loadBalancingWeight: 1 - loadBalancingWeight: 1 - locality: {} - - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump - dynamicActiveClusters: - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - name: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: ratelimit_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-ratelimit.envoy-gateway.svc.cluster.local - portValue: 8081 - loadBalancingWeight: 1 - locality: {} - name: ratelimit_cluster - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificates: - - certificateChain: - filename: /certs/tls.crt - privateKey: - filename: /certs/tls.key - validationContext: - trustedCa: - filename: /certs/ca.crt - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} - - '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump - dynamicListeners: - - activeState: - listener: - '@type': type.googleapis.com/envoy.config.listener.v3.Listener - accessLog: - - filter: - responseFlagFilter: - flags: - - NR - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.ratelimit - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit - domain: envoy-gateway-system/eg/http - enableXRatelimitHeaders: DRAFT_VERSION_03 - rateLimitService: - grpcService: - envoyGrpc: - clusterName: ratelimit_cluster - transportApiVersion: V3 - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: envoy-gateway-system/eg/http - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: envoy-gateway-system/eg/http - perConnectionBufferLimitBytes: 32768 - - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump - dynamicRouteConfigs: - - routeConfig: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - ignorePortInHostMatching: true - name: envoy-gateway-system/eg/http - virtualHosts: - - domains: - - '*' - name: envoy-gateway-system/eg/http - routes: - - match: - headers: - - name: :authority - stringMatch: - exact: ratelimit.example - prefix: / - name: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - route: - cluster: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - rateLimits: - - actions: - - headerValueMatch: - descriptorKey: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example-key-rule-0-match-0 - descriptorValue: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example-value-rule-0-match-0 - expectMatch: true - headers: - - name: x-user-id - stringMatch: - exact: one diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.bootstrap.yaml b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.bootstrap.yaml deleted file mode 100644 index fb207fdf33c..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.bootstrap.yaml +++ /dev/null @@ -1,100 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump - bootstrap: - admin: - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socketAddress: - address: 127.0.0.1 - portValue: 19000 - dynamicResources: - adsConfig: - apiType: DELTA_GRPC - grpcServices: - - envoyGrpc: - clusterName: xds_cluster - setNodeOnFirstMessageOnly: true - transportApiVersion: V3 - cdsConfig: - ads: {} - resourceApiVersion: V3 - ldsConfig: - ads: {} - resourceApiVersion: V3 - layeredRuntime: - layers: - - name: runtime-0 - rtdsLayer: - name: runtime-0 - rtdsConfig: - ads: {} - resourceApiVersion: V3 - staticResources: - clusters: - - connectTimeout: 10s - loadAssignment: - clusterName: xds_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-gateway - portValue: 18000 - name: xds_cluster - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificateSdsSecretConfigs: - - name: xds_certificate - sdsConfig: - pathConfigSource: - path: /sds/xds-certificate.json - resourceApiVersion: V3 - tlsParams: - tlsMaximumProtocolVersion: TLSv1_3 - validationContextSdsSecretConfig: - name: xds_trusted_ca - sdsConfig: - pathConfigSource: - path: /sds/xds-trusted-ca.json - resourceApiVersion: V3 - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} - listeners: - - address: - socketAddress: - address: 0.0.0.0 - portValue: 19001 - filterChains: - - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - httpFilters: - - name: envoy.filters.http.health_check - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck - headers: - - name: :path - stringMatch: - exact: /ready - passThroughMode: false - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - routeConfig: - name: local_route - statPrefix: eg-ready-http - name: envoy-gateway-proxy-ready-0.0.0.0-19001 diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.cluster.yaml b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.cluster.yaml deleted file mode 100644 index 90ac09a93a3..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.cluster.yaml +++ /dev/null @@ -1,60 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump - dynamicActiveClusters: - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - name: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS - - cluster: - '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster - commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: ratelimit_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-ratelimit.envoy-gateway.svc.cluster.local - portValue: 8081 - loadBalancingWeight: 1 - locality: {} - name: ratelimit_cluster - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificates: - - certificateChain: - filename: /certs/tls.crt - privateKey: - filename: /certs/tls.key - validationContext: - trustedCa: - filename: /certs/ca.crt - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.endpoint.yaml b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.endpoint.yaml deleted file mode 100644 index de63801b22b..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.endpoint.yaml +++ /dev/null @@ -1,16 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump - dynamicEndpointConfigs: - - endpointConfig: - '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment - clusterName: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - portValue: 3000 - loadBalancingWeight: 1 - loadBalancingWeight: 1 - locality: {} diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.listener.yaml b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.listener.yaml deleted file mode 100644 index cfb25f80221..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.listener.yaml +++ /dev/null @@ -1,72 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump - dynamicListeners: - - activeState: - listener: - '@type': type.googleapis.com/envoy.config.listener.v3.Listener - accessLog: - - filter: - responseFlagFilter: - flags: - - NR - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.ratelimit - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit - domain: envoy-gateway-system/eg/http - enableXRatelimitHeaders: DRAFT_VERSION_03 - rateLimitService: - grpcService: - envoyGrpc: - clusterName: ratelimit_cluster - transportApiVersion: V3 - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: envoy-gateway-system/eg/http - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: envoy-gateway-system/eg/http - perConnectionBufferLimitBytes: 32768 diff --git a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.route.yaml b/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.route.yaml deleted file mode 100644 index 400a255406d..00000000000 --- a/internal/cmd/egctl/testdata/translate/out/rate-limit-filter-single-route-single-match-to-xds.route.yaml +++ /dev/null @@ -1,32 +0,0 @@ -xds: - envoy-gateway-system/eg: - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump - dynamicRouteConfigs: - - routeConfig: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - ignorePortInHostMatching: true - name: envoy-gateway-system/eg/http - virtualHosts: - - domains: - - '*' - name: envoy-gateway-system/eg/http - routes: - - match: - headers: - - name: :authority - stringMatch: - exact: ratelimit.example - prefix: / - name: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - route: - cluster: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example - rateLimits: - - actions: - - headerValueMatch: - descriptorKey: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example-key-rule-0-match-0 - descriptorValue: envoy-gateway-system/http-ratelimit/rule/0/match/0-ratelimit.example-value-rule-0-match-0 - expectMatch: true - headers: - - name: x-user-id - stringMatch: - exact: one diff --git a/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml b/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml index ebcd5ab0b10..c578d14aef5 100644 --- a/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml +++ b/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml @@ -32,6 +32,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -62,8 +67,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/cmd/egctl/testdata/translate/out/valid-envoyproxy.all.yaml b/internal/cmd/egctl/testdata/translate/out/valid-envoyproxy.all.yaml index f40e25a404c..ef42d68c93e 100644 --- a/internal/cmd/egctl/testdata/translate/out/valid-envoyproxy.all.yaml +++ b/internal/cmd/egctl/testdata/translate/out/valid-envoyproxy.all.yaml @@ -11,7 +11,6 @@ envoyProxy: annotations: custom1: svc-annotation1 type: Kubernetes - telemetry: {} status: {} gatewayClass: metadata: @@ -21,7 +20,7 @@ gatewayClass: spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: example namespace: default @@ -80,6 +79,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io @@ -96,6 +100,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io @@ -112,6 +121,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls-passthrough supportedKinds: - group: gateway.networking.k8s.io @@ -128,6 +142,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -144,6 +163,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: grpc supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/cmd/egctl/translate.go b/internal/cmd/egctl/translate.go index ed7e4177006..47ab944f424 100644 --- a/internal/cmd/egctl/translate.go +++ b/internal/cmd/egctl/translate.go @@ -27,12 +27,11 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - egv1alpha1 "github.com/envoyproxy/gateway/api/config/v1alpha1" - "github.com/envoyproxy/gateway/api/config/v1alpha1/validation" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1/validation" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -262,9 +261,10 @@ func translateGatewayAPIToGatewayAPI(resources *gatewayapi.Resources) (gatewayap // Translate from Gateway API to Xds IR gTranslator := &gatewayapi.Translator{ - GatewayControllerName: egv1alpha1.GatewayControllerName, - GatewayClassName: v1beta1.ObjectName(resources.GatewayClass.Name), - GlobalRateLimitEnabled: true, + GatewayControllerName: egv1a1.GatewayControllerName, + GatewayClassName: gwapiv1.ObjectName(resources.GatewayClass.Name), + GlobalRateLimitEnabled: true, + EndpointRoutingDisabled: true, } gRes := gTranslator.Translate(resources) // Update the status of the GatewayClass based on EnvoyProxy validation @@ -273,12 +273,12 @@ func translateGatewayAPIToGatewayAPI(resources *gatewayapi.Resources) (gatewayap if err := validation.ValidateEnvoyProxy(resources.EnvoyProxy); err != nil { epInvalid = true msg := fmt.Sprintf("%s: %v", status.MsgGatewayClassInvalidParams, err) - status.SetGatewayClassAccepted(resources.GatewayClass, false, string(v1beta1.GatewayClassReasonInvalidParameters), msg) + status.SetGatewayClassAccepted(resources.GatewayClass, false, string(gwapiv1.GatewayClassReasonInvalidParameters), msg) } gRes.EnvoyProxy = resources.EnvoyProxy } if !epInvalid { - status.SetGatewayClassAccepted(resources.GatewayClass, true, string(v1beta1.GatewayClassReasonAccepted), status.MsgValidGatewayClass) + status.SetGatewayClassAccepted(resources.GatewayClass, true, string(gwapiv1.GatewayClassReasonAccepted), status.MsgValidGatewayClass) } gRes.GatewayClass = resources.GatewayClass @@ -292,9 +292,10 @@ func translateGatewayAPIToXds(dnsDomain string, resourceType string, resources * // Translate from Gateway API to Xds IR gTranslator := &gatewayapi.Translator{ - GatewayControllerName: egv1alpha1.GatewayControllerName, - GatewayClassName: v1beta1.ObjectName(resources.GatewayClass.Name), - GlobalRateLimitEnabled: true, + GatewayControllerName: egv1a1.GatewayControllerName, + GatewayClassName: gwapiv1.ObjectName(resources.GatewayClass.Name), + GlobalRateLimitEnabled: true, + EndpointRoutingDisabled: true, } gRes := gTranslator.Translate(resources) @@ -385,18 +386,22 @@ func constructConfigDump(resources *gatewayapi.Resources, tCtx *xds_types.Resour endpointConfigs := &adminv3.EndpointsConfigDump{} // construct bootstrap config + var bootstrapConfigurations string + var err error + if bootstrapConfigurations, err = bootstrap.GetRenderedBootstrapConfig(nil); err != nil { + return nil, err + } - var bootstrapYAML string + // Apply Bootstrap from EnvoyProxy API if set by the user + // The config should have been validated already if resources.EnvoyProxy != nil && resources.EnvoyProxy.Spec.Bootstrap != nil { - bootstrapYAML = *resources.EnvoyProxy.Spec.Bootstrap - } else { - var err error - if bootstrapYAML, err = bootstrap.GetRenderedBootstrapConfig(nil); err != nil { + bootstrapConfigurations, err = bootstrap.ApplyBootstrapConfig(resources.EnvoyProxy.Spec.Bootstrap, bootstrapConfigurations) + if err != nil { return nil, err } } - jsonData, err := yaml.YAMLToJSON([]byte(bootstrapYAML)) + jsonData, err := yaml.YAMLToJSON([]byte(bootstrapConfigurations)) if err != nil { return nil, err } @@ -493,33 +498,33 @@ func addMissingServices(requiredServices map[string]*v1.Service, obj interface{} var objNamespace string protocol := v1.Protocol(gatewayapi.TCPProtocol) - refs := []v1beta1.BackendRef{} + refs := []gwapiv1.BackendRef{} switch route := obj.(type) { - case *v1beta1.HTTPRoute: + case *gwapiv1.HTTPRoute: objNamespace = route.Namespace for _, rule := range route.Spec.Rules { for _, httpBakcendRef := range rule.BackendRefs { refs = append(refs, httpBakcendRef.BackendRef) } } - case *v1alpha2.GRPCRoute: + case *gwapiv1a2.GRPCRoute: objNamespace = route.Namespace for _, rule := range route.Spec.Rules { for _, gRPCBakcendRef := range rule.BackendRefs { refs = append(refs, gRPCBakcendRef.BackendRef) } } - case *v1alpha2.TLSRoute: + case *gwapiv1a2.TLSRoute: objNamespace = route.Namespace for _, rule := range route.Spec.Rules { refs = append(refs, rule.BackendRefs...) } - case *v1alpha2.TCPRoute: + case *gwapiv1a2.TCPRoute: objNamespace = route.Namespace for _, rule := range route.Spec.Rules { refs = append(refs, rule.BackendRefs...) } - case *v1alpha2.UDPRoute: + case *gwapiv1a2.UDPRoute: protocol = v1.Protocol(gatewayapi.UDPProtocol) objNamespace = route.Namespace for _, rule := range route.Spec.Rules { @@ -622,37 +627,37 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap switch gvk.Kind { case gatewayapi.KindEnvoyProxy: typedSpec := spec.Interface() - envoyProxy := &egv1alpha1.EnvoyProxy{ + envoyProxy := &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: typedSpec.(egv1alpha1.EnvoyProxySpec), + Spec: typedSpec.(egv1a1.EnvoyProxySpec), } resources.EnvoyProxy = envoyProxy case gatewayapi.KindGatewayClass: typedSpec := spec.Interface() - gatewayClass := &v1beta1.GatewayClass{ + gatewayClass := &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1beta1.GatewayClassSpec), + Spec: typedSpec.(gwapiv1.GatewayClassSpec), } resources.GatewayClass = gatewayClass case gatewayapi.KindGateway: typedSpec := spec.Interface() - gateway := &v1beta1.Gateway{ + gateway := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1beta1.GatewaySpec), + Spec: typedSpec.(gwapiv1.GatewaySpec), } resources.Gateways = append(resources.Gateways, gateway) case gatewayapi.KindTCPRoute: typedSpec := spec.Interface() - tcpRoute := &v1alpha2.TCPRoute{ + tcpRoute := &gwapiv1a2.TCPRoute{ TypeMeta: metav1.TypeMeta{ Kind: gatewayapi.KindTCPRoute, }, @@ -660,12 +665,12 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1alpha2.TCPRouteSpec), + Spec: typedSpec.(gwapiv1a2.TCPRouteSpec), } resources.TCPRoutes = append(resources.TCPRoutes, tcpRoute) case gatewayapi.KindUDPRoute: typedSpec := spec.Interface() - udpRoute := &v1alpha2.UDPRoute{ + udpRoute := &gwapiv1a2.UDPRoute{ TypeMeta: metav1.TypeMeta{ Kind: gatewayapi.KindUDPRoute, }, @@ -673,12 +678,12 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1alpha2.UDPRouteSpec), + Spec: typedSpec.(gwapiv1a2.UDPRouteSpec), } resources.UDPRoutes = append(resources.UDPRoutes, udpRoute) case gatewayapi.KindTLSRoute: typedSpec := spec.Interface() - tlsRoute := &v1alpha2.TLSRoute{ + tlsRoute := &gwapiv1a2.TLSRoute{ TypeMeta: metav1.TypeMeta{ Kind: gatewayapi.KindTLSRoute, }, @@ -686,12 +691,12 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1alpha2.TLSRouteSpec), + Spec: typedSpec.(gwapiv1a2.TLSRouteSpec), } resources.TLSRoutes = append(resources.TLSRoutes, tlsRoute) case gatewayapi.KindHTTPRoute: typedSpec := spec.Interface() - httpRoute := &v1beta1.HTTPRoute{ + httpRoute := &gwapiv1.HTTPRoute{ TypeMeta: metav1.TypeMeta{ Kind: gatewayapi.KindHTTPRoute, }, @@ -699,12 +704,12 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1beta1.HTTPRouteSpec), + Spec: typedSpec.(gwapiv1.HTTPRouteSpec), } resources.HTTPRoutes = append(resources.HTTPRoutes, httpRoute) case gatewayapi.KindGRPCRoute: typedSpec := spec.Interface() - grpcRoute := &v1alpha2.GRPCRoute{ + grpcRoute := &gwapiv1a2.GRPCRoute{ TypeMeta: metav1.TypeMeta{ Kind: gatewayapi.KindGRPCRoute, }, @@ -712,7 +717,7 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Name: name, Namespace: namespace, }, - Spec: typedSpec.(v1alpha2.GRPCRouteSpec), + Spec: typedSpec.(gwapiv1a2.GRPCRouteSpec), } resources.GRPCRoutes = append(resources.GRPCRoutes, grpcRoute) case gatewayapi.KindNamespace: @@ -733,48 +738,62 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Spec: typedSpec.(v1.ServiceSpec), } resources.Services = append(resources.Services, service) - case egv1a1.KindAuthenticationFilter: + case egv1a1.KindEnvoyPatchPolicy: typedSpec := spec.Interface() - authenticationFilter := &egv1a1.AuthenticationFilter{ + envoyPatchPolicy := &egv1a1.EnvoyPatchPolicy{ TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, + Kind: egv1a1.KindEnvoyPatchPolicy, APIVersion: egv1a1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, }, - Spec: typedSpec.(egv1a1.AuthenticationFilterSpec), + Spec: typedSpec.(egv1a1.EnvoyPatchPolicySpec), } - resources.AuthenticationFilters = append(resources.AuthenticationFilters, authenticationFilter) - case egv1a1.KindEnvoyPatchPolicy: + resources.EnvoyPatchPolicies = append(resources.EnvoyPatchPolicies, envoyPatchPolicy) + case egv1a1.KindClientTrafficPolicy: typedSpec := spec.Interface() - envoyPatchPolicy := &egv1a1.EnvoyPatchPolicy{ + clientTrafficPolicy := &egv1a1.ClientTrafficPolicy{ TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindEnvoyPatchPolicy, + Kind: egv1a1.KindClientTrafficPolicy, APIVersion: egv1a1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, }, - Spec: typedSpec.(egv1a1.EnvoyPatchPolicySpec), + Spec: typedSpec.(egv1a1.ClientTrafficPolicySpec), } - resources.EnvoyPatchPolicies = append(resources.EnvoyPatchPolicies, envoyPatchPolicy) - case egv1a1.KindRateLimitFilter: + resources.ClientTrafficPolicies = append(resources.ClientTrafficPolicies, clientTrafficPolicy) + case egv1a1.KindBackendTrafficPolicy: typedSpec := spec.Interface() - rateLimitFilter := &egv1a1.RateLimitFilter{ + backendTrafficPolicy := &egv1a1.BackendTrafficPolicy{ TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindRateLimitFilter, + Kind: egv1a1.KindBackendTrafficPolicy, APIVersion: egv1a1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, }, - Spec: typedSpec.(egv1a1.RateLimitFilterSpec), + Spec: typedSpec.(egv1a1.BackendTrafficPolicySpec), } - resources.RateLimitFilters = append(resources.RateLimitFilters, rateLimitFilter) + resources.BackendTrafficPolicies = append(resources.BackendTrafficPolicies, backendTrafficPolicy) + case egv1a1.KindSecurityPolicy: + typedSpec := spec.Interface() + securityPolicy := &egv1a1.SecurityPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindSecurityPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: typedSpec.(egv1a1.SecurityPolicySpec), + } + resources.SecurityPolicies = append(resources.SecurityPolicies, securityPolicy) } } @@ -872,19 +891,21 @@ func addDefaultEnvoyProxy(resources *gatewayapi.Resources) error { if err != nil { return err } - ep := &egv1alpha1.EnvoyProxy{ + ep := &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: defaultEnvoyProxyName, }, - Spec: egv1alpha1.EnvoyProxySpec{ - Bootstrap: &defaultBootstrapStr, + Spec: egv1a1.EnvoyProxySpec{ + Bootstrap: &egv1a1.ProxyBootstrap{ + Value: defaultBootstrapStr, + }, }, } resources.EnvoyProxy = ep - ns := v1beta1.Namespace(namespace) - resources.GatewayClass.Spec.ParametersRef = &v1beta1.ParametersReference{ - Group: v1beta1.Group(egv1alpha1.GroupVersion.Group), + ns := gwapiv1.Namespace(namespace) + resources.GatewayClass.Spec.ParametersRef = &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), Kind: gatewayapi.KindEnvoyProxy, Name: defaultEnvoyProxyName, Namespace: &ns, diff --git a/internal/cmd/egctl/translate_test.go b/internal/cmd/egctl/translate_test.go index 4b13327b682..d9cd598174a 100644 --- a/internal/cmd/egctl/translate_test.go +++ b/internal/cmd/egctl/translate_test.go @@ -111,27 +111,27 @@ func TestTranslate(t *testing.T) { expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: jsonOutput, expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, @@ -139,7 +139,7 @@ func TestTranslate(t *testing.T) { expect: false, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, @@ -147,7 +147,7 @@ func TestTranslate(t *testing.T) { expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, @@ -155,7 +155,7 @@ func TestTranslate(t *testing.T) { expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, @@ -163,7 +163,7 @@ func TestTranslate(t *testing.T) { expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, @@ -171,7 +171,7 @@ func TestTranslate(t *testing.T) { expect: true, }, { - name: "authn-single-route-single-match-to-xds", + name: "jwt-single-route-single-match-to-xds", from: "gateway-api", to: "xds", output: yamlOutput, @@ -186,83 +186,6 @@ func TestTranslate(t *testing.T) { resourceType: string(AllEnvoyConfigType), expect: true, }, - { - - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: jsonOutput, - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: "unknown", - expect: false, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: string(AllEnvoyConfigType), - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: string(BootstrapEnvoyConfigType), - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: string(ClusterEnvoyConfigType), - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: string(ListenerEnvoyConfigType), - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: string(RouteEnvoyConfigType), - expect: true, - }, - { - name: "rate-limit-filter-single-route-single-match-to-xds", - from: "gateway-api", - to: "xds", - output: yamlOutput, - resourceType: string(EndpointEnvoyConfigType), - expect: true, - }, { name: "default-resources", from: "gateway-api", diff --git a/internal/cmd/server.go b/internal/cmd/server.go index 863b7d1df34..6dc25a19946 100644 --- a/internal/cmd/server.go +++ b/internal/cmd/server.go @@ -6,17 +6,10 @@ package cmd import ( - "fmt" - "net" - "net/http" - "net/http/pprof" - "time" - - "github.com/davecgh/go-spew/spew" - "github.com/spf13/cobra" ctrl "sigs.k8s.io/controller-runtime" + "github.com/envoyproxy/gateway/internal/admin" "github.com/envoyproxy/gateway/internal/envoygateway/config" extensionregistry "github.com/envoyproxy/gateway/internal/extension/registry" gatewayapirunner "github.com/envoyproxy/gateway/internal/gatewayapi/runner" @@ -24,6 +17,7 @@ import ( infrarunner "github.com/envoyproxy/gateway/internal/infrastructure/runner" "github.com/envoyproxy/gateway/internal/logging" "github.com/envoyproxy/gateway/internal/message" + "github.com/envoyproxy/gateway/internal/metrics" providerrunner "github.com/envoyproxy/gateway/internal/provider/runner" xdsserverrunner "github.com/envoyproxy/gateway/internal/xds/server/runner" xdstranslatorrunner "github.com/envoyproxy/gateway/internal/xds/translator/runner" @@ -57,12 +51,16 @@ func server() error { return err } - if cfg.EnvoyGateway.Admin.Debug { - spewConfig := spew.NewDefaultConfig() - spewConfig.DisableMethods = true - spewConfig.Dump(cfg) + // Init eg admin servers. + if err := admin.Init(cfg); err != nil { + return err + } + // Init eg metrics servers. + if err := metrics.Init(cfg); err != nil { + return err } + // init eg runners. if err := setupRunners(cfg); err != nil { return err } @@ -124,16 +122,14 @@ func setupRunners(cfg *config.Server) error { } pResources := new(message.ProviderResources) - ePatchPolicyStatuses := new(message.EnvoyPatchPolicyStatuses) // Start the Provider Service // It fetches the resources from the configured provider type // and publishes it // It also subscribes to status resources and once it receives // a status resource back, it writes it out. providerRunner := providerrunner.New(&providerrunner.Config{ - Server: *cfg, - ProviderResources: pResources, - EnvoyPatchPolicyStatuses: ePatchPolicyStatuses, + Server: *cfg, + ProviderResources: pResources, }) if err := providerRunner.Start(ctx); err != nil { return err @@ -160,11 +156,11 @@ func setupRunners(cfg *config.Server) error { // It subscribes to the xdsIR, translates it into xds Resources and publishes it. // It also computes the EnvoyPatchPolicy statuses and publishes it. xdsTranslatorRunner := xdstranslatorrunner.New(&xdstranslatorrunner.Config{ - Server: *cfg, - XdsIR: xdsIR, - Xds: xds, - ExtensionManager: extMgr, - EnvoyPatchPolicyStatuses: ePatchPolicyStatuses, + Server: *cfg, + XdsIR: xdsIR, + Xds: xds, + ExtensionManager: extMgr, + ProviderResources: pResources, }) if err := xdsTranslatorRunner.Start(ctx); err != nil { return err @@ -205,14 +201,10 @@ func setupRunners(cfg *config.Server) error { } } - // Start the admin server - go setupAdminServer(cfg) - // Wait until done <-ctx.Done() // Close messages pResources.Close() - ePatchPolicyStatuses.Close() xdsIR.Close() infraIR.Close() xds.Close() @@ -226,33 +218,3 @@ func setupRunners(cfg *config.Server) error { return nil } - -func setupAdminServer(cfg *config.Server) { - adminHandlers := http.NewServeMux() - - address := cfg.EnvoyGateway.GetEnvoyGatewayAdmin().Address - - if cfg.EnvoyGateway.GetEnvoyGatewayAdmin().Debug { - // Serve pprof endpoints to aid in live debugging. - adminHandlers.HandleFunc("/debug/pprof/", pprof.Index) - adminHandlers.HandleFunc("/debug/pprof/profile", pprof.Profile) - adminHandlers.HandleFunc("/debug/pprof/trace", pprof.Trace) - adminHandlers.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - adminHandlers.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - } - - adminServer := &http.Server{ - Handler: adminHandlers, - Addr: net.JoinHostPort(address.Host, fmt.Sprint(address.Port)), - ReadTimeout: 5 * time.Second, - ReadHeaderTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 15 * time.Second, - } - - // Listen And Serve Admin Server. - if err := adminServer.ListenAndServe(); err != nil { - cfg.Logger.Error(err, "start debug server failed") - } - -} diff --git a/internal/cmd/server_test.go b/internal/cmd/server_test.go index a1818bed678..2dde218d1b2 100644 --- a/internal/cmd/server_test.go +++ b/internal/cmd/server_test.go @@ -14,7 +14,7 @@ import ( var ( validGatewayConfig = ` -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway provider: type: Kubernetes @@ -23,7 +23,7 @@ gateway: ` invalidGatewayConfig = ` kind: EnvoyGateway -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 gateway: {} ` ) diff --git a/internal/cmd/version/version.go b/internal/cmd/version/version.go index 760245ddcef..eb169af2b7c 100644 --- a/internal/cmd/version/version.go +++ b/internal/cmd/version/version.go @@ -14,7 +14,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" ) type Info struct { diff --git a/internal/crypto/certgen.go b/internal/crypto/certgen.go index 41000d26055..4f0e86f6bfc 100644 --- a/internal/crypto/certgen.go +++ b/internal/crypto/certgen.go @@ -17,7 +17,7 @@ import ( "math/big" "time" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" ) @@ -29,7 +29,7 @@ const ( DefaultEnvoyDNSPrefix = "*" // DefaultCertificateLifetime holds the default certificate lifetime (in days). - DefaultCertificateLifetime = 365 + DefaultCertificateLifetime = 365 * 5 // keySize sets the RSA key size to 2048 bits. This is minimum recommended size // for RSA keys. diff --git a/internal/envoygateway/config/config.go b/internal/envoygateway/config/config.go index fac7e3bcad2..4c9674a88b4 100644 --- a/internal/envoygateway/config/config.go +++ b/internal/envoygateway/config/config.go @@ -7,12 +7,9 @@ package config import ( "errors" - "fmt" - "net/url" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1/validation" "github.com/envoyproxy/gateway/internal/logging" "github.com/envoyproxy/gateway/internal/utils/env" ) @@ -57,72 +54,12 @@ func (s *Server) Validate() error { switch { case s == nil: return errors.New("server config is unspecified") - case s.EnvoyGateway == nil: - return errors.New("envoy gateway config is unspecified") - case s.EnvoyGateway.Gateway == nil: - return errors.New("gateway is unspecified") - case len(s.EnvoyGateway.Gateway.ControllerName) == 0: - return errors.New("gateway controllerName is unspecified") - case s.EnvoyGateway.Provider == nil: - return errors.New("provider is unspecified") - case s.EnvoyGateway.Provider.Type != v1alpha1.ProviderTypeKubernetes: - return fmt.Errorf("unsupported provider %v", s.EnvoyGateway.Provider.Type) case len(s.Namespace) == 0: return errors.New("namespace is empty string") - case s.EnvoyGateway.Logging != nil && len(s.EnvoyGateway.Logging.Level) != 0: - level := s.EnvoyGateway.Logging.Level - for component, logLevel := range level { - switch component { - case v1alpha1.LogComponentGatewayDefault, - v1alpha1.LogComponentProviderRunner, - v1alpha1.LogComponentGatewayAPIRunner, - v1alpha1.LogComponentXdsTranslatorRunner, - v1alpha1.LogComponentXdsServerRunner, - v1alpha1.LogComponentInfrastructureRunner, - v1alpha1.LogComponentGlobalRateLimitRunner: - switch logLevel { - case v1alpha1.LogLevelDebug, v1alpha1.LogLevelError, v1alpha1.LogLevelWarn, v1alpha1.LogLevelInfo: - default: - return errors.New("envoy gateway logging level invalid. valid options: info/debug/warn/error") - } - default: - return errors.New("envoy gateway logging components invalid. valid options: system/provider/gateway-api/xds-translator/xds-server/infrastructure") - } - } - case s.EnvoyGateway.RateLimit != nil: - if s.EnvoyGateway.RateLimit.Backend.Type != v1alpha1.RedisBackendType { - return fmt.Errorf("unsupported ratelimit backend %v", s.EnvoyGateway.RateLimit.Backend.Type) - } - if s.EnvoyGateway.RateLimit.Backend.Redis == nil || s.EnvoyGateway.RateLimit.Backend.Redis.URL == "" { - return fmt.Errorf("empty ratelimit redis settings") - } - if _, err := url.Parse(s.EnvoyGateway.RateLimit.Backend.Redis.URL); err != nil { - return fmt.Errorf("unknown ratelimit redis url format: %w", err) - } - case s.EnvoyGateway.ExtensionManager != nil: - if s.EnvoyGateway.ExtensionManager.Hooks == nil || s.EnvoyGateway.ExtensionManager.Hooks.XDSTranslator == nil { - return fmt.Errorf("registered extension has no hooks specified") - } - - if len(s.EnvoyGateway.ExtensionManager.Hooks.XDSTranslator.Pre) == 0 && len(s.EnvoyGateway.ExtensionManager.Hooks.XDSTranslator.Post) == 0 { - return fmt.Errorf("registered extension has no hooks specified") - } - - if s.EnvoyGateway.ExtensionManager.Service == nil { - return fmt.Errorf("extension service config is empty") - } - - if s.EnvoyGateway.ExtensionManager.Service.TLS != nil { - certificateRefKind := s.EnvoyGateway.ExtensionManager.Service.TLS.CertificateRef.Kind - - if certificateRefKind == nil { - return fmt.Errorf("certificateRef empty in extension service server TLS settings") - } - - if *certificateRefKind != gwapiv1b1.Kind("Secret") { - return fmt.Errorf("unsupported extension server TLS certificateRef %v", certificateRefKind) - } - } } + if err := validation.ValidateEnvoyGateway(s.EnvoyGateway); err != nil { + return err + } + return nil } diff --git a/internal/envoygateway/config/config_test.go b/internal/envoygateway/config/config_test.go index 209b2259867..09f3ddfdcb2 100644 --- a/internal/envoygateway/config/config_test.go +++ b/internal/envoygateway/config/config_test.go @@ -9,15 +9,15 @@ import ( "testing" "github.com/stretchr/testify/require" - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/logging" ) var ( - TLSSecretKind = v1beta1.Kind("Secret") - TLSUnrecognizedKind = v1beta1.Kind("Unrecognized") + TLSSecretKind = v1.Kind("Secret") + TLSUnrecognizedKind = v1.Kind("Unrecognized") ) func TestValidate(t *testing.T) { @@ -29,6 +29,11 @@ func TestValidate(t *testing.T) { cfg *Server expect bool }{ + { + name: "nil cfg", + cfg: nil, + expect: false, + }, { name: "default", cfg: cfg, @@ -55,440 +60,6 @@ func TestValidate(t *testing.T) { }, expect: false, }, - { - name: "unspecified gateway", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "unspecified provider", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "empty gateway controllerName", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: &v1alpha1.Gateway{ControllerName: ""}, - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "unsupported provider", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: &v1alpha1.EnvoyGatewayProvider{Type: v1alpha1.ProviderTypeFile}, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "empty ratelimit", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - RateLimit: &v1alpha1.RateLimit{}, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "empty ratelimit redis setting", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - RateLimit: &v1alpha1.RateLimit{ - Backend: v1alpha1.RateLimitDatabaseBackend{ - Type: v1alpha1.RedisBackendType, - Redis: &v1alpha1.RateLimitRedisSettings{}, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "unknown ratelimit redis url format", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - RateLimit: &v1alpha1.RateLimit{ - Backend: v1alpha1.RateLimitDatabaseBackend{ - Type: v1alpha1.RedisBackendType, - Redis: &v1alpha1.RateLimitRedisSettings{ - URL: ":foo", - }, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "happy ratelimit redis settings", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - RateLimit: &v1alpha1.RateLimit{ - Backend: v1alpha1.RateLimitDatabaseBackend{ - Type: v1alpha1.RedisBackendType, - Redis: &v1alpha1.RateLimitRedisSettings{ - URL: "localhost:6376", - }, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "happy extension settings", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - ExtensionManager: &v1alpha1.ExtensionManager{ - Resources: []v1alpha1.GroupVersionKind{ - { - Group: "foo.example.io", - Version: "v1alpha1", - Kind: "Foo", - }, - }, - Hooks: &v1alpha1.ExtensionHooks{ - XDSTranslator: &v1alpha1.XDSTranslatorHooks{ - Pre: []v1alpha1.XDSTranslatorHook{}, - Post: []v1alpha1.XDSTranslatorHook{ - v1alpha1.XDSHTTPListener, - v1alpha1.XDSTranslation, - v1alpha1.XDSRoute, - v1alpha1.XDSVirtualHost, - }, - }, - }, - Service: &v1alpha1.ExtensionService{ - Host: "foo.extension", - Port: 80, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "happy extension settings tls", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - ExtensionManager: &v1alpha1.ExtensionManager{ - Resources: []v1alpha1.GroupVersionKind{ - { - Group: "foo.example.io", - Version: "v1alpha1", - Kind: "Foo", - }, - }, - Hooks: &v1alpha1.ExtensionHooks{ - XDSTranslator: &v1alpha1.XDSTranslatorHooks{ - Pre: []v1alpha1.XDSTranslatorHook{}, - Post: []v1alpha1.XDSTranslatorHook{ - v1alpha1.XDSHTTPListener, - v1alpha1.XDSTranslation, - v1alpha1.XDSRoute, - v1alpha1.XDSVirtualHost, - }, - }, - }, - Service: &v1alpha1.ExtensionService{ - Host: "foo.extension", - Port: 443, - TLS: &v1alpha1.ExtensionTLS{ - CertificateRef: v1beta1.SecretObjectReference{ - Kind: &TLSSecretKind, - Name: v1beta1.ObjectName("certificate"), - }, - }, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "happy extension settings no resources", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - ExtensionManager: &v1alpha1.ExtensionManager{ - Hooks: &v1alpha1.ExtensionHooks{ - XDSTranslator: &v1alpha1.XDSTranslatorHooks{ - Pre: []v1alpha1.XDSTranslatorHook{}, - Post: []v1alpha1.XDSTranslatorHook{ - v1alpha1.XDSHTTPListener, - v1alpha1.XDSTranslation, - v1alpha1.XDSRoute, - v1alpha1.XDSVirtualHost, - }, - }, - }, - Service: &v1alpha1.ExtensionService{ - Host: "foo.extension", - Port: 443, - TLS: &v1alpha1.ExtensionTLS{ - CertificateRef: v1beta1.SecretObjectReference{ - Kind: &TLSSecretKind, - Name: v1beta1.ObjectName("certificate"), - }, - }, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "unknown TLS certificateRef in extension settings", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - ExtensionManager: &v1alpha1.ExtensionManager{ - Resources: []v1alpha1.GroupVersionKind{ - { - Group: "foo.example.io", - Version: "v1alpha1", - Kind: "Foo", - }, - }, - Hooks: &v1alpha1.ExtensionHooks{ - XDSTranslator: &v1alpha1.XDSTranslatorHooks{ - Pre: []v1alpha1.XDSTranslatorHook{}, - Post: []v1alpha1.XDSTranslatorHook{ - v1alpha1.XDSHTTPListener, - v1alpha1.XDSTranslation, - v1alpha1.XDSRoute, - v1alpha1.XDSVirtualHost, - }, - }, - }, - Service: &v1alpha1.ExtensionService{ - Host: "foo.extension", - Port: 8080, - TLS: &v1alpha1.ExtensionTLS{ - CertificateRef: v1beta1.SecretObjectReference{ - Kind: &TLSUnrecognizedKind, - Name: v1beta1.ObjectName("certificate"), - }, - }, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "empty service in extension settings", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - ExtensionManager: &v1alpha1.ExtensionManager{ - Resources: []v1alpha1.GroupVersionKind{ - { - Group: "foo.example.io", - Version: "v1alpha1", - Kind: "Foo", - }, - }, - Hooks: &v1alpha1.ExtensionHooks{ - XDSTranslator: &v1alpha1.XDSTranslatorHooks{ - Pre: []v1alpha1.XDSTranslatorHook{}, - Post: []v1alpha1.XDSTranslatorHook{ - v1alpha1.XDSHTTPListener, - v1alpha1.XDSTranslation, - v1alpha1.XDSRoute, - v1alpha1.XDSVirtualHost, - }, - }, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "empty hooks in extension settings", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - ExtensionManager: &v1alpha1.ExtensionManager{ - Resources: []v1alpha1.GroupVersionKind{ - { - Group: "foo.example.io", - Version: "v1alpha1", - Kind: "Foo", - }, - }, - Service: &v1alpha1.ExtensionService{ - Host: "foo.extension", - Port: 8080, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, - { - name: "valid gateway logging level info", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - Logging: &v1alpha1.EnvoyGatewayLogging{ - Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ - v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelInfo, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "valid gateway logging level warn", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - Logging: &v1alpha1.EnvoyGatewayLogging{ - Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ - v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelWarn, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "valid gateway logging level error", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - Logging: &v1alpha1.EnvoyGatewayLogging{ - Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ - v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelError, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "valid gateway logging level debug", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - Logging: &v1alpha1.EnvoyGatewayLogging{ - Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ - v1alpha1.LogComponentGatewayDefault: v1alpha1.LogLevelDebug, - v1alpha1.LogComponentProviderRunner: v1alpha1.LogLevelDebug, - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: true, - }, - { - name: "invalid gateway logging level", - cfg: &Server{ - EnvoyGateway: &v1alpha1.EnvoyGateway{ - EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ - Gateway: v1alpha1.DefaultGateway(), - Provider: v1alpha1.DefaultEnvoyGatewayProvider(), - Logging: &v1alpha1.EnvoyGatewayLogging{ - Level: map[v1alpha1.EnvoyGatewayLogComponent]v1alpha1.LogLevel{ - v1alpha1.LogComponentGatewayDefault: "inffo", - }, - }, - }, - }, - Namespace: "test-ns", - }, - expect: false, - }, } for _, tc := range testCases { diff --git a/internal/envoygateway/config/decoder.go b/internal/envoygateway/config/decoder.go index 636be4cc3df..dea52b68321 100644 --- a/internal/envoygateway/config/decoder.go +++ b/internal/envoygateway/config/decoder.go @@ -11,7 +11,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" ) diff --git a/internal/envoygateway/config/decoder_test.go b/internal/envoygateway/config/decoder_test.go index a45043687c3..ebd1bf145e6 100644 --- a/internal/envoygateway/config/decoder_test.go +++ b/internal/envoygateway/config/decoder_test.go @@ -12,10 +12,10 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" ) var ( @@ -148,11 +148,11 @@ func TestDecode(t *testing.T) { Value: "env_b_value", }, }, - Image: pointer.String("envoyproxy/ratelimit:latest"), + Image: ptr.To("envoyproxy/ratelimit:latest"), Resources: v1alpha1.DefaultResourceRequirements(), SecurityContext: &corev1.SecurityContext{ - RunAsUser: pointer.Int64(2000), - AllowPrivilegeEscalation: pointer.Bool(false), + RunAsUser: ptr.To[int64](2000), + AllowPrivilegeEscalation: ptr.To(false), }, }, Pod: &v1alpha1.KubernetesPodSpec{ @@ -161,9 +161,9 @@ func TestDecode(t *testing.T) { "key2": "val2", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), - RunAsGroup: pointer.Int64(3000), - FSGroup: pointer.Int64(2000), + RunAsUser: ptr.To[int64](1000), + RunAsGroup: ptr.To[int64](3000), + FSGroup: ptr.To[int64](2000), FSGroupChangePolicy: func(s corev1.PodFSGroupChangePolicy) *corev1.PodFSGroupChangePolicy { return &s }(corev1.FSGroupChangeOnRootMismatch), }, }, @@ -176,7 +176,7 @@ func TestDecode(t *testing.T) { Redis: &v1alpha1.RateLimitRedisSettings{ URL: "localhost:6379", TLS: &v1alpha1.RedisTLSSettings{ - CertificateRef: &gwapiv1b1.SecretObjectReference{ + CertificateRef: &gwapiv1.SecretObjectReference{ Name: "ratelimit-cert", }, }, @@ -187,6 +187,32 @@ func TestDecode(t *testing.T) { }, expect: true, }, + { + in: inPath + "gateway-global-ratelimit.yaml", + out: &v1alpha1.EnvoyGateway{ + TypeMeta: metav1.TypeMeta{ + Kind: v1alpha1.KindEnvoyGateway, + APIVersion: v1alpha1.GroupVersion.String(), + }, + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Provider: v1alpha1.DefaultEnvoyGatewayProvider(), + Gateway: v1alpha1.DefaultGateway(), + RateLimit: &v1alpha1.RateLimit{ + Timeout: &metav1.Duration{ + Duration: 10000000, + }, + FailClosed: true, + Backend: v1alpha1.RateLimitDatabaseBackend{ + Type: v1alpha1.RedisBackendType, + Redis: &v1alpha1.RateLimitRedisSettings{ + URL: "localhost:6379", + }, + }, + }, + }, + }, + expect: true, + }, { in: inPath + "gateway-logging.yaml", out: &v1alpha1.EnvoyGateway{ diff --git a/internal/envoygateway/config/testdata/decoder/in/gateway-controller-name.yaml b/internal/envoygateway/config/testdata/decoder/in/gateway-controller-name.yaml index 57226ff214a..f2dfce4804e 100644 --- a/internal/envoygateway/config/testdata/decoder/in/gateway-controller-name.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/gateway-controller-name.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway gateway: controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/envoygateway/config/testdata/decoder/in/gateway-global-ratelimit.yaml b/internal/envoygateway/config/testdata/decoder/in/gateway-global-ratelimit.yaml new file mode 100644 index 00000000000..90feadd0491 --- /dev/null +++ b/internal/envoygateway/config/testdata/decoder/in/gateway-global-ratelimit.yaml @@ -0,0 +1,13 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +provider: + type: Kubernetes +rateLimit: + timeout: 10ms + failClosed: true + backend: + type: Redis + redis: + url: localhost:6379 diff --git a/internal/envoygateway/config/testdata/decoder/in/gateway-logging.yaml b/internal/envoygateway/config/testdata/decoder/in/gateway-logging.yaml index ab789b0adfe..c9e24913aa2 100644 --- a/internal/envoygateway/config/testdata/decoder/in/gateway-logging.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/gateway-logging.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway gateway: controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/envoygateway/config/testdata/decoder/in/gateway-mixing-provider.yaml b/internal/envoygateway/config/testdata/decoder/in/gateway-mixing-provider.yaml index aaef7204554..29595ea1b8a 100644 --- a/internal/envoygateway/config/testdata/decoder/in/gateway-mixing-provider.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/gateway-mixing-provider.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway gateway: controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/envoygateway/config/testdata/decoder/in/gateway-ratelimit.yaml b/internal/envoygateway/config/testdata/decoder/in/gateway-ratelimit.yaml index b9b59043d86..015d7e149ec 100644 --- a/internal/envoygateway/config/testdata/decoder/in/gateway-ratelimit.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/gateway-ratelimit.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway gateway: controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-kind.yaml b/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-kind.yaml index 9fe60c708b4..64ff4615b87 100644 --- a/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-kind.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-kind.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateways provider: type: Kubernetes diff --git a/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-logging.yaml b/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-logging.yaml index c52d64bee9b..dfab49e3c49 100644 --- a/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-logging.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-logging.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway gateway: controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-version.yaml b/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-version.yaml index 74b64c923ac..dcc0bb23783 100644 --- a/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-version.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/invalid-gateway-version.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1beta +apiVersion: gateway.envoyproxy.io/v1beta kind: EnvoyGateway provider: type: Kubernetes diff --git a/internal/envoygateway/config/testdata/decoder/in/kube-provider.yaml b/internal/envoygateway/config/testdata/decoder/in/kube-provider.yaml index ec9798d6f02..5b57b025643 100644 --- a/internal/envoygateway/config/testdata/decoder/in/kube-provider.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/kube-provider.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway provider: type: Kubernetes diff --git a/internal/envoygateway/config/testdata/decoder/in/no-kind.yaml b/internal/envoygateway/config/testdata/decoder/in/no-kind.yaml index 8e8e2013075..938a28ad5e6 100644 --- a/internal/envoygateway/config/testdata/decoder/in/no-kind.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/no-kind.yaml @@ -1,3 +1,3 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 provider: type: Kubernetes diff --git a/internal/envoygateway/config/testdata/decoder/in/provider-mixing-gateway.yaml b/internal/envoygateway/config/testdata/decoder/in/provider-mixing-gateway.yaml index 128d8f89f8f..d5b7a239d87 100644 --- a/internal/envoygateway/config/testdata/decoder/in/provider-mixing-gateway.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/provider-mixing-gateway.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway provider: type: Kubernetes diff --git a/internal/envoygateway/config/testdata/decoder/in/provider-with-gateway.yaml b/internal/envoygateway/config/testdata/decoder/in/provider-with-gateway.yaml index 86be0afa6f2..f16039a315e 100644 --- a/internal/envoygateway/config/testdata/decoder/in/provider-with-gateway.yaml +++ b/internal/envoygateway/config/testdata/decoder/in/provider-with-gateway.yaml @@ -1,4 +1,4 @@ -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyGateway provider: type: Kubernetes diff --git a/internal/envoygateway/scheme.go b/internal/envoygateway/scheme.go index 8f175ee21cc..911ae708351 100644 --- a/internal/envoygateway/scheme.go +++ b/internal/envoygateway/scheme.go @@ -8,10 +8,11 @@ package envoygateway import ( "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) @@ -30,19 +31,23 @@ func init() { panic(err) } // Add Envoy Gateway types. - if err := egcfgv1a1.AddToScheme(scheme); err != nil { - panic(err) - } if err := egv1a1.AddToScheme(scheme); err != nil { panic(err) } // Add Gateway API types. + if err := gwapiv1.AddToScheme(scheme); err != nil { + panic(err) + } if err := gwapiv1b1.AddToScheme(scheme); err != nil { panic(err) } if err := gwapiv1a2.AddToScheme(scheme); err != nil { panic(err) } + // Add mcs api types. + if err := mcsapi.AddToScheme(scheme); err != nil { + panic(err) + } } // GetScheme returns a scheme with types supported by the Kubernetes provider. diff --git a/internal/extension/registry/extension_manager.go b/internal/extension/registry/extension_manager.go index 14f368a3309..dd2170c5be6 100644 --- a/internal/extension/registry/extension_manager.go +++ b/internal/extension/registry/extension_manager.go @@ -18,9 +18,9 @@ import ( corev1 "k8s.io/api/core/v1" k8scli "sigs.k8s.io/controller-runtime/pkg/client" k8sclicfg "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" extTypes "github.com/envoyproxy/gateway/internal/extension/types" @@ -76,11 +76,11 @@ func NewManager(cfg *config.Server) (extTypes.Manager, error) { // HasExtension checks to see whether a given Group and Kind has an // associated extension registered for it. -func (m *Manager) HasExtension(g v1beta1.Group, k v1beta1.Kind) bool { +func (m *Manager) HasExtension(g v1.Group, k v1.Kind) bool { extension := m.extension // TODO: not currently checking the version since extensionRef only supports group and kind. for _, gvk := range extension.Resources { - if g == v1beta1.Group(gvk.Group) && k == v1beta1.Kind(gvk.Kind) { + if g == v1.Group(gvk.Group) && k == v1.Kind(gvk.Kind) { return true } } diff --git a/internal/extension/testutils/hooks.go b/internal/extension/testutils/hooks.go index d42867163ba..f6b699b34b8 100644 --- a/internal/extension/testutils/hooks.go +++ b/internal/extension/testutils/hooks.go @@ -80,7 +80,7 @@ func (c *XDSHookClient) PostRouteModifyHook(route *routeV3.Route, routeHostnames func (c *XDSHookClient) PostVirtualHostModifyHook(vh *routeV3.VirtualHost) (*routeV3.VirtualHost, error) { // Only make the change when the VirtualHost's name matches the expected testdata // This prevents us from having to update every single testfile.out - if vh.Name == "extension-post-xdsvirtualhost-hook-error" { + if vh.Name == "extension-post-xdsvirtualhost-hook-error/*" { return nil, fmt.Errorf("extension post xds virtual host hook error") } else if vh.Name == "extension-listener" { // Setup a new VirtualHost to avoid operating directly on the passed in pointer for better test coverage that the diff --git a/internal/extension/testutils/manager.go b/internal/extension/testutils/manager.go index aa85c02b4c7..7185d58739f 100644 --- a/internal/extension/testutils/manager.go +++ b/internal/extension/testutils/manager.go @@ -6,9 +6,9 @@ package testutils import ( - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" extType "github.com/envoyproxy/gateway/internal/extension/types" ) @@ -24,11 +24,11 @@ func NewManager(ext v1alpha1.ExtensionManager) extType.Manager { } } -func (m *Manager) HasExtension(g v1beta1.Group, k v1beta1.Kind) bool { +func (m *Manager) HasExtension(g v1.Group, k v1.Kind) bool { extension := m.extension // TODO: not currently checking the version since extensionRef only supports group and kind. for _, gvk := range extension.Resources { - if g == v1beta1.Group(gvk.Group) && k == v1beta1.Kind(gvk.Kind) { + if g == v1.Group(gvk.Group) && k == v1.Kind(gvk.Kind) { return true } } diff --git a/internal/extension/types/manager.go b/internal/extension/types/manager.go index 29111ef28fc..766bd4fda3c 100644 --- a/internal/extension/types/manager.go +++ b/internal/extension/types/manager.go @@ -6,9 +6,9 @@ package types import ( - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" ) // Manager handles and maintains registered extensions and returns clients for @@ -19,7 +19,7 @@ type Manager interface { // // If a Group and Kind is registered with an extension, then it should // return true, otherwise return false. - HasExtension(g v1beta1.Group, k v1beta1.Kind) bool + HasExtension(g v1.Group, k v1.Kind) bool // GetPreXDSHookClient checks if the registered extension makes use of a particular hook type that modifies inputs // that are used to generate an xDS resource. diff --git a/internal/gatewayapi/address.go b/internal/gatewayapi/address.go index b8d7d507563..54907df0b12 100644 --- a/internal/gatewayapi/address.go +++ b/internal/gatewayapi/address.go @@ -6,7 +6,7 @@ package gatewayapi import ( - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) var _ AddressesTranslator = (*Translator)(nil) @@ -18,12 +18,12 @@ type AddressesTranslator interface { func (t *Translator) ProcessAddresses(gateways []*GatewayContext, xdsIR XdsIRMap, infraIR InfraIRMap, resources *Resources) { for _, gateway := range gateways { // Infra IR already exist - irKey := irStringKey(gateway.Gateway.Namespace, gateway.Gateway.Name) + irKey := t.getIRKey(gateway.Gateway) gwInfraIR := infraIR[irKey] var ipAddr []string for _, addr := range gateway.Spec.Addresses { - if *addr.Type == v1beta1.IPAddressType { + if *addr.Type == v1.IPAddressType { ipAddr = append(ipAddr, addr.Value) } } diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go new file mode 100644 index 00000000000..f9af6fc0d68 --- /dev/null +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -0,0 +1,484 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package gatewayapi + +import ( + "fmt" + "net" + "sort" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/status" +) + +type policyTargetRouteKey struct { + Kind string + Namespace string + Name string +} + +type policyRouteTargetContext struct { + RouteContext + attached bool +} + +type policyGatewayTargetContext struct { + *GatewayContext + attached bool +} + +func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv1a1.BackendTrafficPolicy, + gateways []*GatewayContext, + routes []RouteContext, + xdsIR XdsIRMap) []*egv1a1.BackendTrafficPolicy { + var res []*egv1a1.BackendTrafficPolicy + + // Sort based on timestamp + sort.Slice(backendTrafficPolicies, func(i, j int) bool { + return backendTrafficPolicies[i].CreationTimestamp.Before(&(backendTrafficPolicies[j].CreationTimestamp)) + }) + + // First build a map out of the routes and gateways for faster lookup since users might have thousands of routes or more. + // For gateways this probably isn't quite as necessary. + routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{} + for _, route := range routes { + key := policyTargetRouteKey{ + Kind: string(GetRouteType(route)), + Name: route.GetName(), + Namespace: route.GetNamespace(), + } + routeMap[key] = &policyRouteTargetContext{RouteContext: route} + } + gatewayMap := map[types.NamespacedName]*policyGatewayTargetContext{} + for _, gw := range gateways { + key := types.NamespacedName{ + Name: gw.GetName(), + Namespace: gw.GetNamespace(), + } + gatewayMap[key] = &policyGatewayTargetContext{GatewayContext: gw} + } + + // Translate + // 1. First translate Policies targeting xRoutes + // 2.. Finally, the policies targeting Gateways + + // Process the policies targeting xRoutes + for _, policy := range backendTrafficPolicies { + if policy.Spec.TargetRef.Kind != KindGateway { + policy := policy.DeepCopy() + res = append(res, policy) + + // Negative statuses have already been assigned so its safe to skip + route := resolveBTPolicyRouteTargetRef(policy, routeMap) + if route == nil { + continue + } + + t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR) + + message := "BackendTrafficPolicy has been accepted." + status.SetBackendTrafficPolicyAcceptedIfUnset(&policy.Status, message) + } + } + + // Process the policies targeting Gateways + for _, policy := range backendTrafficPolicies { + if policy.Spec.TargetRef.Kind == KindGateway { + policy := policy.DeepCopy() + res = append(res, policy) + + // Negative statuses have already been assigned so its safe to skip + gateway := resolveBTPolicyGatewayTargetRef(policy, gatewayMap) + if gateway == nil { + continue + } + + t.translateBackendTrafficPolicyForGateway(policy, gateway, xdsIR) + + message := "BackendTrafficPolicy has been accepted." + status.SetBackendTrafficPolicyAcceptedIfUnset(&policy.Status, message) + } + } + + return res +} + +func resolveBTPolicyGatewayTargetRef(policy *egv1a1.BackendTrafficPolicy, gateways map[types.NamespacedName]*policyGatewayTargetContext) *GatewayContext { + targetNs := policy.Spec.TargetRef.Namespace + // If empty, default to namespace of policy + if targetNs == nil { + targetNs = ptr.To(gwv1b1.Namespace(policy.Namespace)) + } + + // Ensure Policy and target are in the same namespace + if policy.Namespace != string(*targetNs) { + + message := fmt.Sprintf("Namespace:%s TargetRef.Namespace:%s, BackendTrafficPolicy can only target a resource in the same namespace.", + policy.Namespace, *targetNs) + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + + // Find the Gateway + key := types.NamespacedName{ + Name: string(policy.Spec.TargetRef.Name), + Namespace: string(*targetNs), + } + gateway, ok := gateways[key] + + // Gateway not found + if !ok { + message := fmt.Sprintf("Gateway:%s not found.", policy.Spec.TargetRef.Name) + + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + return nil + } + + // Check if another policy targeting the same Gateway exists + if gateway.attached { + message := "Unable to target Gateway, another BackendTrafficPolicy has already attached to it" + + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonConflicted, + message, + ) + return nil + } + + // Set context and save + gateway.attached = true + gateways[key] = gateway + + return gateway.GatewayContext +} + +func resolveBTPolicyRouteTargetRef(policy *egv1a1.BackendTrafficPolicy, routes map[policyTargetRouteKey]*policyRouteTargetContext) RouteContext { + targetNs := policy.Spec.TargetRef.Namespace + // If empty, default to namespace of policy + if targetNs == nil { + targetNs = ptr.To(gwv1b1.Namespace(policy.Namespace)) + } + + // Ensure Policy and target are in the same namespace + if policy.Namespace != string(*targetNs) { + + message := fmt.Sprintf("Namespace:%s TargetRef.Namespace:%s, BackendTrafficPolicy can only target a resource in the same namespace.", + policy.Namespace, *targetNs) + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + + // Check if the route exists + key := policyTargetRouteKey{ + Kind: string(policy.Spec.TargetRef.Kind), + Name: string(policy.Spec.TargetRef.Name), + Namespace: string(*targetNs), + } + route, ok := routes[key] + + // Route not found + if !ok { + message := fmt.Sprintf("%s/%s/%s not found.", policy.Spec.TargetRef.Kind, string(*targetNs), policy.Spec.TargetRef.Name) + + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + return nil + } + + // Check if another policy targeting the same xRoute exists + if route.attached { + message := fmt.Sprintf("Unable to target %s, another BackendTrafficPolicy has already attached to it", string(policy.Spec.TargetRef.Kind)) + + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonConflicted, + message, + ) + return nil + } + + // Set context and save + route.attached = true + routes[key] = route + + return route.RouteContext +} + +func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.BackendTrafficPolicy, route RouteContext, xdsIR XdsIRMap) { + var ( + rl *ir.RateLimit + lb *ir.LoadBalancer + pp *ir.ProxyProtocol + ) + + // Build IR + if policy.Spec.RateLimit != nil { + rl = t.buildRateLimit(policy) + } + if policy.Spec.LoadBalancer != nil { + lb = t.buildLoadBalancer(policy) + } + if policy.Spec.ProxyProtocol != nil { + pp = t.buildProxyProtocol(policy) + } + // Apply IR to all relevant routes + prefix := irRoutePrefix(route) + for _, ir := range xdsIR { + for _, http := range ir.HTTP { + for _, r := range http.Routes { + // Apply if there is a match + if strings.HasPrefix(r.Name, prefix) { + r.RateLimit = rl + r.LoadBalancer = lb + r.ProxyProtocol = pp + } + } + } + + } +} + +func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.BackendTrafficPolicy, gateway *GatewayContext, xdsIR XdsIRMap) { + var ( + rl *ir.RateLimit + lb *ir.LoadBalancer + pp *ir.ProxyProtocol + ) + + // Build IR + if policy.Spec.RateLimit != nil { + rl = t.buildRateLimit(policy) + } + if policy.Spec.LoadBalancer != nil { + lb = t.buildLoadBalancer(policy) + } + if policy.Spec.ProxyProtocol != nil { + pp = t.buildProxyProtocol(policy) + } + // Apply IR to all the routes within the specific Gateway + // If the feature is already set, then skip it, since it must be have + // set by a policy attaching to the route + irKey := t.getIRKey(gateway.Gateway) + // Should exist since we've validated this + ir := xdsIR[irKey] + + for _, http := range ir.HTTP { + for _, r := range http.Routes { + // Apply if not already set + if r.RateLimit == nil { + r.RateLimit = rl + } + if r.LoadBalancer == nil { + r.LoadBalancer = lb + } + if r.ProxyProtocol == nil { + r.ProxyProtocol = pp + } + } + } + +} + +func (t *Translator) buildRateLimit(policy *egv1a1.BackendTrafficPolicy) *ir.RateLimit { + if policy.Spec.RateLimit.Global == nil { + message := "Global configuration empty for rateLimit" + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + if !t.GlobalRateLimitEnabled { + message := "Enable Ratelimit in the EnvoyGateway config to configure global rateLimit" + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + rateLimit := &ir.RateLimit{ + Global: &ir.GlobalRateLimit{ + Rules: make([]*ir.RateLimitRule, len(policy.Spec.RateLimit.Global.Rules)), + }, + } + + rules := rateLimit.Global.Rules + for i, rule := range policy.Spec.RateLimit.Global.Rules { + rules[i] = &ir.RateLimitRule{ + Limit: &ir.RateLimitValue{ + Requests: rule.Limit.Requests, + Unit: ir.RateLimitUnit(rule.Limit.Unit), + }, + HeaderMatches: make([]*ir.StringMatch, 0), + } + for _, match := range rule.ClientSelectors { + for _, header := range match.Headers { + switch { + case header.Type == nil && header.Value != nil: + fallthrough + case *header.Type == egv1a1.HeaderMatchExact && header.Value != nil: + m := &ir.StringMatch{ + Name: header.Name, + Exact: header.Value, + } + rules[i].HeaderMatches = append(rules[i].HeaderMatches, m) + case *header.Type == egv1a1.HeaderMatchRegularExpression && header.Value != nil: + m := &ir.StringMatch{ + Name: header.Name, + SafeRegex: header.Value, + } + rules[i].HeaderMatches = append(rules[i].HeaderMatches, m) + case *header.Type == egv1a1.HeaderMatchDistinct && header.Value == nil: + m := &ir.StringMatch{ + Name: header.Name, + Distinct: true, + } + rules[i].HeaderMatches = append(rules[i].HeaderMatches, m) + default: + // set negative status condition. + message := "Unable to translate rateLimit. Either the header.Type is not valid or the header is missing a value" + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + + return nil + } + } + + if match.SourceCIDR != nil { + // distinct means that each IP Address within the specified Source IP CIDR is treated as a + // distinct client selector and uses a separate rate limit bucket/counter. + distinct := false + sourceCIDR := match.SourceCIDR.Value + if match.SourceCIDR.Type != nil && *match.SourceCIDR.Type == egv1a1.SourceMatchDistinct { + distinct = true + } + + ip, ipn, err := net.ParseCIDR(sourceCIDR) + if err != nil { + message := "Unable to translate rateLimit" + status.SetBackendTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + + return nil + } + + mask, _ := ipn.Mask.Size() + rules[i].CIDRMatch = &ir.CIDRMatch{ + CIDR: ipn.String(), + IPv6: ip.To4() == nil, + MaskLen: mask, + Distinct: distinct, + } + } + } + } + + return rateLimit +} + +func (t *Translator) buildLoadBalancer(policy *egv1a1.BackendTrafficPolicy) *ir.LoadBalancer { + var lb *ir.LoadBalancer + switch policy.Spec.LoadBalancer.Type { + case egv1a1.ConsistentHashLoadBalancerType: + lb = &ir.LoadBalancer{ + ConsistentHash: &ir.ConsistentHash{}, + } + if policy.Spec.LoadBalancer.ConsistentHash != nil && + policy.Spec.LoadBalancer.ConsistentHash.Type == egv1a1.SourceIPConsistentHashType { + lb.ConsistentHash.SourceIP = ptr.To(true) + } + case egv1a1.LeastRequestLoadBalancerType: + lb = &ir.LoadBalancer{} + if policy.Spec.LoadBalancer.SlowStart != nil { + if policy.Spec.LoadBalancer.SlowStart.Window != nil { + lb.LeastRequest = &ir.LeastRequest{ + SlowStart: &ir.SlowStart{ + Window: policy.Spec.LoadBalancer.SlowStart.Window, + }, + } + } + } + case egv1a1.RandomLoadBalancerType: + lb = &ir.LoadBalancer{ + Random: &ir.Random{}, + } + case egv1a1.RoundRobinLoadBalancerType: + lb = &ir.LoadBalancer{ + RoundRobin: &ir.RoundRobin{ + SlowStart: &ir.SlowStart{}, + }, + } + if policy.Spec.LoadBalancer.SlowStart != nil { + if policy.Spec.LoadBalancer.SlowStart.Window != nil { + lb.RoundRobin = &ir.RoundRobin{ + SlowStart: &ir.SlowStart{ + Window: policy.Spec.LoadBalancer.SlowStart.Window, + }, + } + } + } + } + + return lb +} + +func (t *Translator) buildProxyProtocol(policy *egv1a1.BackendTrafficPolicy) *ir.ProxyProtocol { + var pp *ir.ProxyProtocol + switch policy.Spec.ProxyProtocol.Version { + case egv1a1.ProxyProtocolVersionV1: + pp = &ir.ProxyProtocol{ + Version: ir.ProxyProtocolVersionV1, + } + case egv1a1.ProxyProtocolVersionV2: + pp = &ir.ProxyProtocol{ + Version: ir.ProxyProtocolVersionV2, + } + } + + return pp +} diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go new file mode 100644 index 00000000000..bb026f1a750 --- /dev/null +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -0,0 +1,337 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package gatewayapi + +import ( + "fmt" + "sort" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/utils/ptr" + gwv1b1 "sigs.k8s.io/gateway-api/apis/v1" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/status" +) + +const ( + // Use an invalid string to represent all sections (listeners) within a Gateway + AllSections = "/" +) + +func hasSectionName(policy *egv1a1.ClientTrafficPolicy) bool { + return policy.Spec.TargetRef.SectionName != nil +} + +func ProcessClientTrafficPolicies(clientTrafficPolicies []*egv1a1.ClientTrafficPolicy, + gateways []*GatewayContext, + xdsIR XdsIRMap) []*egv1a1.ClientTrafficPolicy { + var res []*egv1a1.ClientTrafficPolicy + + // Sort based on timestamp + sort.Slice(clientTrafficPolicies, func(i, j int) bool { + return clientTrafficPolicies[i].CreationTimestamp.Before(&(clientTrafficPolicies[j].CreationTimestamp)) + }) + + policyMap := make(map[types.NamespacedName]sets.Set[string]) + + // Translate + // 1. First translate Policies with a sectionName set + // 2. Then loop again and translate the policies without a sectionName + // TODO: Import sort order to ensure policy with same section always appear + // before policy with no section so below loops can be flattened into 1. + + for _, policy := range clientTrafficPolicies { + if hasSectionName(policy) { + policy := policy.DeepCopy() + res = append(res, policy) + + gateway := resolveCTPolicyTargetRef(policy, gateways) + + // Negative statuses have already been assigned so its safe to skip + if gateway == nil { + continue + } + + // Check for conflicts + key := types.NamespacedName{ + Name: gateway.Name, + Namespace: gateway.Namespace, + } + + // Check if another policy targeting the same section exists + section := string(*(policy.Spec.TargetRef.SectionName)) + s, ok := policyMap[key] + if ok && s.Has(section) { + message := "Unable to target section, another ClientTrafficPolicy has already attached to it" + + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonConflicted, + message, + ) + continue + } + + // Add section to policy map + if s == nil { + policyMap[key] = sets.New[string]() + } + policyMap[key].Insert(section) + + // Translate for listener matching section name + for _, l := range gateway.listeners { + if string(l.Name) == section { + translateClientTrafficPolicyForListener(&policy.Spec, l, xdsIR) + break + } + } + // Set Accepted=True + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionTrue, + gwv1a2.PolicyReasonAccepted, + "ClientTrafficPolicy has been accepted.", + ) + } + } + + // Policy with no section set (targeting all sections) + for _, policy := range clientTrafficPolicies { + if !hasSectionName(policy) { + + policy := policy.DeepCopy() + res = append(res, policy) + + gateway := resolveCTPolicyTargetRef(policy, gateways) + + // Negative statuses have already been assigned so its safe to skip + if gateway == nil { + continue + } + + // Check for conflicts + key := types.NamespacedName{ + Name: gateway.Name, + Namespace: gateway.Namespace, + } + s, ok := policyMap[key] + // Check if another policy targeting the same Gateway exists + if ok && s.Has(AllSections) { + message := "Unable to target Gateway, another ClientTrafficPolicy has already attached to it" + + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonConflicted, + message, + ) + + continue + + } + + // Check if another policy targeting the same Gateway exists + if ok && (s.Len() > 0) { + // Maintain order here to ensure status/string does not change with same data + sections := s.UnsortedList() + sort.Strings(sections) + message := fmt.Sprintf("There are existing ClientTrafficPolicies that are overriding these sections %v", sections) + + status.SetClientTrafficPolicyCondition(policy, + egv1a1.PolicyConditionOverridden, + metav1.ConditionTrue, + egv1a1.PolicyReasonOverridden, + message, + ) + } + + // Add section to policy map + if s == nil { + policyMap[key] = sets.New[string]() + } + policyMap[key].Insert(AllSections) + + // Translate sections that have not yet been targeted + for _, l := range gateway.listeners { + // Skip if section has already been targeted + if s != nil && s.Has(string(l.Name)) { + continue + } + + translateClientTrafficPolicyForListener(&policy.Spec, l, xdsIR) + } + + // Set Accepted=True + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionTrue, + gwv1a2.PolicyReasonAccepted, + "ClientTrafficPolicy has been accepted.", + ) + } + } + + return res +} + +func resolveCTPolicyTargetRef(policy *egv1a1.ClientTrafficPolicy, gateways []*GatewayContext) *GatewayContext { + targetNs := policy.Spec.TargetRef.Namespace + // If empty, default to namespace of policy + if targetNs == nil { + targetNs = ptr.To(gwv1b1.Namespace(policy.Namespace)) + } + + // Ensure policy can only target a Gateway + if policy.Spec.TargetRef.Group != gwv1b1.GroupName || policy.Spec.TargetRef.Kind != KindGateway { + message := fmt.Sprintf("TargetRef.Group:%s TargetRef.Kind:%s, only TargetRef.Group:%s and TargetRef.Kind:%s is supported.", + policy.Spec.TargetRef.Group, policy.Spec.TargetRef.Kind, gwv1b1.GroupName, KindGateway) + + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + + // Ensure Policy and target Gateway are in the same namespace + if policy.Namespace != string(*targetNs) { + + message := fmt.Sprintf("Namespace:%s TargetRef.Namespace:%s, ClientTrafficPolicy can only target a Gateway in the same namespace.", + policy.Namespace, *targetNs) + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + + // Find the Gateway + var gateway *GatewayContext + for _, g := range gateways { + if g.Name == string(policy.Spec.TargetRef.Name) && g.Namespace == string(*targetNs) { + gateway = g + break + } + } + + // Gateway not found + if gateway == nil { + message := fmt.Sprintf("Gateway:%s not found.", policy.Spec.TargetRef.Name) + + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + return nil + } + + // If sectionName is set, make sure its valid + if policy.Spec.TargetRef.SectionName != nil { + found := false + for _, l := range gateway.listeners { + if l.Name == *(policy.Spec.TargetRef.SectionName) { + found = true + break + } + } + if !found { + message := fmt.Sprintf("SectionName(Listener):%s not found.", *(policy.Spec.TargetRef.SectionName)) + status.SetClientTrafficPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + return nil + } + } + + return gateway +} + +func translateClientTrafficPolicyForListener(policySpec *egv1a1.ClientTrafficPolicySpec, l *ListenerContext, xdsIR XdsIRMap) { + // Find IR + irKey := irStringKey(l.gateway.Namespace, l.gateway.Name) + // It must exist since we've already finished processing the gateways + gwXdsIR := xdsIR[irKey] + + // Find Listener IR + // TODO: Support TLSRoute and TCPRoute once + // https://github.com/envoyproxy/gateway/issues/1635 is completed + + irListenerName := irHTTPListenerName(l) + var httpIR *ir.HTTPListener + for _, http := range gwXdsIR.HTTP { + if http.Name == irListenerName { + httpIR = http + break + } + } + + // IR must exist since we're past validation + if httpIR != nil { + // Translate TCPKeepalive + translateListenerTCPKeepalive(policySpec.TCPKeepalive, httpIR) + + // Translate Proxy Protocol + translateListenerProxyProtocol(policySpec.EnableProxyProtocol, httpIR) + } +} + +func translateListenerTCPKeepalive(tcpKeepAlive *egv1a1.TCPKeepalive, httpIR *ir.HTTPListener) { + // Return early if not set + if tcpKeepAlive == nil { + return + } + + irTCPKeepalive := &ir.TCPKeepalive{} + + if tcpKeepAlive.Probes != nil { + irTCPKeepalive.Probes = tcpKeepAlive.Probes + } + + if tcpKeepAlive.IdleTime != nil { + d, err := time.ParseDuration(string(*tcpKeepAlive.IdleTime)) + if err != nil { + return + } + irTCPKeepalive.IdleTime = ptr.To(uint32(d.Seconds())) + } + + if tcpKeepAlive.Interval != nil { + d, err := time.ParseDuration(string(*tcpKeepAlive.Interval)) + if err != nil { + return + } + irTCPKeepalive.Interval = ptr.To(uint32(d.Seconds())) + } + + httpIR.TCPKeepalive = irTCPKeepalive +} + +func translateListenerProxyProtocol(enableProxyProtocol *bool, httpIR *ir.HTTPListener) { + // Return early if not set + if enableProxyProtocol == nil { + return + } + + if *enableProxyProtocol { + httpIR.EnableProxyProtocol = true + } +} diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go index 03823dc6642..83eb153a94d 100644 --- a/internal/gatewayapi/contexts.go +++ b/internal/gatewayapi/contexts.go @@ -13,14 +13,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" ) // GatewayContext wraps a Gateway and provides helper methods for // setting conditions, accessing Listeners, etc. type GatewayContext struct { - *v1beta1.Gateway + *gwapiv1.Gateway listeners []*ListenerContext } @@ -29,11 +29,11 @@ type GatewayContext struct { // ListenerContexts from the Gateway spec. func (g *GatewayContext) ResetListeners() { numListeners := len(g.Spec.Listeners) - g.Status.Listeners = make([]v1beta1.ListenerStatus, numListeners) + g.Status.Listeners = make([]gwapiv1.ListenerStatus, numListeners) g.listeners = make([]*ListenerContext, numListeners) for i := range g.Spec.Listeners { listener := &g.Spec.Listeners[i] - g.Status.Listeners[i] = v1beta1.ListenerStatus{Name: listener.Name} + g.Status.Listeners[i] = gwapiv1.ListenerStatus{Name: listener.Name} g.listeners[i] = &ListenerContext{ Listener: listener, gateway: g.Gateway, @@ -46,15 +46,15 @@ func (g *GatewayContext) ResetListeners() { // setting conditions and other status information on the associated // Gateway, etc. type ListenerContext struct { - *v1beta1.Listener + *gwapiv1.Listener - gateway *v1beta1.Gateway + gateway *gwapiv1.Gateway listenerStatusIdx int namespaceSelector labels.Selector tlsSecrets []*v1.Secret } -func (l *ListenerContext) SetCondition(conditionType v1beta1.ListenerConditionType, status metav1.ConditionStatus, reason v1beta1.ListenerConditionReason, message string) { +func (l *ListenerContext) SetCondition(conditionType gwapiv1.ListenerConditionType, status metav1.ConditionStatus, reason gwapiv1.ListenerConditionReason, message string) { cond := metav1.Condition{ Type: string(conditionType), Status: status, @@ -86,7 +86,7 @@ func (l *ListenerContext) SetCondition(conditionType v1beta1.ListenerConditionTy } } -func (l *ListenerContext) SetSupportedKinds(kinds ...v1beta1.RouteGroupKind) { +func (l *ListenerContext) SetSupportedKinds(kinds ...gwapiv1.RouteGroupKind) { l.gateway.Status.Listeners[l.listenerStatusIdx].SupportedKinds = kinds } @@ -98,7 +98,7 @@ func (l *ListenerContext) AttachedRoutes() int32 { return l.gateway.Status.Listeners[l.listenerStatusIdx].AttachedRoutes } -func (l *ListenerContext) AllowsKind(kind v1beta1.RouteGroupKind) bool { +func (l *ListenerContext) AllowsKind(kind gwapiv1.RouteGroupKind) bool { for _, allowed := range l.gateway.Status.Listeners[l.listenerStatusIdx].SupportedKinds { if GroupDerefOr(allowed.Group, "") == GroupDerefOr(kind.Group, "") && allowed.Kind == kind.Kind { @@ -119,9 +119,9 @@ func (l *ListenerContext) AllowsNamespace(namespace *v1.Namespace) bool { } switch *l.AllowedRoutes.Namespaces.From { - case v1beta1.NamespacesFromAll: + case gwapiv1.NamespacesFromAll: return true - case v1beta1.NamespacesFromSelector: + case gwapiv1.NamespacesFromSelector: if l.namespaceSelector == nil { return false } @@ -134,7 +134,7 @@ func (l *ListenerContext) AllowsNamespace(namespace *v1.Namespace) bool { func (l *ListenerContext) IsReady() bool { for _, cond := range l.gateway.Status.Listeners[l.listenerStatusIdx].Conditions { - if cond.Type == string(v1beta1.ListenerConditionProgrammed) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.ListenerConditionProgrammed) && cond.Status == metav1.ConditionTrue { return true } } @@ -162,9 +162,9 @@ type HTTPRouteContext struct { // GatewayControllerName is the name of the Gateway API controller. GatewayControllerName string - *v1beta1.HTTPRoute + *gwapiv1.HTTPRoute - ParentRefs map[v1beta1.ParentReference]*RouteParentContext + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } // GRPCRouteContext wraps a GRPCRoute and provides helper methods for @@ -175,7 +175,7 @@ type GRPCRouteContext struct { *v1alpha2.GRPCRoute - ParentRefs map[v1beta1.ParentReference]*RouteParentContext + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } // TLSRouteContext wraps a TLSRoute and provides helper methods for @@ -186,7 +186,7 @@ type TLSRouteContext struct { *v1alpha2.TLSRoute - ParentRefs map[v1beta1.ParentReference]*RouteParentContext + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } // UDPRouteContext wraps a UDPRoute and provides helper methods for @@ -197,7 +197,7 @@ type UDPRouteContext struct { *v1alpha2.UDPRoute - ParentRefs map[v1beta1.ParentReference]*RouteParentContext + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } // TCPRouteContext wraps a TCPRoute and provides helper methods for @@ -208,18 +208,18 @@ type TCPRouteContext struct { *v1alpha2.TCPRoute - ParentRefs map[v1beta1.ParentReference]*RouteParentContext + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext } // GetRouteType returns the Kind of the Route object, HTTPRoute, // TLSRoute, TCPRoute, UDPRoute etc. -func GetRouteType(route RouteContext) v1beta1.Kind { +func GetRouteType(route RouteContext) gwapiv1.Kind { rv := reflect.ValueOf(route).Elem() - return v1beta1.Kind(rv.FieldByName("Kind").String()) + return gwapiv1.Kind(rv.FieldByName("Kind").String()) } -// TODO: [v1alpha2-v1beta1] This should not be required once all Route -// objects being implemented are of type v1beta1. +// TODO: [v1alpha2-gwapiv1] This should not be required once all Route +// objects being implemented are of type gwapiv1. // GetHostnames returns the hosts targeted by the Route object. func GetHostnames(route RouteContext) []string { rv := reflect.ValueOf(route).Elem() @@ -236,39 +236,39 @@ func GetHostnames(route RouteContext) []string { return hostnames } -// TODO: [v1alpha2-v1beta1] This should not be required once all Route -// objects being implemented are of type v1beta1. +// TODO: [v1alpha2-gwapiv1] This should not be required once all Route +// objects being implemented are of type gwapiv1. // GetParentReferences returns the ParentReference of the Route object. -func GetParentReferences(route RouteContext) []v1beta1.ParentReference { +func GetParentReferences(route RouteContext) []gwapiv1.ParentReference { rv := reflect.ValueOf(route).Elem() kind := rv.FieldByName("Kind").String() pr := rv.FieldByName("Spec").FieldByName("ParentRefs") if kind == KindHTTPRoute || kind == KindGRPCRoute { - return pr.Interface().([]v1beta1.ParentReference) + return pr.Interface().([]gwapiv1.ParentReference) } - parentReferences := make([]v1beta1.ParentReference, pr.Len()) + parentReferences := make([]gwapiv1.ParentReference, pr.Len()) for i := 0; i < len(parentReferences); i++ { - p := pr.Index(i).Interface().(v1beta1.ParentReference) + p := pr.Index(i).Interface().(gwapiv1.ParentReference) parentReferences[i] = UpgradeParentReference(p) } return parentReferences } // GetRouteStatus returns the RouteStatus object associated with the Route. -func GetRouteStatus(route RouteContext) *v1beta1.RouteStatus { +func GetRouteStatus(route RouteContext) *gwapiv1.RouteStatus { rv := reflect.ValueOf(route).Elem() - rs := rv.FieldByName("Status").FieldByName("RouteStatus").Interface().(v1beta1.RouteStatus) + rs := rv.FieldByName("Status").FieldByName("RouteStatus").Interface().(gwapiv1.RouteStatus) return &rs } // GetRouteParentContext returns RouteParentContext by using the Route // objects' ParentReference. -func GetRouteParentContext(route RouteContext, forParentRef v1beta1.ParentReference) *RouteParentContext { +func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentReference) *RouteParentContext { rv := reflect.ValueOf(route).Elem() pr := rv.FieldByName("ParentRefs") if pr.IsNil() { - mm := reflect.MakeMap(reflect.TypeOf(map[v1beta1.ParentReference]*RouteParentContext{})) + mm := reflect.MakeMap(reflect.TypeOf(map[gwapiv1.ParentReference]*RouteParentContext{})) pr.Set(mm) } @@ -282,10 +282,10 @@ func GetRouteParentContext(route RouteContext, forParentRef v1beta1.ParentRefere isHTTPRoute = true } - var parentRef *v1beta1.ParentReference + var parentRef *gwapiv1.ParentReference specParentRefs := rv.FieldByName("Spec").FieldByName("ParentRefs") for i := 0; i < specParentRefs.Len(); i++ { - p := specParentRefs.Index(i).Interface().(v1beta1.ParentReference) + p := specParentRefs.Index(i).Interface().(gwapiv1.ParentReference) up := p if !isHTTPRoute { up = UpgradeParentReference(p) @@ -307,10 +307,10 @@ func GetRouteParentContext(route RouteContext, forParentRef v1beta1.ParentRefere routeParentStatusIdx := -1 statusParents := rv.FieldByName("Status").FieldByName("Parents") for i := 0; i < statusParents.Len(); i++ { - p := statusParents.Index(i).FieldByName("ParentRef").Interface().(v1beta1.ParentReference) + p := statusParents.Index(i).FieldByName("ParentRef").Interface().(gwapiv1.ParentReference) if !isHTTPRoute { p = UpgradeParentReference(p) - defaultNamespace := v1beta1.Namespace(metav1.NamespaceDefault) + defaultNamespace := gwapiv1.Namespace(metav1.NamespaceDefault) if forParentRef.Namespace == nil { forParentRef.Namespace = &defaultNamespace } @@ -350,11 +350,11 @@ func GetRouteParentContext(route RouteContext, forParentRef v1beta1.ParentRefere // setting conditions and other status information on the associated // HTTPRoute, TLSRoute etc. type RouteParentContext struct { - *v1beta1.ParentReference + *gwapiv1.ParentReference - // TODO: [v1alpha2-v1beta1] This can probably be replaced with - // a single field pointing to *v1beta1.RouteStatus. - HTTPRoute *v1beta1.HTTPRoute + // TODO: [v1alpha2-gwapiv1] This can probably be replaced with + // a single field pointing to *gwapiv1.RouteStatus. + HTTPRoute *gwapiv1.HTTPRoute GRPCRoute *v1alpha2.GRPCRoute TLSRoute *v1alpha2.TLSRoute TCPRoute *v1alpha2.TCPRoute @@ -368,7 +368,7 @@ func (r *RouteParentContext) SetListeners(listeners ...*ListenerContext) { r.listeners = append(r.listeners, listeners...) } -func (r *RouteParentContext) SetCondition(route RouteContext, conditionType v1beta1.RouteConditionType, status metav1.ConditionStatus, reason v1beta1.RouteConditionReason, message string) { +func (r *RouteParentContext) SetCondition(route RouteContext, conditionType gwapiv1.RouteConditionType, status metav1.ConditionStatus, reason gwapiv1.RouteConditionReason, message string) { cond := metav1.Condition{ Type: string(conditionType), Status: status, @@ -406,7 +406,7 @@ func (r *RouteParentContext) ResetConditions(route RouteContext) { routeStatus.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0) } -func (r *RouteParentContext) HasCondition(route RouteContext, condType v1beta1.RouteConditionType, status metav1.ConditionStatus) bool { +func (r *RouteParentContext) HasCondition(route RouteContext, condType gwapiv1.RouteConditionType, status metav1.ConditionStatus) bool { var conditions []metav1.Condition routeStatus := GetRouteStatus(route) conditions = routeStatus.Parents[r.routeParentStatusIdx].Conditions diff --git a/internal/gatewayapi/contexts_test.go b/internal/gatewayapi/contexts_test.go index 3b1867b53de..7a6b1897113 100644 --- a/internal/gatewayapi/contexts_test.go +++ b/internal/gatewayapi/contexts_test.go @@ -10,17 +10,17 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) func TestContexts(t *testing.T) { - gateway := &v1beta1.Gateway{ + gateway := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Namespace: "envoy-gateway", Name: "gateway-1", }, - Spec: v1beta1.GatewaySpec{ - Listeners: []v1beta1.Listener{ + Spec: gwapiv1.GatewaySpec{ + Listeners: []gwapiv1.Listener{ { Name: "http", }, @@ -37,17 +37,17 @@ func TestContexts(t *testing.T) { lctx := gctx.listeners[0] require.NotNil(t, lctx) - lctx.SetCondition(v1beta1.ListenerConditionAccepted, metav1.ConditionFalse, v1beta1.ListenerReasonUnsupportedProtocol, "HTTPS protocol is not supported yet") + lctx.SetCondition(gwapiv1.ListenerConditionAccepted, metav1.ConditionFalse, gwapiv1.ListenerReasonUnsupportedProtocol, "HTTPS protocol is not supported yet") require.Len(t, gateway.Status.Listeners, 1) require.EqualValues(t, gateway.Status.Listeners[0].Name, "http") require.Len(t, gateway.Status.Listeners[0].Conditions, 1) - require.EqualValues(t, gateway.Status.Listeners[0].Conditions[0].Type, v1beta1.ListenerConditionAccepted) + require.EqualValues(t, gateway.Status.Listeners[0].Conditions[0].Type, gwapiv1.ListenerConditionAccepted) require.EqualValues(t, gateway.Status.Listeners[0].Conditions[0].Status, metav1.ConditionFalse) - require.EqualValues(t, gateway.Status.Listeners[0].Conditions[0].Reason, v1beta1.ListenerReasonUnsupportedProtocol) + require.EqualValues(t, gateway.Status.Listeners[0].Conditions[0].Reason, gwapiv1.ListenerReasonUnsupportedProtocol) require.EqualValues(t, gateway.Status.Listeners[0].Conditions[0].Message, "HTTPS protocol is not supported yet") - lctx.SetSupportedKinds(v1beta1.RouteGroupKind{Group: GroupPtr(v1beta1.GroupName), Kind: "HTTPRoute"}) + lctx.SetSupportedKinds(gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: "HTTPRoute"}) require.Len(t, gateway.Status.Listeners, 1) require.Len(t, gateway.Status.Listeners[0].SupportedKinds, 1) @@ -58,13 +58,13 @@ func TestContexts(t *testing.T) { } func TestContextsStaleListener(t *testing.T) { - gateway := &v1beta1.Gateway{ + gateway := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Namespace: "envoy-gateway", Name: "gateway-1", }, - Spec: v1beta1.GatewaySpec{ - Listeners: []v1beta1.Listener{ + Spec: gwapiv1.GatewaySpec{ + Listeners: []gwapiv1.Listener{ { Name: "https", }, @@ -73,13 +73,13 @@ func TestContextsStaleListener(t *testing.T) { }, }, }, - Status: v1beta1.GatewayStatus{ - Listeners: []v1beta1.ListenerStatus{ + Status: gwapiv1.GatewayStatus{ + Listeners: []gwapiv1.ListenerStatus{ { Name: "https", Conditions: []metav1.Condition{ { - Status: metav1.ConditionStatus(v1beta1.ListenerConditionProgrammed), + Status: metav1.ConditionStatus(gwapiv1.ListenerConditionProgrammed), }, }, }, @@ -87,7 +87,7 @@ func TestContextsStaleListener(t *testing.T) { Name: "http", Conditions: []metav1.Condition{ { - Status: metav1.ConditionStatus(v1beta1.ListenerConditionProgrammed), + Status: metav1.ConditionStatus(gwapiv1.ListenerConditionProgrammed), }, }, }, @@ -98,7 +98,7 @@ func TestContextsStaleListener(t *testing.T) { gCtx := &GatewayContext{Gateway: gateway} httpsListenerCtx := &ListenerContext{ - Listener: &v1beta1.Listener{ + Listener: &gwapiv1.Listener{ Name: "https", }, gateway: gateway, @@ -106,7 +106,7 @@ func TestContextsStaleListener(t *testing.T) { } httpListenerCtx := &ListenerContext{ - Listener: &v1beta1.Listener{ + Listener: &gwapiv1.Listener{ Name: "http", }, gateway: gateway, @@ -125,7 +125,7 @@ func TestContextsStaleListener(t *testing.T) { require.Len(t, gCtx.Status.Listeners, 2) - expectedListenerStatuses := []v1beta1.ListenerStatus{ + expectedListenerStatuses := []gwapiv1.ListenerStatus{ { Name: "https", }, @@ -142,7 +142,7 @@ func TestContextsStaleListener(t *testing.T) { // Ensure the listener status has been updated and the stale listener has been // removed. - expectedListenerStatus := []v1beta1.ListenerStatus{{Name: "https"}} + expectedListenerStatus := []gwapiv1.ListenerStatus{{Name: "https"}} require.EqualValues(t, expectedListenerStatus, gCtx.Gateway.Status.Listeners) // Ensure that the listeners within GatewayContext have been properly updated. diff --git a/internal/gatewayapi/envoypatchpolicy.go b/internal/gatewayapi/envoypatchpolicy.go index e5e9a2f33e0..6e790ff25db 100644 --- a/internal/gatewayapi/envoypatchpolicy.go +++ b/internal/gatewayapi/envoypatchpolicy.go @@ -10,8 +10,9 @@ import ( "sort" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + gwv1b1 "sigs.k8s.io/gateway-api/apis/v1" gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" @@ -27,18 +28,10 @@ func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.Envo for _, policy := range envoyPatchPolicies { policy := policy.DeepCopy() targetNs := policy.Spec.TargetRef.Namespace - if targetNs == nil { - // This status condition will not get updated in the resource because - // we dont have access to the IR yet, but it has been kept here in case we publish - // the status from this layer instead of the xds layer. - status.SetEnvoyPatchPolicyCondition(policy, - gwv1a2.PolicyConditionAccepted, - metav1.ConditionFalse, - gwv1a2.PolicyReasonInvalid, - "TargetRef.Namespace must be set", - ) - continue + // If empty, default to namespace of policy + if targetNs == nil { + targetNs = ptr.To(gwv1b1.Namespace(policy.Namespace)) } // Get the IR diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index 074c907ca1f..73dcf54ca7f 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -7,14 +7,12 @@ package gatewayapi import ( "fmt" - "net" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" ) @@ -25,12 +23,12 @@ type FiltersTranslator interface { var _ FiltersTranslator = (*Translator)(nil) type HTTPFiltersTranslator interface { - processURLRewriteFilter(rewrite *v1beta1.HTTPURLRewriteFilter, filterContext *HTTPFiltersContext) - processRedirectFilter(redirect *v1beta1.HTTPRequestRedirectFilter, filterContext *HTTPFiltersContext) - processRequestHeaderModifierFilter(headerModifier *v1beta1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) - processResponseHeaderModifierFilter(headerModifier *v1beta1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) - processRequestMirrorFilter(mirror *v1beta1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *Resources) - processExtensionRefHTTPFilter(extRef *v1beta1.LocalObjectReference, filterContext *HTTPFiltersContext, resources *Resources) + processURLRewriteFilter(rewrite *gwapiv1.HTTPURLRewriteFilter, filterContext *HTTPFiltersContext) + processRedirectFilter(redirect *gwapiv1.HTTPRequestRedirectFilter, filterContext *HTTPFiltersContext) + processRequestHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) + processResponseHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) + processRequestMirrorFilter(filterIdx int, mirror *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *Resources) + processExtensionRefHTTPFilter(extRef *gwapiv1.LocalObjectReference, filterContext *HTTPFiltersContext, resources *Resources) processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) } @@ -40,6 +38,7 @@ type HTTPFiltersContext struct { ParentRef *RouteParentContext Route RouteContext + RuleIdx int } // HTTPFilterIR contains the ir processing results. @@ -57,23 +56,21 @@ type HTTPFilterIR struct { Mirrors []*ir.RouteDestination - RequestAuthentication *ir.RequestAuthentication - RateLimit *ir.RateLimit - ExtensionRefs []*ir.UnstructuredRef } // ProcessHTTPFilters translates gateway api http filters to IRs. func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, route RouteContext, - filters []v1beta1.HTTPRouteFilter, + filters []gwapiv1.HTTPRouteFilter, + ruleIdx int, resources *Resources) *HTTPFiltersContext { httpFiltersContext := &HTTPFiltersContext{ ParentRef: parentRef, Route: route, + RuleIdx: ruleIdx, HTTPFilterIR: &HTTPFilterIR{}, } - for i := range filters { filter := filters[i] // If an invalid filter type has been configured then skip processing any more filters @@ -86,17 +83,17 @@ func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, } switch filter.Type { - case v1beta1.HTTPRouteFilterURLRewrite: + case gwapiv1.HTTPRouteFilterURLRewrite: t.processURLRewriteFilter(filter.URLRewrite, httpFiltersContext) - case v1beta1.HTTPRouteFilterRequestRedirect: + case gwapiv1.HTTPRouteFilterRequestRedirect: t.processRedirectFilter(filter.RequestRedirect, httpFiltersContext) - case v1beta1.HTTPRouteFilterRequestHeaderModifier: + case gwapiv1.HTTPRouteFilterRequestHeaderModifier: t.processRequestHeaderModifierFilter(filter.RequestHeaderModifier, httpFiltersContext) - case v1beta1.HTTPRouteFilterResponseHeaderModifier: + case gwapiv1.HTTPRouteFilterResponseHeaderModifier: t.processResponseHeaderModifierFilter(filter.ResponseHeaderModifier, httpFiltersContext) - case v1beta1.HTTPRouteFilterRequestMirror: - t.processRequestMirrorFilter(filter.RequestMirror, httpFiltersContext, resources) - case v1beta1.HTTPRouteFilterExtensionRef: + case gwapiv1.HTTPRouteFilterRequestMirror: + t.processRequestMirrorFilter(i, filter.RequestMirror, httpFiltersContext, resources) + case gwapiv1.HTTPRouteFilterExtensionRef: t.processExtensionRefHTTPFilter(filter.ExtensionRef, httpFiltersContext, resources) default: t.processUnsupportedHTTPFilter(string(filter.Type), httpFiltersContext) @@ -117,7 +114,6 @@ func (t *Translator) ProcessGRPCFilters(parentRef *RouteParentContext, HTTPFilterIR: &HTTPFilterIR{}, } - for i := range filters { filter := filters[i] // If an invalid filter type has been configured then skip processing any more filters @@ -135,7 +131,9 @@ func (t *Translator) ProcessGRPCFilters(parentRef *RouteParentContext, case v1alpha2.GRPCRouteFilterResponseHeaderModifier: t.processResponseHeaderModifierFilter(filter.ResponseHeaderModifier, httpFiltersContext) case v1alpha2.GRPCRouteFilterRequestMirror: - t.processRequestMirrorFilter(filter.RequestMirror, httpFiltersContext, resources) + t.processRequestMirrorFilter(i, filter.RequestMirror, httpFiltersContext, resources) + case v1alpha2.GRPCRouteFilterExtensionRef: + t.processExtensionRefHTTPFilter(filter.ExtensionRef, httpFiltersContext, resources) default: t.processUnsupportedHTTPFilter(string(filter.Type), httpFiltersContext) } @@ -145,13 +143,13 @@ func (t *Translator) ProcessGRPCFilters(parentRef *RouteParentContext, } func (t *Translator) processURLRewriteFilter( - rewrite *v1beta1.HTTPURLRewriteFilter, + rewrite *gwapiv1.HTTPURLRewriteFilter, filterContext *HTTPFiltersContext) { if filterContext.URLRewrite != nil { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "Cannot configure multiple urlRewrite filters for a single HTTPRouteRule", ) return @@ -166,9 +164,9 @@ func (t *Translator) processURLRewriteFilter( if rewrite.Hostname != nil { if err := t.validateHostname(string(*rewrite.Hostname)); err != nil { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, err.Error(), ) return @@ -179,13 +177,13 @@ func (t *Translator) processURLRewriteFilter( if rewrite.Path != nil { switch rewrite.Path.Type { - case v1beta1.FullPathHTTPPathModifier: + case gwapiv1.FullPathHTTPPathModifier: if rewrite.Path.ReplacePrefixMatch != nil { errMsg := "ReplacePrefixMatch cannot be set when rewrite path type is \"ReplaceFullPath\"" filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -193,9 +191,9 @@ func (t *Translator) processURLRewriteFilter( if rewrite.Path.ReplaceFullPath == nil { errMsg := "ReplaceFullPath must be set when rewrite path type is \"ReplaceFullPath\"" filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -205,13 +203,13 @@ func (t *Translator) processURLRewriteFilter( FullReplace: rewrite.Path.ReplaceFullPath, } } - case v1beta1.PrefixMatchHTTPPathModifier: + case gwapiv1.PrefixMatchHTTPPathModifier: if rewrite.Path.ReplaceFullPath != nil { errMsg := "ReplaceFullPath cannot be set when rewrite path type is \"ReplacePrefixMatch\"" filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -219,9 +217,9 @@ func (t *Translator) processURLRewriteFilter( if rewrite.Path.ReplacePrefixMatch == nil { errMsg := "ReplacePrefixMatch must be set when rewrite path type is \"ReplacePrefixMatch\"" filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -234,9 +232,9 @@ func (t *Translator) processURLRewriteFilter( default: errMsg := fmt.Sprintf("Rewrite path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported", rewrite.Path.Type) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -247,14 +245,14 @@ func (t *Translator) processURLRewriteFilter( } func (t *Translator) processRedirectFilter( - redirect *v1beta1.HTTPRequestRedirectFilter, + redirect *gwapiv1.HTTPRequestRedirectFilter, filterContext *HTTPFiltersContext) { // Can't have two redirects for the same route if filterContext.RedirectResponse != nil { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "Cannot configure multiple requestRedirect filters for a single HTTPRouteRule", ) return @@ -273,9 +271,9 @@ func (t *Translator) processRedirectFilter( } else { errMsg := fmt.Sprintf("Scheme: %s is unsupported, only 'https' and 'http' are supported", *redirect.Scheme) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -285,9 +283,9 @@ func (t *Translator) processRedirectFilter( if redirect.Hostname != nil { if err := t.validateHostname(string(*redirect.Hostname)); err != nil { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, err.Error(), ) } else { @@ -298,13 +296,13 @@ func (t *Translator) processRedirectFilter( if redirect.Path != nil { switch redirect.Path.Type { - case v1beta1.FullPathHTTPPathModifier: + case gwapiv1.FullPathHTTPPathModifier: if redirect.Path.ReplaceFullPath != nil { redir.Path = &ir.HTTPPathModifier{ FullReplace: redirect.Path.ReplaceFullPath, } } - case v1beta1.PrefixMatchHTTPPathModifier: + case gwapiv1.PrefixMatchHTTPPathModifier: if redirect.Path.ReplacePrefixMatch != nil { redir.Path = &ir.HTTPPathModifier{ PrefixMatchReplace: redirect.Path.ReplacePrefixMatch, @@ -313,9 +311,9 @@ func (t *Translator) processRedirectFilter( default: errMsg := fmt.Sprintf("Redirect path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported", redirect.Path.Type) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -330,9 +328,9 @@ func (t *Translator) processRedirectFilter( } else { errMsg := fmt.Sprintf("Status code %d is invalid, only 302 and 301 are supported", redirectCode) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) return @@ -348,7 +346,7 @@ func (t *Translator) processRedirectFilter( } func (t *Translator) processRequestHeaderModifierFilter( - headerModifier *v1beta1.HTTPHeaderFilter, + headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) { // Make sure the header modifier config actually exists if headerModifier == nil { @@ -365,9 +363,9 @@ func (t *Translator) processRequestHeaderModifierFilter( emptyFilterConfig = false if addHeader.Name == "" { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "RequestHeaderModifier Filter cannot add a header with an empty name", ) // try to process the rest of the headers and produce a valid config. @@ -376,9 +374,9 @@ func (t *Translator) processRequestHeaderModifierFilter( // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names if strings.Contains(string(addHeader.Name), "/") || strings.Contains(string(addHeader.Name), ":") { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, fmt.Sprintf("RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)), ) continue @@ -416,9 +414,9 @@ func (t *Translator) processRequestHeaderModifierFilter( if setHeader.Name == "" { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "RequestHeaderModifier Filter cannot set a header with an empty name", ) continue @@ -426,9 +424,9 @@ func (t *Translator) processRequestHeaderModifierFilter( // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names if strings.Contains(string(setHeader.Name), "/") || strings.Contains(string(setHeader.Name), ":") { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, fmt.Sprintf("RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)), ) continue @@ -466,9 +464,9 @@ func (t *Translator) processRequestHeaderModifierFilter( for _, removedHeader := range headersToRemove { if removedHeader == "" { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "RequestHeaderModifier Filter cannot remove a header with an empty name", ) continue @@ -492,16 +490,16 @@ func (t *Translator) processRequestHeaderModifierFilter( // Update the status if the filter failed to configure any valid headers to add/remove if len(filterContext.AddRequestHeaders) == 0 && len(filterContext.RemoveRequestHeaders) == 0 && !emptyFilterConfig { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "RequestHeaderModifier Filter did not provide valid configuration to add/set/remove any headers", ) } } func (t *Translator) processResponseHeaderModifierFilter( - headerModifier *v1beta1.HTTPHeaderFilter, + headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) { // Make sure the header modifier config actually exists if headerModifier == nil { @@ -518,9 +516,9 @@ func (t *Translator) processResponseHeaderModifierFilter( emptyFilterConfig = false if addHeader.Name == "" { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "ResponseHeaderModifier Filter cannot add a header with an empty name", ) // try to process the rest of the headers and produce a valid config. @@ -529,9 +527,9 @@ func (t *Translator) processResponseHeaderModifierFilter( // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names if strings.Contains(string(addHeader.Name), "/") || strings.Contains(string(addHeader.Name), ":") { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, fmt.Sprintf("ResponseHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)), ) continue @@ -569,9 +567,9 @@ func (t *Translator) processResponseHeaderModifierFilter( if setHeader.Name == "" { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "ResponseHeaderModifier Filter cannot set a header with an empty name", ) continue @@ -579,9 +577,9 @@ func (t *Translator) processResponseHeaderModifierFilter( // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names if strings.Contains(string(setHeader.Name), "/") || strings.Contains(string(setHeader.Name), ":") { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, fmt.Sprintf("ResponseHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)), ) continue @@ -619,9 +617,9 @@ func (t *Translator) processResponseHeaderModifierFilter( for _, removedHeader := range headersToRemove { if removedHeader == "" { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "ResponseHeaderModifier Filter cannot remove a header with an empty name", ) continue @@ -646,137 +644,21 @@ func (t *Translator) processResponseHeaderModifierFilter( // Update the status if the filter failed to configure any valid headers to add/remove if len(filterContext.AddResponseHeaders) == 0 && len(filterContext.RemoveResponseHeaders) == 0 && !emptyFilterConfig { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "ResponseHeaderModifier Filter did not provide valid configuration to add/set/remove any headers", ) } } -func (t *Translator) processExtensionRefHTTPFilter(extFilter *v1beta1.LocalObjectReference, filterContext *HTTPFiltersContext, resources *Resources) { +func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjectReference, filterContext *HTTPFiltersContext, resources *Resources) { // Make sure the config actually exists. if extFilter == nil { return } filterNs := filterContext.Route.GetNamespace() - // Set the filter context and return early if a matching AuthenticationFilter is found. - if string(extFilter.Kind) == egv1a1.KindAuthenticationFilter { - for _, authenFilter := range resources.AuthenticationFilters { - if authenFilter.Namespace == filterNs && - authenFilter.Name == string(extFilter.Name) { - filterContext.HTTPFilterIR.RequestAuthentication = &ir.RequestAuthentication{ - JWT: &ir.JwtRequestAuthentication{ - Providers: authenFilter.Spec.JwtProviders, - }, - } - return - } - } - } - - // Set the filter context and return early if a matching RateLimitFilter is found. - if string(extFilter.Kind) == egv1a1.KindRateLimitFilter { - for _, rateLimitFilter := range resources.RateLimitFilters { - if rateLimitFilter.Namespace == filterNs && - rateLimitFilter.Name == string(extFilter.Name) { - if rateLimitFilter.Spec.Global == nil { - errMsg := fmt.Sprintf("Global configuration empty for RateLimitFilter: %s/%s", filterNs, - extFilter.Name) - t.processUnresolvedHTTPFilter(errMsg, filterContext) - return - } - if !t.GlobalRateLimitEnabled { - errMsg := fmt.Sprintf("Enable Ratelimit in the EnvoyGateway config to configure RateLimitFilter: %s/%s", - filterNs, extFilter.Name) - t.processUnresolvedHTTPFilter(errMsg, filterContext) - return - } - rateLimit := &ir.RateLimit{ - Global: &ir.GlobalRateLimit{ - Rules: make([]*ir.RateLimitRule, len(rateLimitFilter.Spec.Global.Rules)), - }, - } - rules := rateLimit.Global.Rules - for i, rule := range rateLimitFilter.Spec.Global.Rules { - rules[i] = &ir.RateLimitRule{ - Limit: &ir.RateLimitValue{ - Requests: rule.Limit.Requests, - Unit: ir.RateLimitUnit(rule.Limit.Unit), - }, - HeaderMatches: make([]*ir.StringMatch, 0), - } - for _, match := range rule.ClientSelectors { - for _, header := range match.Headers { - switch { - case header.Type == nil && header.Value != nil: - fallthrough - case *header.Type == egv1a1.HeaderMatchExact && header.Value != nil: - m := &ir.StringMatch{ - Name: header.Name, - Exact: header.Value, - } - rules[i].HeaderMatches = append(rules[i].HeaderMatches, m) - case *header.Type == egv1a1.HeaderMatchRegularExpression && header.Value != nil: - m := &ir.StringMatch{ - Name: header.Name, - SafeRegex: header.Value, - } - rules[i].HeaderMatches = append(rules[i].HeaderMatches, m) - case *header.Type == egv1a1.HeaderMatchDistinct && header.Value == nil: - m := &ir.StringMatch{ - Name: header.Name, - Distinct: true, - } - rules[i].HeaderMatches = append(rules[i].HeaderMatches, m) - default: - // set negative status condition. - errMsg := fmt.Sprintf("Unable to translate RateLimitFilter. Either the header.Type is not valid or the header is missing a value: %s/%s", filterNs, - extFilter.Name) - t.processUnresolvedHTTPFilter(errMsg, filterContext) - return - } - } - - if match.SourceCIDR != nil || match.SourceIP != nil { - sourceCIDR := "" - // distinct means that each IP Address within the specified Source IP CIDR is treated as a - // distinct client selector and uses a separate rate limit bucket/counter. - distinct := false - if match.SourceCIDR != nil { - sourceCIDR = match.SourceCIDR.Value - if match.SourceCIDR.Type != nil && *match.SourceCIDR.Type == egv1a1.SourceMatchDistinct { - distinct = true - } - } else { - sourceCIDR = *match.SourceIP - } - - ip, ipn, err := net.ParseCIDR(sourceCIDR) - if err != nil { - errMsg := fmt.Sprintf("Unable to translate RateLimitFilter: %s/%s", filterNs, - extFilter.Name) - t.processUnresolvedHTTPFilter(errMsg, filterContext) - return - } - - mask, _ := ipn.Mask.Size() - rules[i].CIDRMatch = &ir.CIDRMatch{ - CIDR: ipn.String(), - IPv6: ip.To4() == nil, - MaskLen: mask, - Distinct: distinct, - } - } - } - } - filterContext.HTTPFilterIR.RateLimit = rateLimit - return - } - } - } - // This list of resources will be empty unless an extension is loaded (and introduces resources) for _, res := range resources.ExtensionRefFilters { if res.GetKind() == string(extFilter.Kind) && res.GetName() == string(extFilter.Name) && res.GetNamespace() == filterNs { @@ -808,7 +690,8 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *v1beta1.LocalObjec } func (t *Translator) processRequestMirrorFilter( - mirrorFilter *v1beta1.HTTPRequestMirrorFilter, + filterIdx int, + mirrorFilter *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *Resources) { @@ -821,7 +704,7 @@ func (t *Translator) processRequestMirrorFilter( // Wrap the filter's BackendObjectReference into a BackendRef so we can use existing tooling to check it weight := int32(1) - mirrorBackendRef := v1beta1.BackendRef{ + mirrorBackendRef := gwapiv1.BackendRef{ BackendObjectReference: mirrorBackend, Weight: &weight, } @@ -834,36 +717,26 @@ func (t *Translator) processRequestMirrorFilter( return } - mirrorDests, _ := t.processRouteDestinations(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources) - - // Only add missing mirror destinations - for _, mirrorDest := range mirrorDests { - var found bool - for _, mirror := range filterContext.Mirrors { - if mirror != nil { - if mirror.Host == mirrorDest.Host && mirror.Port == mirrorDest.Port { - found = true - } - } - } + ds, _ := t.processDestination(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources) - if !found { - filterContext.Mirrors = append(filterContext.Mirrors, mirrorDest) - } + newMirror := &ir.RouteDestination{ + Name: fmt.Sprintf("%s-mirror-%d", irRouteDestinationName(filterContext.Route, filterContext.RuleIdx), filterIdx), + Settings: []*ir.DestinationSetting{ds}, } + filterContext.Mirrors = append(filterContext.Mirrors, newMirror) } func (t *Translator) processUnresolvedHTTPFilter(errMsg string, filterContext *HTTPFiltersContext) { filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.RouteReasonBackendNotFound, + gwapiv1.RouteReasonBackendNotFound, errMsg, ) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) filterContext.DirectResponse = &ir.DirectResponse{ @@ -875,9 +748,9 @@ func (t *Translator) processUnresolvedHTTPFilter(errMsg string, filterContext *H func (t *Translator) processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) { errMsg := fmt.Sprintf("Unsupported filter type: %s", filterType) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) filterContext.DirectResponse = &ir.DirectResponse{ @@ -889,9 +762,9 @@ func (t *Translator) processUnsupportedHTTPFilter(filterType string, filterConte func (t *Translator) processInvalidHTTPFilter(filterType string, filterContext *HTTPFiltersContext, err error) { errMsg := fmt.Sprintf("Invalid filter %s: %v", filterType, err) filterContext.ParentRef.SetCondition(filterContext.Route, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, errMsg, ) filterContext.DirectResponse = &ir.DirectResponse{ diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index 8ac0c1c05ba..1c5392262a9 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -13,10 +13,9 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" ) @@ -29,48 +28,36 @@ const ( ) type protocolPort struct { - protocol v1beta1.ProtocolType + protocol gwapiv1.ProtocolType port int32 } -func GroupPtr(name string) *v1beta1.Group { - group := v1beta1.Group(name) +func GroupPtr(name string) *gwapiv1.Group { + group := gwapiv1.Group(name) return &group } -func KindPtr(name string) *v1beta1.Kind { - kind := v1beta1.Kind(name) +func KindPtr(name string) *gwapiv1.Kind { + kind := gwapiv1.Kind(name) return &kind } -func NamespacePtr(name string) *v1beta1.Namespace { - namespace := v1beta1.Namespace(name) +func NamespacePtr(name string) *gwapiv1.Namespace { + namespace := gwapiv1.Namespace(name) return &namespace } -func FromNamespacesPtr(fromNamespaces v1beta1.FromNamespaces) *v1beta1.FromNamespaces { +func FromNamespacesPtr(fromNamespaces gwapiv1.FromNamespaces) *gwapiv1.FromNamespaces { return &fromNamespaces } -func SectionNamePtr(name string) *v1beta1.SectionName { - sectionName := v1beta1.SectionName(name) +func SectionNamePtr(name string) *gwapiv1.SectionName { + sectionName := gwapiv1.SectionName(name) return §ionName } -func TLSModeTypePtr(mode v1beta1.TLSModeType) *v1beta1.TLSModeType { - return &mode -} - -func StringPtr(val string) *string { - return &val -} - -func Int32Ptr(val int32) *int32 { - return &val -} - -func PortNumPtr(val int32) *v1beta1.PortNumber { - portNum := v1beta1.PortNumber(val) +func PortNumPtr(val int32) *gwapiv1.PortNumber { + portNum := gwapiv1.PortNumber(val) return &portNum } @@ -79,15 +66,7 @@ func ObjectNamePtr(val string) *v1alpha2.ObjectName { return &objectName } -func PathMatchTypePtr(pType v1beta1.PathMatchType) *v1beta1.PathMatchType { - return &pType -} - -func GatewayAddressTypePtr(addr v1beta1.AddressType) *v1beta1.AddressType { - return &addr -} - -func PathMatchTypeDerefOr(matchType *v1beta1.PathMatchType, defaultType v1beta1.PathMatchType) v1beta1.PathMatchType { +func PathMatchTypeDerefOr(matchType *gwapiv1.PathMatchType, defaultType gwapiv1.PathMatchType) gwapiv1.PathMatchType { if matchType != nil { return *matchType } @@ -101,41 +80,48 @@ func GRPCMethodMatchTypeDerefOr(matchType *v1alpha2.GRPCMethodMatchType, default return defaultType } -func HeaderMatchTypeDerefOr(matchType *v1beta1.HeaderMatchType, defaultType v1beta1.HeaderMatchType) v1beta1.HeaderMatchType { +func HeaderMatchTypeDerefOr(matchType *gwapiv1.HeaderMatchType, defaultType gwapiv1.HeaderMatchType) gwapiv1.HeaderMatchType { if matchType != nil { return *matchType } return defaultType } -func QueryParamMatchTypeDerefOr(matchType *v1beta1.QueryParamMatchType, - defaultType v1beta1.QueryParamMatchType) v1beta1.QueryParamMatchType { +func QueryParamMatchTypeDerefOr(matchType *gwapiv1.QueryParamMatchType, + defaultType gwapiv1.QueryParamMatchType) gwapiv1.QueryParamMatchType { if matchType != nil { return *matchType } return defaultType } -func NamespaceDerefOr(namespace *v1beta1.Namespace, defaultNamespace string) string { +func NamespaceDerefOr(namespace *gwapiv1.Namespace, defaultNamespace string) string { if namespace != nil && *namespace != "" { return string(*namespace) } return defaultNamespace } -func GroupDerefOr(group *v1beta1.Group, defaultGroup string) string { +func GroupDerefOr(group *gwapiv1.Group, defaultGroup string) string { if group != nil && *group != "" { return string(*group) } return defaultGroup } +func KindDerefOr(kind *gwapiv1.Kind, defaultKind string) string { + if kind != nil && *kind != "" { + return string(*kind) + } + return defaultKind +} + // IsRefToGateway returns whether the provided parent ref is a reference // to a Gateway with the given namespace/name, irrespective of whether a // section/listener name has been specified (i.e. a parent ref to a listener // on the specified gateway will return "true"). -func IsRefToGateway(parentRef v1beta1.ParentReference, gateway types.NamespacedName) bool { - if parentRef.Group != nil && string(*parentRef.Group) != v1beta1.GroupName { +func IsRefToGateway(parentRef gwapiv1.ParentReference, gateway types.NamespacedName) bool { + if parentRef.Group != nil && string(*parentRef.Group) != gwapiv1.GroupName { return false } @@ -154,7 +140,7 @@ func IsRefToGateway(parentRef v1beta1.ParentReference, gateway types.NamespacedN // in the given list, and if so, a list of the Listeners within that Gateway that // are included by the parent ref (either one specific Listener, or all Listeners // in the Gateway, depending on whether section name is specified or not). -func GetReferencedListeners(parentRef v1beta1.ParentReference, gateways []*GatewayContext) (bool, []*ListenerContext) { +func GetReferencedListeners(parentRef gwapiv1.ParentReference, gateways []*GatewayContext) (bool, []*ListenerContext) { var selectsGateway bool var referencedListeners []*ListenerContext @@ -188,30 +174,24 @@ func HasReadyListener(listeners []*ListenerContext) bool { } // ValidateHTTPRouteFilter validates the provided filter within HTTPRoute. -func ValidateHTTPRouteFilter(filter *v1beta1.HTTPRouteFilter, extGKs ...schema.GroupKind) error { +func ValidateHTTPRouteFilter(filter *gwapiv1.HTTPRouteFilter, extGKs ...schema.GroupKind) error { switch { case filter == nil: return errors.New("filter is nil") - case filter.Type == v1beta1.HTTPRouteFilterRequestMirror || - filter.Type == v1beta1.HTTPRouteFilterURLRewrite || - filter.Type == v1beta1.HTTPRouteFilterRequestRedirect || - filter.Type == v1beta1.HTTPRouteFilterRequestHeaderModifier || - filter.Type == v1beta1.HTTPRouteFilterResponseHeaderModifier: + case filter.Type == gwapiv1.HTTPRouteFilterRequestMirror || + filter.Type == gwapiv1.HTTPRouteFilterURLRewrite || + filter.Type == gwapiv1.HTTPRouteFilterRequestRedirect || + filter.Type == gwapiv1.HTTPRouteFilterRequestHeaderModifier || + filter.Type == gwapiv1.HTTPRouteFilterResponseHeaderModifier: return nil - case filter.Type == v1beta1.HTTPRouteFilterExtensionRef: + case filter.Type == gwapiv1.HTTPRouteFilterExtensionRef: switch { case filter.ExtensionRef == nil: return errors.New("extensionRef field must be specified for an extended filter") - case string(filter.ExtensionRef.Group) == egv1a1.GroupVersion.Group && - string(filter.ExtensionRef.Kind) == egv1a1.KindAuthenticationFilter: - return nil - case string(filter.ExtensionRef.Group) == egv1a1.GroupVersion.Group && - string(filter.ExtensionRef.Kind) == egv1a1.KindRateLimitFilter: - return nil default: for _, gk := range extGKs { - if filter.ExtensionRef.Group == v1beta1.Group(gk.Group) && - filter.ExtensionRef.Kind == v1beta1.Kind(gk.Kind) { + if filter.ExtensionRef.Group == gwapiv1.Group(gk.Group) && + filter.ExtensionRef.Kind == gwapiv1.Kind(gk.Kind) { return nil } } @@ -222,22 +202,6 @@ func ValidateHTTPRouteFilter(filter *v1beta1.HTTPRouteFilter, extGKs ...schema.G } } -// IsAuthnHTTPFilter returns true if the provided filter is an AuthenticationFilter. -func IsAuthnHTTPFilter(filter *v1beta1.HTTPRouteFilter) bool { - return filter.Type == v1beta1.HTTPRouteFilterExtensionRef && - filter.ExtensionRef != nil && - string(filter.ExtensionRef.Group) == egv1a1.GroupVersion.Group && - string(filter.ExtensionRef.Kind) == egv1a1.KindAuthenticationFilter -} - -// IsRateLimitHTTPFilter returns true if the provided filter is a RateLimitFilter. -func IsRateLimitHTTPFilter(filter *v1beta1.HTTPRouteFilter) bool { - return filter.Type == v1beta1.HTTPRouteFilterExtensionRef && - filter.ExtensionRef != nil && - string(filter.ExtensionRef.Group) == egv1a1.GroupVersion.Group && - string(filter.ExtensionRef.Kind) == egv1a1.KindRateLimitFilter -} - // ValidateGRPCRouteFilter validates the provided filter within GRPCRoute. func ValidateGRPCRouteFilter(filter *v1alpha2.GRPCRouteFilter, extGKs ...schema.GroupKind) error { switch { @@ -248,16 +212,18 @@ func ValidateGRPCRouteFilter(filter *v1alpha2.GRPCRouteFilter, extGKs ...schema. filter.Type == v1alpha2.GRPCRouteFilterResponseHeaderModifier: return nil case filter.Type == v1alpha2.GRPCRouteFilterExtensionRef: - if filter.ExtensionRef == nil { + switch { + case filter.ExtensionRef == nil: return errors.New("extensionRef field must be specified for an extended filter") - } - for _, gk := range extGKs { - if filter.ExtensionRef.Group == v1beta1.Group(gk.Group) && - filter.ExtensionRef.Kind == v1beta1.Kind(gk.Kind) { - return nil + default: + for _, gk := range extGKs { + if filter.ExtensionRef.Group == gwapiv1.Group(gk.Group) && + filter.ExtensionRef.Kind == gwapiv1.Kind(gk.Kind) { + return nil + } } + return fmt.Errorf("unknown kind %s/%s", string(filter.ExtensionRef.Group), string(filter.ExtensionRef.Kind)) } - return fmt.Errorf("unknown kind %s/%s", string(filter.ExtensionRef.Group), string(filter.ExtensionRef.Kind)) default: return fmt.Errorf("unsupported filter type %v", filter.Type) } @@ -272,6 +238,12 @@ func GatewayOwnerLabels(namespace, name string) map[string]string { } } +// GatewayClassOwnerLabel returns the GatewayCLass Owner label using +// the provided name as the value. +func GatewayClassOwnerLabel(name string) map[string]string { + return map[string]string{OwningGatewayClassLabel: name} +} + // servicePortToContainerPort translates a service port into an ephemeral // container port. func servicePortToContainerPort(servicePort int32) int32 { @@ -287,7 +259,7 @@ func servicePortToContainerPort(servicePort int32) int32 { // computeHosts returns a list of the intersecting hostnames between the route // and the listener. -func computeHosts(routeHostnames []string, listenerHostname *v1beta1.Hostname) []string { +func computeHosts(routeHostnames []string, listenerHostname *gwapiv1.Hostname) []string { var listenerHostnameVal string if listenerHostname != nil { listenerHostnameVal = string(*listenerHostname) @@ -363,9 +335,9 @@ func containsPort(ports []*protocolPort, port *protocolPort) bool { // layer4Protocol returns listener L4 protocol and listen protocol level func layer4Protocol(protocolPort *protocolPort) (string, string) { switch protocolPort.protocol { - case v1beta1.HTTPProtocolType, v1beta1.HTTPSProtocolType, v1beta1.TLSProtocolType: + case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TLSProtocolType: return TCPProtocol, L7Protocol - case v1beta1.TCPProtocolType: + case gwapiv1.TCPProtocolType: return TCPProtocol, L4Protocol default: return UDPProtocol, L4Protocol @@ -405,8 +377,16 @@ func irUDPListenerName(listener *ListenerContext, udpRoute *UDPRouteContext) str return fmt.Sprintf("%s/%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name, udpRoute.Name) } -func routeName(route RouteContext, ruleIdx, matchIdx int) string { - return fmt.Sprintf("%s/%s/rule/%d/match/%d", route.GetNamespace(), route.GetName(), ruleIdx, matchIdx) +func irRoutePrefix(route RouteContext) string { + return fmt.Sprintf("%s/%s/%s", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName()) +} + +func irRouteName(route RouteContext, ruleIdx, matchIdx int) string { + return fmt.Sprintf("%s/rule/%d/match/%d", irRoutePrefix(route), ruleIdx, matchIdx) +} + +func irRouteDestinationName(route RouteContext, ruleIdx int) string { + return fmt.Sprintf("%s/rule/%d", irRoutePrefix(route), ruleIdx) } func irTLSConfigs(tlsSecrets []*v1.Secret) []*ir.TLSListenerConfig { @@ -429,7 +409,11 @@ func irTLSListenerConfigName(secret *v1.Secret) string { return fmt.Sprintf("%s-%s", secret.Namespace, secret.Name) } -func protocolSliceToStringSlice(protocols []v1beta1.ProtocolType) []string { +func isMergeGatewaysEnabled(resources *Resources) bool { + return resources.EnvoyProxy != nil && resources.EnvoyProxy.Spec.MergeGateways != nil && *resources.EnvoyProxy.Spec.MergeGateways +} + +func protocolSliceToStringSlice(protocols []gwapiv1.ProtocolType) []string { var protocolStrings []string for _, protocol := range protocols { protocolStrings = append(protocolStrings, string(protocol)) diff --git a/internal/gatewayapi/helpers_test.go b/internal/gatewayapi/helpers_test.go index 840bd82939a..6c8f6623067 100644 --- a/internal/gatewayapi/helpers_test.go +++ b/internal/gatewayapi/helpers_test.go @@ -16,10 +16,8 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime/schema" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) func TestValidateGRPCFilterRef(t *testing.T) { @@ -53,7 +51,7 @@ func TestValidateGRPCFilterRef(t *testing.T) { name: "valid extension resource", filter: &gwapiv1a2.GRPCRouteFilter{ Type: gwapiv1a2.GRPCRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ + ExtensionRef: &gwapiv1.LocalObjectReference{ Group: "example.io", Kind: "Foo", Name: "test", @@ -65,7 +63,7 @@ func TestValidateGRPCFilterRef(t *testing.T) { name: "unsupported extended filter", filter: &gwapiv1a2.GRPCRouteFilter{ Type: gwapiv1a2.GRPCRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ + ExtensionRef: &gwapiv1.LocalObjectReference{ Group: "UnsupportedGroup", Kind: "UnsupportedKind", Name: "test", @@ -84,7 +82,7 @@ func TestValidateGRPCFilterRef(t *testing.T) { name: "invalid filter type", filter: &gwapiv1a2.GRPCRouteFilter{ Type: "Invalid", - ExtensionRef: &gwapiv1b1.LocalObjectReference{ + ExtensionRef: &gwapiv1.LocalObjectReference{ Group: "example.io", Kind: "Foo", Name: "test", @@ -109,42 +107,42 @@ func TestValidateGRPCFilterRef(t *testing.T) { func TestValidateHTTPFilterRef(t *testing.T) { testCases := []struct { name string - filter *gwapiv1b1.HTTPRouteFilter + filter *gwapiv1.HTTPRouteFilter expected bool }{ { name: "request mirror filter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterRequestMirror, + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterRequestMirror, }, expected: true, }, { name: "url rewrite filter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterURLRewrite, + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterURLRewrite, }, expected: true, }, { name: "request header modifier filter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterRequestHeaderModifier, + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterRequestHeaderModifier, }, expected: true, }, { name: "request redirect filter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterRequestRedirect, + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterRequestRedirect, }, expected: true, }, { name: "unsupported extended filter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterExtensionRef, + ExtensionRef: &gwapiv1.LocalObjectReference{ Group: "UnsupportedGroup", Kind: "UnsupportedKind", Name: "test", @@ -154,64 +152,16 @@ func TestValidateHTTPFilterRef(t *testing.T) { }, { name: "extended filter with missing reference", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - }, - expected: false, - }, - { - name: "invalid authenticationfilter group", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: "UnsupportedGroup", - Kind: egv1a1.KindAuthenticationFilter, - Name: "test", - }, + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterExtensionRef, }, expected: false, }, - { - name: "invalid authenticationfilter kind", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: "UnsupportedKind", - Name: "test", - }, - }, - expected: false, - }, - { - name: "valid authenticationfilter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: egv1a1.KindAuthenticationFilter, - Name: "test", - }, - }, - expected: true, - }, - { - name: "valid rateLimitfilter", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: egv1a1.KindRateLimitFilter, - Name: "test", - }, - }, - expected: true, - }, { name: "valid extension resource", - filter: &gwapiv1b1.HTTPRouteFilter{ - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ + filter: &gwapiv1.HTTPRouteFilter{ + Type: gwapiv1.HTTPRouteFilterExtensionRef, + ExtensionRef: &gwapiv1.LocalObjectReference{ Group: "example.io", Kind: "Foo", Name: "test", @@ -221,9 +171,9 @@ func TestValidateHTTPFilterRef(t *testing.T) { }, { name: "invalid filter type", - filter: &gwapiv1b1.HTTPRouteFilter{ + filter: &gwapiv1.HTTPRouteFilter{ Type: "Invalid", - ExtensionRef: &gwapiv1b1.LocalObjectReference{ + ExtensionRef: &gwapiv1.LocalObjectReference{ Group: "example.io", Kind: "Foo", Name: "test", diff --git a/internal/gatewayapi/helpers_v1alpha2.go b/internal/gatewayapi/helpers_v1alpha2.go index 65b31ad74af..afa80413175 100644 --- a/internal/gatewayapi/helpers_v1alpha2.go +++ b/internal/gatewayapi/helpers_v1alpha2.go @@ -12,49 +12,49 @@ package gatewayapi import ( - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) -// TODO: [v1alpha2-v1beta1] -// This file can be removed once TLSRoute graduates to v1beta1. +// TODO: [gwapiv1a2-gwapiv1] +// This file can be removed once TLSRoute graduates to gwapiv1. -func GroupPtrV1Alpha2(group string) *v1alpha2.Group { - gwGroup := v1alpha2.Group(group) +func GroupPtrV1Alpha2(group string) *gwapiv1a2.Group { + gwGroup := gwapiv1a2.Group(group) return &gwGroup } -func KindPtrV1Alpha2(kind string) *v1alpha2.Kind { - gwKind := v1alpha2.Kind(kind) +func KindPtrV1Alpha2(kind string) *gwapiv1a2.Kind { + gwKind := gwapiv1a2.Kind(kind) return &gwKind } -func NamespacePtrV1Alpha2(namespace string) *v1alpha2.Namespace { - gwNamespace := v1alpha2.Namespace(namespace) +func NamespacePtrV1Alpha2(namespace string) *gwapiv1a2.Namespace { + gwNamespace := gwapiv1a2.Namespace(namespace) return &gwNamespace } -func SectionNamePtrV1Alpha2(sectionName string) *v1alpha2.SectionName { - gwSectionName := v1alpha2.SectionName(sectionName) +func SectionNamePtrV1Alpha2(sectionName string) *gwapiv1a2.SectionName { + gwSectionName := gwapiv1a2.SectionName(sectionName) return &gwSectionName } -func PortNumPtrV1Alpha2(port int) *v1alpha2.PortNumber { - pn := v1alpha2.PortNumber(port) +func PortNumPtrV1Alpha2(port int) *gwapiv1a2.PortNumber { + pn := gwapiv1a2.PortNumber(port) return &pn } -func UpgradeParentReferences(old []v1alpha2.ParentReference) []v1beta1.ParentReference { - newParentReferences := make([]v1beta1.ParentReference, len(old)) +func UpgradeParentReferences(old []gwapiv1a2.ParentReference) []gwapiv1.ParentReference { + newParentReferences := make([]gwapiv1.ParentReference, len(old)) for i, o := range old { newParentReferences[i] = UpgradeParentReference(o) } return newParentReferences } -// UpgradeParentReference converts v1alpha2.ParentReference to v1beta1.ParentReference -func UpgradeParentReference(old v1alpha2.ParentReference) v1beta1.ParentReference { - upgraded := v1beta1.ParentReference{} +// UpgradeParentReference converts gwapiv1a2.ParentReference to gwapiv1.ParentReference +func UpgradeParentReference(old gwapiv1a2.ParentReference) gwapiv1.ParentReference { + upgraded := gwapiv1.ParentReference{} if old.Group != nil { upgraded.Group = GroupPtr(string(*old.Group)) @@ -81,8 +81,8 @@ func UpgradeParentReference(old v1alpha2.ParentReference) v1beta1.ParentReferenc return upgraded } -func DowngradeParentReference(old v1beta1.ParentReference) v1alpha2.ParentReference { - downgraded := v1alpha2.ParentReference{} +func DowngradeParentReference(old gwapiv1.ParentReference) gwapiv1a2.ParentReference { + downgraded := gwapiv1a2.ParentReference{} if old.Group != nil { downgraded.Group = GroupPtrV1Alpha2(string(*old.Group)) @@ -109,11 +109,11 @@ func DowngradeParentReference(old v1beta1.ParentReference) v1alpha2.ParentRefere return downgraded } -func UpgradeRouteParentStatuses(routeParentStatuses []v1alpha2.RouteParentStatus) []v1beta1.RouteParentStatus { - var res []v1beta1.RouteParentStatus +func UpgradeRouteParentStatuses(routeParentStatuses []gwapiv1a2.RouteParentStatus) []gwapiv1.RouteParentStatus { + var res []gwapiv1.RouteParentStatus for _, rps := range routeParentStatuses { - res = append(res, v1beta1.RouteParentStatus{ + res = append(res, gwapiv1.RouteParentStatus{ ParentRef: UpgradeParentReference(rps.ParentRef), ControllerName: rps.ControllerName, Conditions: rps.Conditions, @@ -123,11 +123,11 @@ func UpgradeRouteParentStatuses(routeParentStatuses []v1alpha2.RouteParentStatus return res } -func DowngradeRouteParentStatuses(routeParentStatuses []v1beta1.RouteParentStatus) []v1alpha2.RouteParentStatus { - var res []v1alpha2.RouteParentStatus +func DowngradeRouteParentStatuses(routeParentStatuses []gwapiv1.RouteParentStatus) []gwapiv1a2.RouteParentStatus { + var res []gwapiv1a2.RouteParentStatus for _, rps := range routeParentStatuses { - res = append(res, v1alpha2.RouteParentStatus{ + res = append(res, gwapiv1a2.RouteParentStatus{ ParentRef: DowngradeParentReference(rps.ParentRef), ControllerName: rps.ControllerName, Conditions: rps.Conditions, @@ -137,9 +137,9 @@ func DowngradeRouteParentStatuses(routeParentStatuses []v1beta1.RouteParentStatu return res } -// UpgradeBackendRef converts v1alpha2.BackendRef to v1beta1.BackendRef -func UpgradeBackendRef(old v1alpha2.BackendRef) v1beta1.BackendRef { - upgraded := v1beta1.BackendRef{} +// UpgradeBackendRef converts gwapiv1a2.BackendRef to gwapiv1.BackendRef +func UpgradeBackendRef(old gwapiv1a2.BackendRef) gwapiv1.BackendRef { + upgraded := gwapiv1.BackendRef{} if old.Group != nil { upgraded.Group = GroupPtr(string(*old.Group)) @@ -162,8 +162,8 @@ func UpgradeBackendRef(old v1alpha2.BackendRef) v1beta1.BackendRef { return upgraded } -func DowngradeBackendRef(old v1beta1.BackendRef) v1alpha2.BackendRef { - downgraded := v1alpha2.BackendRef{} +func DowngradeBackendRef(old gwapiv1.BackendRef) gwapiv1a2.BackendRef { + downgraded := gwapiv1a2.BackendRef{} if old.Group != nil { downgraded.Group = GroupPtrV1Alpha2(string(*old.Group)) @@ -186,7 +186,7 @@ func DowngradeBackendRef(old v1beta1.BackendRef) v1alpha2.BackendRef { return downgraded } -func NamespaceDerefOrAlpha(namespace *v1alpha2.Namespace, defaultNamespace string) string { +func NamespaceDerefOrAlpha(namespace *gwapiv1a2.Namespace, defaultNamespace string) string { if namespace != nil && *namespace != "" { return string(*namespace) } diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index b6cf8f372cf..e24d58f7fbd 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -10,9 +10,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - configv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils/naming" ) @@ -25,42 +25,37 @@ type ListenersTranslator interface { func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap, infraIR InfraIRMap, resources *Resources) { t.validateConflictedLayer7Listeners(gateways) - t.validateConflictedLayer4Listeners(gateways, v1beta1.TCPProtocolType, v1beta1.TLSProtocolType) - t.validateConflictedLayer4Listeners(gateways, v1beta1.UDPProtocolType) + t.validateConflictedLayer4Listeners(gateways, gwapiv1.TCPProtocolType, gwapiv1.TLSProtocolType) + t.validateConflictedLayer4Listeners(gateways, gwapiv1.UDPProtocolType) + if t.MergeGateways { + t.validateConflictedMergedListeners(gateways) + } // Iterate through all listeners to validate spec // and compute status for each, and add valid ones // to the Xds IR. for _, gateway := range gateways { - // init IR per gateway - irKey := irStringKey(gateway.Gateway.Namespace, gateway.Gateway.Name) - gwXdsIR := &ir.Xds{} - gwInfraIR := ir.NewInfra() - gwInfraIR.Proxy.Name = irKey - gwInfraIR.Proxy.GetProxyMetadata().Labels = GatewayOwnerLabels(gateway.Namespace, gateway.Name) - if resources.EnvoyProxy != nil { - gwInfraIR.Proxy.Config = resources.EnvoyProxy - } - - // save the IR references in the map before the translation starts - xdsIR[irKey] = gwXdsIR - infraIR[irKey] = gwInfraIR - // Infra IR proxy ports must be unique. var foundPorts []*protocolPort + irKey := t.getIRKey(gateway.Gateway) - gwXdsIR.AccessLog = processAccessLog(gwInfraIR.Proxy.Config) - gwXdsIR.Tracing = processTracing(gateway.Gateway, gwInfraIR.Proxy.Config) + if resources.EnvoyProxy != nil { + infraIR[irKey].Proxy.Config = resources.EnvoyProxy + } + + xdsIR[irKey].AccessLog = processAccessLog(infraIR[irKey].Proxy.Config) + xdsIR[irKey].Tracing = processTracing(gateway.Gateway, infraIR[irKey].Proxy.Config) + xdsIR[irKey].Metrics = processMetrics(infraIR[irKey].Proxy.Config) for _, listener := range gateway.listeners { // Process protocol & supported kinds switch listener.Protocol { - case v1beta1.TLSProtocolType: + case gwapiv1.TLSProtocolType: if listener.TLS != nil { switch *listener.TLS.Mode { - case v1beta1.TLSModePassthrough: + case gwapiv1.TLSModePassthrough: t.validateAllowedRoutes(listener, KindTLSRoute) - case v1beta1.TLSModeTerminate: + case gwapiv1.TLSModeTerminate: t.validateAllowedRoutes(listener, KindTCPRoute) default: t.validateAllowedRoutes(listener, KindTCPRoute, KindTLSRoute) @@ -68,19 +63,19 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap } else { t.validateAllowedRoutes(listener, KindTCPRoute, KindTLSRoute) } - case v1beta1.HTTPProtocolType, v1beta1.HTTPSProtocolType: + case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType: t.validateAllowedRoutes(listener, KindHTTPRoute, KindGRPCRoute) - case v1beta1.TCPProtocolType: + case gwapiv1.TCPProtocolType: t.validateAllowedRoutes(listener, KindTCPRoute) - case v1beta1.UDPProtocolType: + case gwapiv1.UDPProtocolType: t.validateAllowedRoutes(listener, KindUDPRoute) default: listener.SetCondition( - v1beta1.ListenerConditionAccepted, + gwapiv1.ListenerConditionAccepted, metav1.ConditionFalse, - v1beta1.ListenerReasonUnsupportedProtocol, + gwapiv1.ListenerReasonUnsupportedProtocol, fmt.Sprintf("Protocol %s is unsupported, must be %s, %s, %s or %s.", listener.Protocol, - v1beta1.HTTPProtocolType, v1beta1.HTTPSProtocolType, v1beta1.TCPProtocolType, v1beta1.UDPProtocolType), + gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType), ) } @@ -103,7 +98,7 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap servicePort := &protocolPort{protocol: listener.Protocol, port: int32(listener.Port)} containerPort := servicePortToContainerPort(servicePort.port) switch listener.Protocol { - case v1beta1.HTTPProtocolType, v1beta1.HTTPSProtocolType: + case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType: irListener := &ir.HTTPListener{ Name: irHTTPListenerName(listener), Address: "0.0.0.0", @@ -115,10 +110,10 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap } else { // Hostname specifies the virtual hostname to match for protocol types that define this concept. // When unspecified, all hostnames are matched. This field is ignored for protocols that don’t require hostname based matching. - // see more https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener. + // see more https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/gwapiv1.Listener. irListener.Hostnames = append(irListener.Hostnames, "*") } - gwXdsIR.HTTP = append(gwXdsIR.HTTP, irListener) + xdsIR[irKey].HTTP = append(xdsIR[irKey].HTTP, irListener) } // Add the listener to the Infra IR. Infra IR ports must have a unique port number per layer-4 protocol @@ -127,32 +122,39 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap foundPorts = append(foundPorts, servicePort) var proto ir.ProtocolType switch listener.Protocol { - case v1beta1.HTTPProtocolType: + case gwapiv1.HTTPProtocolType: proto = ir.HTTPProtocolType - case v1beta1.HTTPSProtocolType: + case gwapiv1.HTTPSProtocolType: proto = ir.HTTPSProtocolType - case v1beta1.TLSProtocolType: + case gwapiv1.TLSProtocolType: proto = ir.TLSProtocolType - case v1beta1.TCPProtocolType: + case gwapiv1.TCPProtocolType: proto = ir.TCPProtocolType - case v1beta1.UDPProtocolType: + case gwapiv1.UDPProtocolType: proto = ir.UDPProtocolType } + + infraPortName := string(listener.Name) + if t.MergeGateways { + infraPortName = irHTTPListenerName(listener) + } + infraPort := ir.ListenerPort{ - Name: string(listener.Name), + Name: infraPortName, Protocol: proto, ServicePort: servicePort.port, ContainerPort: containerPort, } // Only 1 listener is supported. - gwInfraIR.Proxy.Listeners[0].Ports = append(gwInfraIR.Proxy.Listeners[0].Ports, infraPort) + infraIR[irKey].Proxy.Listeners[0].Ports = append(infraIR[irKey].Proxy.Listeners[0].Ports, infraPort) } } } } -func processAccessLog(envoyproxy *configv1a1.EnvoyProxy) *ir.AccessLog { +func processAccessLog(envoyproxy *egv1a1.EnvoyProxy) *ir.AccessLog { if envoyproxy == nil || + envoyproxy.Spec.Telemetry == nil || envoyproxy.Spec.Telemetry.AccessLog == nil || (!envoyproxy.Spec.Telemetry.AccessLog.Disable && len(envoyproxy.Spec.Telemetry.AccessLog.Settings) == 0) { // use the default access log @@ -174,15 +176,19 @@ func processAccessLog(envoyproxy *configv1a1.EnvoyProxy) *ir.AccessLog { for _, accessLog := range envoyproxy.Spec.Telemetry.AccessLog.Settings { for _, sink := range accessLog.Sinks { switch sink.Type { - case configv1a1.ProxyAccessLogSinkTypeFile: + case egv1a1.ProxyAccessLogSinkTypeFile: + if sink.File == nil { + continue + } + switch accessLog.Format.Type { - case configv1a1.ProxyAccessLogFormatTypeText: + case egv1a1.ProxyAccessLogFormatTypeText: al := &ir.TextAccessLog{ Format: accessLog.Format.Text, Path: sink.File.Path, } irAccessLog.Text = append(irAccessLog.Text, al) - case configv1a1.ProxyAccessLogFormatTypeJSON: + case egv1a1.ProxyAccessLogFormatTypeJSON: if len(accessLog.Format.JSON) == 0 { // TODO: use a default JSON format if not specified? continue @@ -194,7 +200,7 @@ func processAccessLog(envoyproxy *configv1a1.EnvoyProxy) *ir.AccessLog { } irAccessLog.JSON = append(irAccessLog.JSON, al) } - case configv1a1.ProxyAccessLogSinkTypeOpenTelemetry: + case egv1a1.ProxyAccessLogSinkTypeOpenTelemetry: if sink.OpenTelemetry == nil { continue } @@ -206,9 +212,9 @@ func processAccessLog(envoyproxy *configv1a1.EnvoyProxy) *ir.AccessLog { } switch accessLog.Format.Type { - case configv1a1.ProxyAccessLogFormatTypeJSON: + case egv1a1.ProxyAccessLogFormatTypeJSON: al.Attributes = accessLog.Format.JSON - case configv1a1.ProxyAccessLogFormatTypeText: + case egv1a1.ProxyAccessLogFormatTypeText: al.Text = accessLog.Format.Text } @@ -220,8 +226,10 @@ func processAccessLog(envoyproxy *configv1a1.EnvoyProxy) *ir.AccessLog { return irAccessLog } -func processTracing(gw *v1beta1.Gateway, envoyproxy *configv1a1.EnvoyProxy) *ir.Tracing { - if envoyproxy == nil || envoyproxy.Spec.Telemetry.Tracing == nil { +func processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.EnvoyProxy) *ir.Tracing { + if envoyproxy == nil || + envoyproxy.Spec.Telemetry == nil || + envoyproxy.Spec.Telemetry.Tracing == nil { return nil } @@ -230,3 +238,14 @@ func processTracing(gw *v1beta1.Gateway, envoyproxy *configv1a1.EnvoyProxy) *ir. ProxyTracing: *envoyproxy.Spec.Telemetry.Tracing, } } + +func processMetrics(envoyproxy *egv1a1.EnvoyProxy) *ir.Metrics { + if envoyproxy == nil || + envoyproxy.Spec.Telemetry == nil || + envoyproxy.Spec.Telemetry.Metrics == nil { + return nil + } + return &ir.Metrics{ + EnableVirtualHostStats: envoyproxy.Spec.Telemetry.Metrics.EnableVirtualHostStats, + } +} diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go index 465a4923168..ddecda17ada 100644 --- a/internal/gatewayapi/listener_test.go +++ b/internal/gatewayapi/listener_test.go @@ -10,22 +10,22 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egcfgv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" ) func TestProcessTracing(t *testing.T) { cases := []struct { - gw v1beta1.Gateway + gw gwapiv1.Gateway proxy *egcfgv1a1.EnvoyProxy expected *ir.Tracing }{ {}, { - gw: v1beta1.Gateway{ + gw: gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "fake-gw", Namespace: "fake-ns", @@ -33,7 +33,7 @@ func TestProcessTracing(t *testing.T) { }, proxy: &egcfgv1a1.EnvoyProxy{ Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: egcfgv1a1.ProxyTelemetry{ + Telemetry: &egcfgv1a1.ProxyTelemetry{ Tracing: &egcfgv1a1.ProxyTracing{}, }, }, @@ -46,9 +46,44 @@ func TestProcessTracing(t *testing.T) { } for _, c := range cases { + c := c t.Run("", func(t *testing.T) { got := processTracing(&c.gw, c.proxy) assert.Equal(t, c.expected, got) }) } } + +func TestProcessMetrics(t *testing.T) { + cases := []struct { + name string + proxy *egcfgv1a1.EnvoyProxy + + expected *ir.Metrics + }{ + { + name: "nil proxy config", + }, + { + name: "virtual host stats enabled", + proxy: &egcfgv1a1.EnvoyProxy{ + Spec: egcfgv1a1.EnvoyProxySpec{ + Telemetry: &egcfgv1a1.ProxyTelemetry{ + Metrics: &egcfgv1a1.ProxyMetrics{ + EnableVirtualHostStats: true, + }, + }, + }, + }, + expected: &ir.Metrics{ + EnableVirtualHostStats: true, + }, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := processMetrics(c.proxy) + assert.Equal(t, c.expected, got) + }) + } +} diff --git a/internal/gatewayapi/resource.go b/internal/gatewayapi/resource.go index 66b5e671fa6..6dc630ddc72 100644 --- a/internal/gatewayapi/resource.go +++ b/internal/gatewayapi/resource.go @@ -9,10 +9,11 @@ import ( v1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" ) @@ -26,40 +27,43 @@ type InfraIRMap map[string]*ir.Infra type Resources struct { // This field is only used for marshalling/unmarshalling purposes and is not used by // the translator - GatewayClass *v1beta1.GatewayClass `json:"gatewayClass,omitempty" yaml:"gatewayClass,omitempty"` - Gateways []*v1beta1.Gateway `json:"gateways,omitempty" yaml:"gateways,omitempty"` - HTTPRoutes []*v1beta1.HTTPRoute `json:"httpRoutes,omitempty" yaml:"httpRoutes,omitempty"` - GRPCRoutes []*v1alpha2.GRPCRoute `json:"grpcRoutes,omitempty" yaml:"grpcRoutes,omitempty"` - TLSRoutes []*v1alpha2.TLSRoute `json:"tlsRoutes,omitempty" yaml:"tlsRoutes,omitempty"` - TCPRoutes []*v1alpha2.TCPRoute `json:"tcpRoutes,omitempty" yaml:"tcpRoutes,omitempty"` - UDPRoutes []*v1alpha2.UDPRoute `json:"udpRoutes,omitempty" yaml:"udpRoutes,omitempty"` - ReferenceGrants []*v1alpha2.ReferenceGrant `json:"referenceGrants,omitempty" yaml:"referenceGrants,omitempty"` - Namespaces []*v1.Namespace `json:"namespaces,omitempty" yaml:"namespaces,omitempty"` - Services []*v1.Service `json:"services,omitempty" yaml:"services,omitempty"` - EndpointSlices []*discoveryv1.EndpointSlice `json:"endpointSlices,omitempty" yaml:"endpointSlices,omitempty"` - Secrets []*v1.Secret `json:"secrets,omitempty" yaml:"secrets,omitempty"` - AuthenticationFilters []*egv1a1.AuthenticationFilter `json:"authenticationFilters,omitempty" yaml:"authenticationFilters,omitempty"` - RateLimitFilters []*egv1a1.RateLimitFilter `json:"rateLimitFilters,omitempty" yaml:"rateLimitFilters,omitempty"` - EnvoyProxy *egcfgv1a1.EnvoyProxy `json:"envoyProxy,omitempty" yaml:"envoyProxy,omitempty"` - ExtensionRefFilters []unstructured.Unstructured `json:"extensionRefFilters,omitempty" yaml:"extensionRefFilters,omitempty"` - EnvoyPatchPolicies []*egv1a1.EnvoyPatchPolicy `json:"envoyPatchPolicies,omitempty" yaml:"envoyPatchPolicies,omitempty"` + GatewayClass *gwapiv1.GatewayClass `json:"gatewayClass,omitempty" yaml:"gatewayClass,omitempty"` + Gateways []*gwapiv1.Gateway `json:"gateways,omitempty" yaml:"gateways,omitempty"` + HTTPRoutes []*gwapiv1.HTTPRoute `json:"httpRoutes,omitempty" yaml:"httpRoutes,omitempty"` + GRPCRoutes []*gwapiv1a2.GRPCRoute `json:"grpcRoutes,omitempty" yaml:"grpcRoutes,omitempty"` + TLSRoutes []*gwapiv1a2.TLSRoute `json:"tlsRoutes,omitempty" yaml:"tlsRoutes,omitempty"` + TCPRoutes []*gwapiv1a2.TCPRoute `json:"tcpRoutes,omitempty" yaml:"tcpRoutes,omitempty"` + UDPRoutes []*gwapiv1a2.UDPRoute `json:"udpRoutes,omitempty" yaml:"udpRoutes,omitempty"` + ReferenceGrants []*gwapiv1b1.ReferenceGrant `json:"referenceGrants,omitempty" yaml:"referenceGrants,omitempty"` + Namespaces []*v1.Namespace `json:"namespaces,omitempty" yaml:"namespaces,omitempty"` + Services []*v1.Service `json:"services,omitempty" yaml:"services,omitempty"` + ServiceImports []*mcsapi.ServiceImport `json:"serviceImports,omitempty" yaml:"serviceImports,omitempty"` + EndpointSlices []*discoveryv1.EndpointSlice `json:"endpointSlices,omitempty" yaml:"endpointSlices,omitempty"` + Secrets []*v1.Secret `json:"secrets,omitempty" yaml:"secrets,omitempty"` + EnvoyProxy *egv1a1.EnvoyProxy `json:"envoyProxy,omitempty" yaml:"envoyProxy,omitempty"` + ExtensionRefFilters []unstructured.Unstructured `json:"extensionRefFilters,omitempty" yaml:"extensionRefFilters,omitempty"` + EnvoyPatchPolicies []*egv1a1.EnvoyPatchPolicy `json:"envoyPatchPolicies,omitempty" yaml:"envoyPatchPolicies,omitempty"` + ClientTrafficPolicies []*egv1a1.ClientTrafficPolicy `json:"clientTrafficPolicies,omitempty" yaml:"clientTrafficPolicies,omitempty"` + BackendTrafficPolicies []*egv1a1.BackendTrafficPolicy `json:"backendTrafficPolicies,omitempty" yaml:"backendTrafficPolicies,omitempty"` + SecurityPolicies []*egv1a1.SecurityPolicy `json:"securityPolicies,omitempty" yaml:"securityPolicies,omitempty"` } func NewResources() *Resources { return &Resources{ - Gateways: []*v1beta1.Gateway{}, - HTTPRoutes: []*v1beta1.HTTPRoute{}, - GRPCRoutes: []*v1alpha2.GRPCRoute{}, - TLSRoutes: []*v1alpha2.TLSRoute{}, - Services: []*v1.Service{}, - EndpointSlices: []*discoveryv1.EndpointSlice{}, - Secrets: []*v1.Secret{}, - ReferenceGrants: []*v1alpha2.ReferenceGrant{}, - Namespaces: []*v1.Namespace{}, - RateLimitFilters: []*egv1a1.RateLimitFilter{}, - AuthenticationFilters: []*egv1a1.AuthenticationFilter{}, - ExtensionRefFilters: []unstructured.Unstructured{}, - EnvoyPatchPolicies: []*egv1a1.EnvoyPatchPolicy{}, + Gateways: []*gwapiv1.Gateway{}, + HTTPRoutes: []*gwapiv1.HTTPRoute{}, + GRPCRoutes: []*gwapiv1a2.GRPCRoute{}, + TLSRoutes: []*gwapiv1a2.TLSRoute{}, + Services: []*v1.Service{}, + EndpointSlices: []*discoveryv1.EndpointSlice{}, + Secrets: []*v1.Secret{}, + ReferenceGrants: []*gwapiv1b1.ReferenceGrant{}, + Namespaces: []*v1.Namespace{}, + ExtensionRefFilters: []unstructured.Unstructured{}, + EnvoyPatchPolicies: []*egv1a1.EnvoyPatchPolicy{}, + ClientTrafficPolicies: []*egv1a1.ClientTrafficPolicy{}, + BackendTrafficPolicies: []*egv1a1.BackendTrafficPolicy{}, + SecurityPolicies: []*egv1a1.SecurityPolicy{}, } } @@ -83,6 +87,16 @@ func (r *Resources) GetService(namespace, name string) *v1.Service { return nil } +func (r *Resources) GetServiceImport(namespace, name string) *mcsapi.ServiceImport { + for _, svcImp := range r.ServiceImports { + if svcImp.Namespace == namespace && svcImp.Name == name { + return svcImp + } + } + + return nil +} + func (r *Resources) GetSecret(namespace, name string) *v1.Secret { for _, secret := range r.Secrets { if secret.Namespace == namespace && secret.Name == name { @@ -92,3 +106,21 @@ func (r *Resources) GetSecret(namespace, name string) *v1.Secret { return nil } + +func (r *Resources) GetEndpointSlicesForBackend(svcNamespace, svcName string, backendKind string) []*discoveryv1.EndpointSlice { + var endpointSlices []*discoveryv1.EndpointSlice + for _, endpointSlice := range r.EndpointSlices { + var backendSelectorLabel string + switch backendKind { + case KindService: + backendSelectorLabel = discoveryv1.LabelServiceName + case KindServiceImport: + backendSelectorLabel = mcsapi.LabelServiceName + } + if svcNamespace == endpointSlice.Namespace && + endpointSlice.GetLabels()[backendSelectorLabel] == svcName { + endpointSlices = append(endpointSlices, endpointSlice) + } + } + return endpointSlices +} diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 8fc96264b95..87ba9a4a25c 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -8,10 +8,15 @@ package gatewayapi import ( "fmt" "strings" + "time" + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a1 "sigs.k8s.io/gateway-api/apis/v1alpha2" + mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" ) @@ -23,14 +28,14 @@ var ( ) type RoutesTranslator interface { - ProcessHTTPRoutes(httpRoutes []*v1beta1.HTTPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*HTTPRouteContext - ProcessGRPCRoutes(grpcRoutes []*v1alpha2.GRPCRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*GRPCRouteContext - ProcessTLSRoutes(tlsRoutes []*v1alpha2.TLSRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TLSRouteContext - ProcessTCPRoutes(tcpRoutes []*v1alpha2.TCPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TCPRouteContext - ProcessUDPRoutes(udpRoutes []*v1alpha2.UDPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*UDPRouteContext + ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*HTTPRouteContext + ProcessGRPCRoutes(grpcRoutes []*gwapiv1a1.GRPCRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*GRPCRouteContext + ProcessTLSRoutes(tlsRoutes []*gwapiv1a1.TLSRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TLSRouteContext + ProcessTCPRoutes(tcpRoutes []*gwapiv1a1.TCPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TCPRouteContext + ProcessUDPRoutes(udpRoutes []*gwapiv1a1.UDPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*UDPRouteContext } -func (t *Translator) ProcessHTTPRoutes(httpRoutes []*v1beta1.HTTPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*HTTPRouteContext { +func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*HTTPRouteContext { var relevantHTTPRoutes []*HTTPRouteContext for _, h := range httpRoutes { @@ -58,7 +63,7 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*v1beta1.HTTPRoute, gateways return relevantHTTPRoutes } -func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*v1alpha2.GRPCRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*GRPCRouteContext { +func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*gwapiv1a1.GRPCRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*GRPCRouteContext { var relevantGRPCRoutes []*GRPCRouteContext for _, g := range grpcRoutes { @@ -94,26 +99,26 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res routeRoutes := t.processHTTPRouteRules(httpRoute, parentRef, resources) // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" - if !parentRef.HasCondition(httpRoute, v1beta1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + if !parentRef.HasCondition(httpRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { parentRef.SetCondition(httpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionTrue, - v1beta1.RouteReasonResolvedRefs, + gwapiv1.RouteReasonResolvedRefs, "Resolved all the Object references for the Route", ) } // Skip parent refs that did not accept the route - if parentRef.HasCondition(httpRoute, v1beta1.RouteConditionAccepted, metav1.ConditionFalse) { + if parentRef.HasCondition(httpRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { continue } var hasHostnameIntersection = t.processHTTPRouteParentRefListener(httpRoute, routeRoutes, parentRef, xdsIR) if !hasHostnameIntersection { parentRef.SetCondition(httpRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonNoMatchingListenerHostname, + gwapiv1.RouteReasonNoMatchingListenerHostname, "There were no hostname intersections between the HTTPRoute and this parent ref's Listener(s).", ) } @@ -122,9 +127,9 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res if parentRef.HTTPRoute != nil && len(parentRef.HTTPRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { parentRef.SetCondition(httpRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionTrue, - v1beta1.RouteReasonAccepted, + gwapiv1.RouteReasonAccepted, "Route is accepted", ) } @@ -137,23 +142,33 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe // compute matches, filters, backends for ruleIdx, rule := range httpRoute.Spec.Rules { - httpFiltersContext := t.ProcessHTTPFilters(parentRef, httpRoute, rule.Filters, resources) + httpFiltersContext := t.ProcessHTTPFilters(parentRef, httpRoute, rule.Filters, ruleIdx, resources) // A rule is matched if any one of its matches // is satisfied (i.e. a logical "OR"), so generate // a unique Xds IR HTTPRoute per match. var ruleRoutes = t.processHTTPRouteRule(httpRoute, ruleIdx, httpFiltersContext, rule) + dstAddrTypeMap := make(map[ir.DestinationAddressType]int) + for _, backendRef := range rule.BackendRefs { - destinations, backendWeight := t.processRouteDestinations(backendRef.BackendRef, parentRef, httpRoute, resources) + ds, backendWeight := t.processDestination(backendRef.BackendRef, parentRef, httpRoute, resources) + if !t.EndpointRoutingDisabled && ds != nil && len(ds.Endpoints) > 0 && ds.AddressType != nil { + dstAddrTypeMap[*ds.AddressType]++ + } + for _, route := range ruleRoutes { // If the route already has a direct response or redirect configured, then it was from a filter so skip // processing any destinations for this route. if route.DirectResponse == nil && route.Redirect == nil { - if len(destinations) > 0 { - route.Destinations = append(route.Destinations, destinations...) + if ds != nil && len(ds.Endpoints) > 0 { + if route.Destination == nil { + route.Destination = &ir.RouteDestination{ + Name: irRouteDestinationName(httpRoute, ruleIdx), + } + } + route.Destination.Settings = append(route.Destination.Settings, ds) route.BackendWeights.Valid += backendWeight - } else { route.BackendWeights.Invalid += backendWeight } @@ -161,9 +176,18 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe } } + // TODO: support mixed endpointslice address type between backendRefs + if !t.EndpointRoutingDisabled && len(dstAddrTypeMap) > 1 { + parentRef.SetCondition(httpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1a1.RouteReasonResolvedRefs, + "Mixed endpointslice address type between backendRefs is not supported") + } + // If the route has no valid backends then just use a direct response and don't fuss with weighted responses for _, ruleRoute := range ruleRoutes { - if ruleRoute.BackendWeights.Invalid > 0 && len(ruleRoute.Destinations) == 0 { + if ruleRoute.BackendWeights.Invalid > 0 && ruleRoute.Destination == nil { ruleRoute.DirectResponse = &ir.DirectResponse{ StatusCode: 500, } @@ -180,14 +204,33 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe return routeRoutes } -func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule v1beta1.HTTPRouteRule) []*ir.HTTPRoute { +func processTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { + if rule.Timeouts != nil { + if rule.Timeouts.Request != nil { + // TODO: handle parse errors + d, _ := time.ParseDuration(string(*rule.Timeouts.Request)) + irRoute.Timeout = ptr.To(metav1.Duration{Duration: d}) + } + + // Also set the IR Route Timeout to the backend request timeout + // until we introduce retries, then set it to per try timeout + if rule.Timeouts.BackendRequest != nil { + // TODO: handle parse errors + d, _ := time.ParseDuration(string(*rule.Timeouts.BackendRequest)) + irRoute.Timeout = ptr.To(metav1.Duration{Duration: d}) + } + } +} + +func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1.HTTPRouteRule) []*ir.HTTPRoute { var ruleRoutes []*ir.HTTPRoute // If no matches are specified, the implementation MUST match every HTTP request. if len(rule.Matches) == 0 { irRoute := &ir.HTTPRoute{ - Name: routeName(httpRoute, ruleIdx, -1), + Name: irRouteName(httpRoute, ruleIdx, -1), } + processTimeout(irRoute, rule) applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) ruleRoutes = append(ruleRoutes, irRoute) } @@ -197,50 +240,51 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i // a unique Xds IR HTTPRoute per match. for matchIdx, match := range rule.Matches { irRoute := &ir.HTTPRoute{ - Name: routeName(httpRoute, ruleIdx, matchIdx), + Name: irRouteName(httpRoute, ruleIdx, matchIdx), } + processTimeout(irRoute, rule) if match.Path != nil { - switch PathMatchTypeDerefOr(match.Path.Type, v1beta1.PathMatchPathPrefix) { - case v1beta1.PathMatchPathPrefix: + switch PathMatchTypeDerefOr(match.Path.Type, gwapiv1.PathMatchPathPrefix) { + case gwapiv1.PathMatchPathPrefix: irRoute.PathMatch = &ir.StringMatch{ Prefix: match.Path.Value, } - case v1beta1.PathMatchExact: + case gwapiv1.PathMatchExact: irRoute.PathMatch = &ir.StringMatch{ Exact: match.Path.Value, } - case v1beta1.PathMatchRegularExpression: + case gwapiv1.PathMatchRegularExpression: irRoute.PathMatch = &ir.StringMatch{ SafeRegex: match.Path.Value, } } } for _, headerMatch := range match.Headers { - switch HeaderMatchTypeDerefOr(headerMatch.Type, v1beta1.HeaderMatchExact) { - case v1beta1.HeaderMatchExact: + switch HeaderMatchTypeDerefOr(headerMatch.Type, gwapiv1.HeaderMatchExact) { + case gwapiv1.HeaderMatchExact: irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), - Exact: StringPtr(headerMatch.Value), + Exact: ptr.To(headerMatch.Value), }) - case v1beta1.HeaderMatchRegularExpression: + case gwapiv1.HeaderMatchRegularExpression: irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), - SafeRegex: StringPtr(headerMatch.Value), + SafeRegex: ptr.To(headerMatch.Value), }) } } for _, queryParamMatch := range match.QueryParams { - switch QueryParamMatchTypeDerefOr(queryParamMatch.Type, v1beta1.QueryParamMatchExact) { - case v1beta1.QueryParamMatchExact: + switch QueryParamMatchTypeDerefOr(queryParamMatch.Type, gwapiv1.QueryParamMatchExact) { + case gwapiv1.QueryParamMatchExact: irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ Name: string(queryParamMatch.Name), - Exact: StringPtr(queryParamMatch.Value), + Exact: ptr.To(queryParamMatch.Value), }) - case v1beta1.QueryParamMatchRegularExpression: + case gwapiv1.QueryParamMatchRegularExpression: irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ Name: string(queryParamMatch.Name), - SafeRegex: StringPtr(queryParamMatch.Value), + SafeRegex: ptr.To(queryParamMatch.Value), }) } } @@ -248,7 +292,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i if match.Method != nil { irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: ":method", - Exact: StringPtr(string(*match.Method)), + Exact: ptr.To(string(*match.Method)), }) } applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) @@ -281,15 +325,10 @@ func applyHTTPFiltersContextToIRRoute(httpFiltersContext *HTTPFiltersContext, ir if len(httpFiltersContext.RemoveResponseHeaders) > 0 { irRoute.RemoveResponseHeaders = httpFiltersContext.RemoveResponseHeaders } - if len(httpFiltersContext.Mirrors) > 0 { + if httpFiltersContext.Mirrors != nil { irRoute.Mirrors = httpFiltersContext.Mirrors } - if httpFiltersContext.RequestAuthentication != nil { - irRoute.RequestAuthentication = httpFiltersContext.RequestAuthentication - } - if httpFiltersContext.RateLimit != nil { - irRoute.RateLimit = httpFiltersContext.RateLimit - } + if len(httpFiltersContext.ExtensionRefs) > 0 { irRoute.ExtensionRefs = httpFiltersContext.ExtensionRefs } @@ -305,24 +344,24 @@ func (t *Translator) processGRPCRouteParentRefs(grpcRoute *GRPCRouteContext, res routeRoutes := t.processGRPCRouteRules(grpcRoute, parentRef, resources) // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" - if !parentRef.HasCondition(grpcRoute, v1beta1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + if !parentRef.HasCondition(grpcRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { parentRef.SetCondition(grpcRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionTrue, - v1beta1.RouteReasonResolvedRefs, + gwapiv1.RouteReasonResolvedRefs, "Resolved all the Object references for the Route", ) } - if parentRef.HasCondition(grpcRoute, v1beta1.RouteConditionAccepted, metav1.ConditionFalse) { + if parentRef.HasCondition(grpcRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { continue } var hasHostnameIntersection = t.processHTTPRouteParentRefListener(grpcRoute, routeRoutes, parentRef, xdsIR) if !hasHostnameIntersection { parentRef.SetCondition(grpcRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonNoMatchingListenerHostname, + gwapiv1.RouteReasonNoMatchingListenerHostname, "There were no hostname intersections between the GRPCRoute and this parent ref's Listener(s).", ) } @@ -331,9 +370,9 @@ func (t *Translator) processGRPCRouteParentRefs(grpcRoute *GRPCRouteContext, res if parentRef.GRPCRoute != nil && len(parentRef.GRPCRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { parentRef.SetCondition(grpcRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionTrue, - v1beta1.RouteReasonAccepted, + gwapiv1.RouteReasonAccepted, "Route is accepted", ) } @@ -354,13 +393,18 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe var ruleRoutes = t.processGRPCRouteRule(grpcRoute, ruleIdx, httpFiltersContext, rule) for _, backendRef := range rule.BackendRefs { - destinations, backendWeight := t.processRouteDestinations(backendRef.BackendRef, parentRef, grpcRoute, resources) + ds, backendWeight := t.processDestination(backendRef.BackendRef, parentRef, grpcRoute, resources) for _, route := range ruleRoutes { // If the route already has a direct response or redirect configured, then it was from a filter so skip // processing any destinations for this route. if route.DirectResponse == nil && route.Redirect == nil { - if len(destinations) > 0 { - route.Destinations = append(route.Destinations, destinations...) + if ds != nil && len(ds.Endpoints) > 0 { + if route.Destination == nil { + route.Destination = &ir.RouteDestination{ + Name: irRouteDestinationName(grpcRoute, ruleIdx), + } + } + route.Destination.Settings = append(route.Destination.Settings, ds) route.BackendWeights.Valid += backendWeight } else { @@ -372,7 +416,7 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe // If the route has no valid backends then just use a direct response and don't fuss with weighted responses for _, ruleRoute := range ruleRoutes { - if ruleRoute.BackendWeights.Invalid > 0 && len(ruleRoute.Destinations) == 0 { + if ruleRoute.BackendWeights.Invalid > 0 && ruleRoute.Destination == nil { ruleRoute.DirectResponse = &ir.DirectResponse{ StatusCode: 500, } @@ -389,13 +433,13 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe return routeRoutes } -func (t *Translator) processGRPCRouteRule(grpcRoute *GRPCRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule v1alpha2.GRPCRouteRule) []*ir.HTTPRoute { +func (t *Translator) processGRPCRouteRule(grpcRoute *GRPCRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1a1.GRPCRouteRule) []*ir.HTTPRoute { var ruleRoutes []*ir.HTTPRoute // If no matches are specified, the implementation MUST match every gRPC request. if len(rule.Matches) == 0 { irRoute := &ir.HTTPRoute{ - Name: routeName(grpcRoute, ruleIdx, -1), + Name: irRouteName(grpcRoute, ruleIdx, -1), } applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) ruleRoutes = append(ruleRoutes, irRoute) @@ -406,30 +450,30 @@ func (t *Translator) processGRPCRouteRule(grpcRoute *GRPCRouteContext, ruleIdx i // a unique Xds IR HTTPRoute per match. for matchIdx, match := range rule.Matches { irRoute := &ir.HTTPRoute{ - Name: routeName(grpcRoute, ruleIdx, matchIdx), + Name: irRouteName(grpcRoute, ruleIdx, matchIdx), } for _, headerMatch := range match.Headers { - switch HeaderMatchTypeDerefOr(headerMatch.Type, v1beta1.HeaderMatchExact) { - case v1beta1.HeaderMatchExact: + switch HeaderMatchTypeDerefOr(headerMatch.Type, gwapiv1.HeaderMatchExact) { + case gwapiv1.HeaderMatchExact: irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), - Exact: StringPtr(headerMatch.Value), + Exact: ptr.To(headerMatch.Value), }) - case v1beta1.HeaderMatchRegularExpression: + case gwapiv1.HeaderMatchRegularExpression: irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), - SafeRegex: StringPtr(headerMatch.Value), + SafeRegex: ptr.To(headerMatch.Value), }) } } if match.Method != nil { // GRPC's path is in the form of "//" - switch GRPCMethodMatchTypeDerefOr(match.Method.Type, v1alpha2.GRPCMethodMatchExact) { - case v1alpha2.GRPCMethodMatchExact: + switch GRPCMethodMatchTypeDerefOr(match.Method.Type, gwapiv1a1.GRPCMethodMatchExact) { + case gwapiv1a1.GRPCMethodMatchExact: t.processGRPCRouteMethodExact(match.Method, irRoute) - case v1alpha2.GRPCMethodMatchRegularExpression: + case gwapiv1a1.GRPCMethodMatchRegularExpression: t.processGRPCRouteMethodRegularExpression(match.Method, irRoute) } } @@ -440,38 +484,38 @@ func (t *Translator) processGRPCRouteRule(grpcRoute *GRPCRouteContext, ruleIdx i return ruleRoutes } -func (t *Translator) processGRPCRouteMethodExact(method *v1alpha2.GRPCMethodMatch, irRoute *ir.HTTPRoute) { +func (t *Translator) processGRPCRouteMethodExact(method *gwapiv1a1.GRPCMethodMatch, irRoute *ir.HTTPRoute) { switch { case method.Service != nil && method.Method != nil: irRoute.PathMatch = &ir.StringMatch{ - Exact: StringPtr(fmt.Sprintf("/%s/%s", *method.Service, *method.Method)), + Exact: ptr.To(fmt.Sprintf("/%s/%s", *method.Service, *method.Method)), } case method.Method != nil: // Use a header match since the PathMatch doesn't support Suffix matching irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: ":path", - Suffix: StringPtr(fmt.Sprintf("/%s", *method.Method)), + Suffix: ptr.To(fmt.Sprintf("/%s", *method.Method)), }) case method.Service != nil: irRoute.PathMatch = &ir.StringMatch{ - Prefix: StringPtr(fmt.Sprintf("/%s", *method.Service)), + Prefix: ptr.To(fmt.Sprintf("/%s", *method.Service)), } } } -func (t *Translator) processGRPCRouteMethodRegularExpression(method *v1alpha2.GRPCMethodMatch, irRoute *ir.HTTPRoute) { +func (t *Translator) processGRPCRouteMethodRegularExpression(method *gwapiv1a1.GRPCMethodMatch, irRoute *ir.HTTPRoute) { switch { case method.Service != nil && method.Method != nil: irRoute.PathMatch = &ir.StringMatch{ - SafeRegex: StringPtr(fmt.Sprintf("/%s/%s", *method.Service, *method.Method)), + SafeRegex: ptr.To(fmt.Sprintf("/%s/%s", *method.Service, *method.Method)), } case method.Method != nil: irRoute.PathMatch = &ir.StringMatch{ - SafeRegex: StringPtr(fmt.Sprintf("/%s/%s", validServiceName, *method.Method)), + SafeRegex: ptr.To(fmt.Sprintf("/%s/%s", validServiceName, *method.Method)), } case method.Service != nil: irRoute.PathMatch = &ir.StringMatch{ - SafeRegex: StringPtr(fmt.Sprintf("/%s/%s", *method.Service, validMethodName)), + SafeRegex: ptr.To(fmt.Sprintf("/%s/%s", *method.Service, validMethodName)), } } } @@ -488,43 +532,43 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route var perHostRoutes []*ir.HTTPRoute for _, host := range hosts { - var headerMatches []*ir.StringMatch - - // If the intersecting host is more specific than the Listener's hostname, - // add an additional header match to all of the routes for it - if host != "*" && (listener.Hostname == nil || string(*listener.Hostname) != host) { - // Hostnames that are prefixed with a wildcard label (*.) - // are interpreted as a suffix match. - if strings.HasPrefix(host, "*.") { - headerMatches = append(headerMatches, &ir.StringMatch{ - Name: ":authority", - Suffix: StringPtr(host[2:]), - }) - } else { - headerMatches = append(headerMatches, &ir.StringMatch{ - Name: ":authority", - Exact: StringPtr(host), - }) - } - } - for _, routeRoute := range routeRoutes { + // If the redirect port is not set, the final redirect port must be derived. + if routeRoute.Redirect != nil && routeRoute.Redirect.Port == nil { + redirectPort := uint32(listener.Port) + // If redirect scheme is not-empty, the redirect post must be the + // well-known port associated with the redirect scheme. + if scheme := routeRoute.Redirect.Scheme; scheme != nil { + switch strings.ToLower(*scheme) { + case "http": + redirectPort = 80 + case "https": + redirectPort = 443 + } + } + // If the redirect scheme does not have a well-known port, or + // if the redirect scheme is empty, the redirect port must be the Gateway Listener port. + routeRoute.Redirect.Port = &redirectPort + } + // Remove dots from the hostname before appending it to the IR Route name + // since dots are special chars used in stats tag extraction in Envoy + underscoredHost := strings.ReplaceAll(host, ".", "_") hostRoute := &ir.HTTPRoute{ - Name: fmt.Sprintf("%s-%s", routeRoute.Name, host), + Name: fmt.Sprintf("%s/%s", routeRoute.Name, underscoredHost), + Hostname: host, PathMatch: routeRoute.PathMatch, - HeaderMatches: append(headerMatches, routeRoute.HeaderMatches...), + HeaderMatches: routeRoute.HeaderMatches, QueryParamMatches: routeRoute.QueryParamMatches, AddRequestHeaders: routeRoute.AddRequestHeaders, RemoveRequestHeaders: routeRoute.RemoveRequestHeaders, AddResponseHeaders: routeRoute.AddResponseHeaders, RemoveResponseHeaders: routeRoute.RemoveResponseHeaders, - Destinations: routeRoute.Destinations, + Destination: routeRoute.Destination, Redirect: routeRoute.Redirect, DirectResponse: routeRoute.DirectResponse, URLRewrite: routeRoute.URLRewrite, Mirrors: routeRoute.Mirrors, - RequestAuthentication: routeRoute.RequestAuthentication, - RateLimit: routeRoute.RateLimit, + Timeout: routeRoute.Timeout, ExtensionRefs: routeRoute.ExtensionRefs, } // Don't bother copying over the weights unless the route has invalid backends. @@ -534,8 +578,7 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route perHostRoutes = append(perHostRoutes, hostRoute) } } - - irKey := irStringKey(listener.gateway.Namespace, listener.gateway.Name) + irKey := t.getIRKey(listener.gateway) irListener := xdsIR[irKey].GetHTTPListener(irHTTPListenerName(listener)) if irListener != nil { if GetRouteType(route) == KindGRPCRoute { @@ -543,18 +586,12 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route } irListener.Routes = append(irListener.Routes, perHostRoutes...) } - // Theoretically there should only be one parent ref per - // Route that attaches to a given Listener, so fine to just increment here, but we - // might want to check to ensure we're not double-counting. - if len(routeRoutes) > 0 { - listener.IncrementAttachedRoutes() - } } return hasHostnameIntersection } -func (t *Translator) ProcessTLSRoutes(tlsRoutes []*v1alpha2.TLSRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TLSRouteContext { +func (t *Translator) ProcessTLSRoutes(tlsRoutes []*gwapiv1a1.TLSRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TLSRouteContext { var relevantTLSRoutes []*TLSRouteContext for _, tls := range tlsRoutes { @@ -588,14 +625,16 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour // Need to compute Route rules within the parentRef loop because // any conditions that come out of it have to go on each RouteParentStatus, // not on the Route as a whole. - var routeDestinations []*ir.RouteDestination + var destSettings []*ir.DestinationSetting // compute backends for _, rule := range tlsRoute.Spec.Rules { for _, backendRef := range rule.BackendRefs { backendRef := backendRef - destinations, _ := t.processRouteDestinations(backendRef, parentRef, tlsRoute, resources) - routeDestinations = append(routeDestinations, destinations...) + ds, _ := t.processDestination(backendRef, parentRef, tlsRoute, resources) + if ds != nil { + destSettings = append(destSettings, ds) + } } // TODO handle: @@ -606,17 +645,17 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour } // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" - if !parentRef.HasCondition(tlsRoute, v1beta1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + if !parentRef.HasCondition(tlsRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { parentRef.SetCondition(tlsRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionTrue, - v1beta1.RouteReasonResolvedRefs, + gwapiv1.RouteReasonResolvedRefs, "Resolved all the Object references for the Route", ) } // Skip parent refs that did not accept the route - if parentRef.HasCondition(tlsRoute, v1beta1.RouteConditionAccepted, metav1.ConditionFalse) { + if parentRef.HasCondition(tlsRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { continue } @@ -629,7 +668,8 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour hasHostnameIntersection = true - irKey := irStringKey(listener.gateway.Namespace, listener.gateway.Name) + irKey := t.getIRKey(listener.gateway) + containerPort := servicePortToContainerPort(int32(listener.Port)) // Create the TCP Listener while parsing the TLSRoute since // the listener directly links to a routeDestination. @@ -640,24 +680,21 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour TLS: &ir.TLS{Passthrough: &ir.TLSInspectorConfig{ SNIs: hosts, }}, - Destinations: routeDestinations, + Destination: &ir.RouteDestination{ + Name: irRouteDestinationName(tlsRoute, -1 /*rule index*/), + Settings: destSettings, + }, } gwXdsIR := xdsIR[irKey] gwXdsIR.TCP = append(gwXdsIR.TCP, irListener) - // Theoretically there should only be one parent ref per - // Route that attaches to a given Listener, so fine to just increment here, but we - // might want to check to ensure we're not double-counting. - if len(routeDestinations) > 0 { - listener.IncrementAttachedRoutes() - } } if !hasHostnameIntersection { parentRef.SetCondition(tlsRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonNoMatchingListenerHostname, + gwapiv1.RouteReasonNoMatchingListenerHostname, "There were no hostname intersections between the HTTPRoute and this parent ref's Listener(s).", ) } @@ -666,16 +703,16 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour if parentRef.TLSRoute != nil && len(parentRef.TLSRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { parentRef.SetCondition(tlsRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionTrue, - v1beta1.RouteReasonAccepted, + gwapiv1.RouteReasonAccepted, "Route is accepted", ) } } } -func (t *Translator) ProcessUDPRoutes(udpRoutes []*v1alpha2.UDPRoute, gateways []*GatewayContext, resources *Resources, +func (t *Translator) ProcessUDPRoutes(udpRoutes []*gwapiv1a1.UDPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*UDPRouteContext { var relevantUDPRoutes []*UDPRouteContext @@ -709,12 +746,12 @@ func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resour // Need to compute Route rules within the parentRef loop because // any conditions that come out of it have to go on each RouteParentStatus, // not on the Route as a whole. - var routeDestinations []*ir.RouteDestination + var destSettings []*ir.DestinationSetting // compute backends if len(udpRoute.Spec.Rules) != 1 { parentRef.SetCondition(udpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, "InvalidRule", "One and only one rule is supported", @@ -723,7 +760,7 @@ func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resour } if len(udpRoute.Spec.Rules[0].BackendRefs) != 1 { parentRef.SetCondition(udpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, "InvalidBackend", "One and only one backend is supported", @@ -732,82 +769,81 @@ func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resour } backendRef := udpRoute.Spec.Rules[0].BackendRefs[0] - destinations, _ := t.processRouteDestinations(backendRef, parentRef, udpRoute, resources) + ds, _ := t.processDestination(backendRef, parentRef, udpRoute, resources) // Skip further processing if route destination is not valid - if len(destinations) == 0 { + if ds == nil || len(ds.Endpoints) == 0 { continue } - routeDestinations = append(routeDestinations, destinations...) + destSettings = append(destSettings, ds) // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" - if !parentRef.HasCondition(udpRoute, v1beta1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + if !parentRef.HasCondition(udpRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { parentRef.SetCondition(udpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionTrue, - v1beta1.RouteReasonResolvedRefs, + gwapiv1.RouteReasonResolvedRefs, "Resolved all the Object references for the Route", ) } // Skip parent refs that did not accept the route - if parentRef.HasCondition(udpRoute, v1beta1.RouteConditionAccepted, metav1.ConditionFalse) { + if parentRef.HasCondition(udpRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { continue } accepted := false for _, listener := range parentRef.listeners { // only one route is allowed for a UDP listener - if listener.AttachedRoutes() > 0 { + if listener.AttachedRoutes() > 1 { continue } if !listener.IsReady() { continue } accepted = true - irKey := irStringKey(listener.gateway.Namespace, listener.gateway.Name) + + irKey := t.getIRKey(listener.gateway) + containerPort := servicePortToContainerPort(int32(listener.Port)) // Create the UDP Listener while parsing the UDPRoute since // the listener directly links to a routeDestination. irListener := &ir.UDPListener{ - Name: irUDPListenerName(listener, udpRoute), - Address: "0.0.0.0", - Port: uint32(containerPort), - Destinations: routeDestinations, + Name: irUDPListenerName(listener, udpRoute), + Address: "0.0.0.0", + Port: uint32(containerPort), + Destination: &ir.RouteDestination{ + Name: irRouteDestinationName(udpRoute, -1 /*rule index*/), + Settings: destSettings, + }, } gwXdsIR := xdsIR[irKey] gwXdsIR.UDP = append(gwXdsIR.UDP, irListener) - // Theoretically there should only be one parent ref per - // Route that attaches to a given Listener, so fine to just increment here, but we - // might want to check to ensure we're not double-counting. - if len(routeDestinations) > 0 { - listener.IncrementAttachedRoutes() - } } // If no negative conditions have been set, the route is considered "Accepted=True". if accepted && parentRef.UDPRoute != nil && len(parentRef.UDPRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { parentRef.SetCondition(udpRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionTrue, - v1beta1.RouteReasonAccepted, + gwapiv1.RouteReasonAccepted, "Route is accepted", ) } if !accepted { parentRef.SetCondition(udpRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "Multiple routes on the same UDP listener", ) } } } -func (t *Translator) ProcessTCPRoutes(tcpRoutes []*v1alpha2.TCPRoute, gateways []*GatewayContext, resources *Resources, +func (t *Translator) ProcessTCPRoutes(tcpRoutes []*gwapiv1a1.TCPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TCPRouteContext { var relevantTCPRoutes []*TCPRouteContext @@ -842,12 +878,12 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour // Need to compute Route rules within the parentRef loop because // any conditions that come out of it have to go on each RouteParentStatus, // not on the Route as a whole. - var routeDestinations []*ir.RouteDestination + var destSettings []*ir.DestinationSetting // compute backends if len(tcpRoute.Spec.Rules) != 1 { parentRef.SetCondition(tcpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, "InvalidRule", "One and only one rule is supported", @@ -856,7 +892,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour } if len(tcpRoute.Spec.Rules[0].BackendRefs) != 1 { parentRef.SetCondition(tcpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, "InvalidBackend", "One and only one backend is supported", @@ -865,74 +901,72 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour } backendRef := tcpRoute.Spec.Rules[0].BackendRefs[0] - destinations, _ := t.processRouteDestinations(backendRef, parentRef, tcpRoute, resources) + ds, _ := t.processDestination(backendRef, parentRef, tcpRoute, resources) // Skip further processing if route destination is not valid - if len(destinations) == 0 { + if ds == nil || len(ds.Endpoints) == 0 { continue } - routeDestinations = append(routeDestinations, destinations...) + destSettings = append(destSettings, ds) // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" - if !parentRef.HasCondition(tcpRoute, v1beta1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + if !parentRef.HasCondition(tcpRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { parentRef.SetCondition(tcpRoute, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionTrue, - v1beta1.RouteReasonResolvedRefs, + gwapiv1.RouteReasonResolvedRefs, "Resolved all the Object references for the Route", ) } // Skip parent refs that did not accept the route - if parentRef.HasCondition(tcpRoute, v1beta1.RouteConditionAccepted, metav1.ConditionFalse) { + if parentRef.HasCondition(tcpRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { continue } accepted := false for _, listener := range parentRef.listeners { // only one route is allowed for a TCP listener - if listener.AttachedRoutes() > 0 { + if listener.AttachedRoutes() > 1 { continue } if !listener.IsReady() { continue } accepted = true - irKey := irStringKey(listener.gateway.Namespace, listener.gateway.Name) + irKey := t.getIRKey(listener.gateway) + containerPort := servicePortToContainerPort(int32(listener.Port)) // Create the TCP Listener while parsing the TCPRoute since // the listener directly links to a routeDestination. irListener := &ir.TCPListener{ - Name: irTCPListenerName(listener, tcpRoute), - Address: "0.0.0.0", - Port: uint32(containerPort), - Destinations: routeDestinations, - TLS: &ir.TLS{Terminate: irTLSConfigs(listener.tlsSecrets)}, + Name: irTCPListenerName(listener, tcpRoute), + Address: "0.0.0.0", + Port: uint32(containerPort), + Destination: &ir.RouteDestination{ + Name: irRouteDestinationName(tcpRoute, -1 /*rule index*/), + Settings: destSettings, + }, + TLS: &ir.TLS{Terminate: irTLSConfigs(listener.tlsSecrets)}, } gwXdsIR := xdsIR[irKey] gwXdsIR.TCP = append(gwXdsIR.TCP, irListener) - // Theoretically there should only be one parent ref per - // Route that attaches to a given Listener, so fine to just increment here, but we - // might want to check to ensure we're not double-counting. - if len(routeDestinations) > 0 { - listener.IncrementAttachedRoutes() - } } // If no negative conditions have been set, the route is considered "Accepted=True". if accepted && parentRef.TCPRoute != nil && len(parentRef.TCPRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { parentRef.SetCondition(tcpRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionTrue, - v1beta1.RouteReasonAccepted, + gwapiv1.RouteReasonAccepted, "Route is accepted", ) } if !accepted { parentRef.SetCondition(tcpRoute, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonUnsupportedValue, + gwapiv1.RouteReasonUnsupportedValue, "Multiple routes on the same TCP listener", ) } @@ -940,41 +974,115 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour } } -// processRouteDestinations takes a backendRef and translates it into route destinations or sets error statuses and +// processDestination takes a backendRef and translates it into destination setting or sets error statuses and // returns the weight for the backend so that 500 error responses can be returned for invalid backends in // the same proportion as the backend would have otherwise received -func (t *Translator) processRouteDestinations(backendRef v1beta1.BackendRef, +func (t *Translator) processDestination(backendRef gwapiv1.BackendRef, parentRef *RouteParentContext, route RouteContext, - resources *Resources) (destinations []*ir.RouteDestination, backendWeight uint32) { + resources *Resources) (ds *ir.DestinationSetting, backendWeight uint32) { weight := uint32(1) if backendRef.Weight != nil { weight = uint32(*backendRef.Weight) } - serviceNamespace := NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()) - service := resources.GetService(serviceNamespace, string(backendRef.Name)) + backendNamespace := NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()) routeType := GetRouteType(route) - if !t.validateBackendRef(&backendRef, parentRef, route, resources, serviceNamespace, routeType) { + if !t.validateBackendRef(&backendRef, parentRef, route, resources, backendNamespace, routeType) { return nil, weight } - var dest *ir.RouteDestination - // Weights are not relevant for TCP and UDP Routes - if routeType == KindTCPRoute || routeType == KindUDPRoute { - dest = ir.NewRouteDest( - service.Spec.ClusterIP, - uint32(*backendRef.Port)) - } else { - dest = ir.NewRouteDestWithWeight( - service.Spec.ClusterIP, - uint32(*backendRef.Port), - weight) + var ( + endpoints []*ir.DestinationEndpoint + addrType *ir.DestinationAddressType + ) + protocol := inspectAppProtocolByRouteKind(routeType) + switch KindDerefOr(backendRef.Kind, KindService) { + case KindServiceImport: + serviceImport := resources.GetServiceImport(backendNamespace, string(backendRef.Name)) + var servicePort mcsapi.ServicePort + for _, port := range serviceImport.Spec.Ports { + if port.Port == int32(*backendRef.Port) { + servicePort = port + break + } + } + + if !t.EndpointRoutingDisabled { + endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, KindService)) + endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, servicePort.Protocol) + } else { + backendIps := resources.GetServiceImport(backendNamespace, string(backendRef.Name)).Spec.IPs + for _, ip := range backendIps { + ep := ir.NewDestEndpoint( + ip, + uint32(*backendRef.Port)) + endpoints = append(endpoints, ep) + } + } + case KindService: + service := resources.GetService(backendNamespace, string(backendRef.Name)) + var servicePort corev1.ServicePort + for _, port := range service.Spec.Ports { + if port.Port == int32(*backendRef.Port) { + servicePort = port + break + } + } + + // support HTTPRouteBackendProtocolH2C + if servicePort.AppProtocol != nil && + *servicePort.AppProtocol == "kubernetes.io/h2c" { + protocol = ir.HTTP2 + } + + // Route to endpoints by default + if !t.EndpointRoutingDisabled { + endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, KindService)) + endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, servicePort.Protocol) + } else { + // Fall back to Service ClusterIP routing + ep := ir.NewDestEndpoint( + service.Spec.ClusterIP, + uint32(*backendRef.Port)) + endpoints = append(endpoints, ep) + } + } + + // TODO: support mixed endpointslice address type for the same backendRef + if !t.EndpointRoutingDisabled && addrType != nil && *addrType == ir.MIXED { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1a1.RouteReasonResolvedRefs, + "Mixed endpointslice address type for the same backendRef is not supported") } - destinations = append(destinations, dest) - return destinations, weight + + ds = &ir.DestinationSetting{ + Weight: &weight, + Protocol: protocol, + Endpoints: endpoints, + AddressType: addrType, + } + return ds, weight +} + +func inspectAppProtocolByRouteKind(kind gwapiv1.Kind) ir.AppProtocol { + switch kind { + case KindUDPRoute: + return ir.UDP + case KindHTTPRoute: + return ir.HTTP + case KindTCPRoute: + return ir.TCP + case KindGRPCRoute: + return ir.GRPC + case KindTLSRoute: + return ir.HTTPS + } + return ir.TCP } // processAllowedListenersForParentRefs finds out if the route attaches to one of our @@ -998,28 +1106,18 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont if len(selectedListeners) == 0 { parentRefCtx.SetCondition(routeContext, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonNoMatchingParent, + gwapiv1.RouteReasonNoMatchingParent, "No listeners match this parent ref", ) continue } - if !HasReadyListener(selectedListeners) { - parentRefCtx.SetCondition(routeContext, - v1beta1.RouteConditionAccepted, - metav1.ConditionFalse, - "NoReadyListeners", - "There are no ready listeners for this parent ref", - ) - continue - } - var allowedListeners []*ListenerContext for _, listener := range selectedListeners { acceptedKind := GetRouteType(routeContext) - if listener.AllowsKind(v1beta1.RouteGroupKind{Group: GroupPtr(v1beta1.GroupName), Kind: acceptedKind}) && + if listener.AllowsKind(gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: acceptedKind}) && listener.AllowsNamespace(resources.GetNamespace(routeContext.GetNamespace())) { allowedListeners = append(allowedListeners, listener) } @@ -1027,22 +1125,95 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont if len(allowedListeners) == 0 { parentRefCtx.SetCondition(routeContext, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionFalse, - v1beta1.RouteReasonNotAllowedByListeners, + gwapiv1.RouteReasonNotAllowedByListeners, "No listeners included by this parent ref allowed this attachment.", ) continue } + // Its safe to increment AttachedRoutes since we've found a valid parentRef + // and the listener allows this Route kind + + // Theoretically there should only be one parent ref per + // Route that attaches to a given Listener, so fine to just increment here, but we + // might want to check to ensure we're not double-counting. + for _, listener := range allowedListeners { + listener.IncrementAttachedRoutes() + } + + if !HasReadyListener(selectedListeners) { + parentRefCtx.SetCondition(routeContext, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + "NoReadyListeners", + "There are no ready listeners for this parent ref", + ) + continue + } + parentRefCtx.SetListeners(allowedListeners...) parentRefCtx.SetCondition(routeContext, - v1beta1.RouteConditionAccepted, + gwapiv1.RouteConditionAccepted, metav1.ConditionTrue, - v1beta1.RouteReasonAccepted, + gwapiv1.RouteReasonAccepted, "Route is accepted", ) } return relevantRoute } + +func getIREndpointsFromEndpointSlices(endpointSlices []*discoveryv1.EndpointSlice, portName string, portProtocol corev1.Protocol) ([]*ir.DestinationEndpoint, *ir.DestinationAddressType) { + var ( + dstEndpoints []*ir.DestinationEndpoint + dstAddrType *ir.DestinationAddressType + ) + + addrTypeMap := make(map[ir.DestinationAddressType]int) + for _, endpointSlice := range endpointSlices { + if endpointSlice.AddressType == discoveryv1.AddressTypeFQDN { + addrTypeMap[ir.FQDN]++ + } else { + addrTypeMap[ir.IP]++ + } + endpoints := getIREndpointsFromEndpointSlice(endpointSlice, portName, portProtocol) + dstEndpoints = append(dstEndpoints, endpoints...) + } + + for addrTypeState, addrTypeCounts := range addrTypeMap { + if addrTypeCounts == len(endpointSlices) { + dstAddrType = ptr.To(addrTypeState) + break + } + } + + if len(addrTypeMap) > 0 && dstAddrType == nil { + dstAddrType = ptr.To(ir.MIXED) + } + + return dstEndpoints, dstAddrType +} + +func getIREndpointsFromEndpointSlice(endpointSlice *discoveryv1.EndpointSlice, portName string, portProtocol corev1.Protocol) []*ir.DestinationEndpoint { + var endpoints []*ir.DestinationEndpoint + for _, endpoint := range endpointSlice.Endpoints { + for _, endpointPort := range endpointSlice.Ports { + // Check if the endpoint port matches the service port + // and if endpoint is Ready + if *endpointPort.Name == portName && + *endpointPort.Protocol == portProtocol && + *endpoint.Conditions.Ready { + for _, address := range endpoint.Addresses { + ep := ir.NewDestEndpoint( + address, + uint32(*endpointPort.Port)) + endpoints = append(endpoints, ep) + } + } + } + } + + return endpoints +} diff --git a/internal/gatewayapi/runner/runner.go b/internal/gatewayapi/runner/runner.go index 7db9c05b962..835ce10fd05 100644 --- a/internal/gatewayapi/runner/runner.go +++ b/internal/gatewayapi/runner/runner.go @@ -9,10 +9,10 @@ import ( "context" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/yaml" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" extension "github.com/envoyproxy/gateway/internal/extension/types" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -41,16 +41,16 @@ func (r *Runner) Name() string { } // Start starts the gateway-api translator runner -func (r *Runner) Start(ctx context.Context) error { +func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) go r.subscribeAndTranslate(ctx) r.Logger.Info("started") - return nil + return } func (r *Runner) subscribeAndTranslate(ctx context.Context) { - message.HandleSubscription(r.ProviderResources.GatewayAPIResources.Subscribe(ctx), - func(update message.Update[string, *gatewayapi.Resources]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentGatewayAPIRunner), Message: "provider-resources"}, r.ProviderResources.GatewayAPIResources.Subscribe(ctx), + func(update message.Update[string, *gatewayapi.Resources], errChan chan error) { r.Logger.Info("received an update") val := update.Value @@ -62,7 +62,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { // Translate and publish IRs. t := &gatewayapi.Translator{ GatewayControllerName: r.Server.EnvoyGateway.Gateway.ControllerName, - GatewayClassName: v1beta1.ObjectName(update.Key), + GatewayClassName: v1.ObjectName(update.Key), GlobalRateLimitEnabled: r.EnvoyGateway.RateLimit != nil, } @@ -93,6 +93,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { for key, val := range result.InfraIR { if err := val.Validate(); err != nil { r.Logger.Error(err, "unable to validate infra ir, skipped sending it") + errChan <- err } else { r.InfraIR.Store(key, val) newKeys = append(newKeys, key) @@ -102,6 +103,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { for key, val := range result.XdsIR { if err := val.Validate(); err != nil { r.Logger.Error(err, "unable to validate xds ir, skipped sending it") + errChan <- err } else { r.XdsIR.Store(key, val) } @@ -117,30 +119,51 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { // Update Status for _, gateway := range result.Gateways { + gateway := gateway key := utils.NamespacedName(gateway) r.ProviderResources.GatewayStatuses.Store(key, &gateway.Status) } for _, httpRoute := range result.HTTPRoutes { + httpRoute := httpRoute key := utils.NamespacedName(httpRoute) r.ProviderResources.HTTPRouteStatuses.Store(key, &httpRoute.Status) } for _, grpcRoute := range result.GRPCRoutes { + grpcRoute := grpcRoute key := utils.NamespacedName(grpcRoute) r.ProviderResources.GRPCRouteStatuses.Store(key, &grpcRoute.Status) } for _, tlsRoute := range result.TLSRoutes { + tlsRoute := tlsRoute key := utils.NamespacedName(tlsRoute) r.ProviderResources.TLSRouteStatuses.Store(key, &tlsRoute.Status) } for _, tcpRoute := range result.TCPRoutes { + tcpRoute := tcpRoute key := utils.NamespacedName(tcpRoute) r.ProviderResources.TCPRouteStatuses.Store(key, &tcpRoute.Status) } for _, udpRoute := range result.UDPRoutes { + udpRoute := udpRoute key := utils.NamespacedName(udpRoute) r.ProviderResources.UDPRouteStatuses.Store(key, &udpRoute.Status) } + for _, clientTrafficPolicy := range result.ClientTrafficPolicies { + clientTrafficPolicy := clientTrafficPolicy + key := utils.NamespacedName(clientTrafficPolicy) + r.ProviderResources.ClientTrafficPolicyStatuses.Store(key, &clientTrafficPolicy.Status) + } + for _, backendTrafficPolicy := range result.BackendTrafficPolicies { + backendTrafficPolicy := backendTrafficPolicy + key := utils.NamespacedName(backendTrafficPolicy) + r.ProviderResources.BackendTrafficPolicyStatuses.Store(key, &backendTrafficPolicy.Status) + } + for _, securityPolicy := range result.SecurityPolicies { + securityPolicy := securityPolicy + key := utils.NamespacedName(securityPolicy) + r.ProviderResources.SecurityPolicyStatuses.Store(key, &securityPolicy.Status) + } }, ) r.Logger.Info("shutting down") diff --git a/internal/gatewayapi/runner/runner_test.go b/internal/gatewayapi/runner/runner_test.go index 227135896cd..b159933b508 100644 --- a/internal/gatewayapi/runner/runner_test.go +++ b/internal/gatewayapi/runner/runner_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - egv1a1cfg "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/extension/testutils" "github.com/envoyproxy/gateway/internal/ir" @@ -33,7 +33,7 @@ func TestRunner(t *testing.T) { ProviderResources: pResources, XdsIR: xdsIR, InfraIR: infraIR, - ExtensionManager: testutils.NewManager(egv1a1cfg.ExtensionManager{}), + ExtensionManager: testutils.NewManager(egv1a1.ExtensionManager{}), }) ctx := context.Background() // Start diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go new file mode 100644 index 00000000000..adcd0bd292b --- /dev/null +++ b/internal/gatewayapi/securitypolicy.go @@ -0,0 +1,596 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package gatewayapi + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/tetratelabs/multierror" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/status" +) + +func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.SecurityPolicy, + gateways []*GatewayContext, + routes []RouteContext, + resources *Resources, + xdsIR XdsIRMap) []*egv1a1.SecurityPolicy { + var res []*egv1a1.SecurityPolicy + + // Sort based on timestamp + sort.Slice(securityPolicies, func(i, j int) bool { + return securityPolicies[i].CreationTimestamp.Before(&(securityPolicies[j].CreationTimestamp)) + }) + + // First build a map out of the routes and gateways for faster lookup since users might have thousands of routes or more. + // For gateways this probably isn't quite as necessary. + routeMap := map[policyTargetRouteKey]*policyRouteTargetContext{} + for _, route := range routes { + key := policyTargetRouteKey{ + Kind: string(GetRouteType(route)), + Name: route.GetName(), + Namespace: route.GetNamespace(), + } + routeMap[key] = &policyRouteTargetContext{RouteContext: route} + } + gatewayMap := map[types.NamespacedName]*policyGatewayTargetContext{} + for _, gw := range gateways { + key := types.NamespacedName{ + Name: gw.GetName(), + Namespace: gw.GetNamespace(), + } + gatewayMap[key] = &policyGatewayTargetContext{GatewayContext: gw} + } + + // Translate + // 1. First translate Policies targeting xRoutes + // 2. Finally, the policies targeting Gateways + + // Process the policies targeting xRoutes + for _, policy := range securityPolicies { + if policy.Spec.TargetRef.Kind != KindGateway { + policy := policy.DeepCopy() + res = append(res, policy) + + // Negative statuses have already been assigned so its safe to skip + route := resolveSecurityPolicyRouteTargetRef(policy, routeMap) + if route == nil { + continue + } + + err := t.translateSecurityPolicyForRoute(policy, route, resources, xdsIR) + if err != nil { + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + status.Error2ConditionMsg(err), + ) + } else { + message := "SecurityPolicy has been accepted." + status.SetSecurityPolicyAccepted(&policy.Status, message) + } + } + } + // Process the policies targeting Gateways + for _, policy := range securityPolicies { + if policy.Spec.TargetRef.Kind == KindGateway { + policy := policy.DeepCopy() + res = append(res, policy) + + // Negative statuses have already been assigned so its safe to skip + gateway := resolveSecurityPolicyGatewayTargetRef(policy, gatewayMap) + if gateway == nil { + continue + } + + err := t.translateSecurityPolicyForGateway(policy, gateway, resources, xdsIR) + if err != nil { + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + status.Error2ConditionMsg(err), + ) + } else { + message := "SecurityPolicy has been accepted." + status.SetSecurityPolicyAccepted(&policy.Status, message) + } + } + } + + return res +} + +func resolveSecurityPolicyGatewayTargetRef( + policy *egv1a1.SecurityPolicy, + gateways map[types.NamespacedName]*policyGatewayTargetContext) *GatewayContext { + targetNs := policy.Spec.TargetRef.Namespace + // If empty, default to namespace of policy + if targetNs == nil { + targetNs = ptr.To(gwv1b1.Namespace(policy.Namespace)) + } + + // Ensure Policy and target are in the same namespace + if policy.Namespace != string(*targetNs) { + + message := fmt.Sprintf( + "Namespace:%s TargetRef.Namespace:%s, SecurityPolicy can only target a resource in the same namespace.", + policy.Namespace, *targetNs) + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + + // Find the Gateway + key := types.NamespacedName{ + Name: string(policy.Spec.TargetRef.Name), + Namespace: string(*targetNs), + } + gateway, ok := gateways[key] + + // Gateway not found + if !ok { + message := fmt.Sprintf("Gateway:%s not found.", policy.Spec.TargetRef.Name) + + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + return nil + } + + // Check if another policy targeting the same Gateway exists + if gateway.attached { + message := "Unable to target Gateway, another SecurityPolicy has already attached to it" + + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonConflicted, + message, + ) + return nil + } + + // Set context and save + gateway.attached = true + gateways[key] = gateway + + return gateway.GatewayContext +} + +func resolveSecurityPolicyRouteTargetRef( + policy *egv1a1.SecurityPolicy, + routes map[policyTargetRouteKey]*policyRouteTargetContext) RouteContext { + targetNs := policy.Spec.TargetRef.Namespace + // If empty, default to namespace of policy + if targetNs == nil { + targetNs = ptr.To(gwv1b1.Namespace(policy.Namespace)) + } + + // Ensure Policy and target are in the same namespace + if policy.Namespace != string(*targetNs) { + + message := fmt.Sprintf( + "Namespace:%s TargetRef.Namespace:%s, SecurityPolicy can only target a resource in the same namespace.", + policy.Namespace, *targetNs) + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + return nil + } + + // Check if the route exists + key := policyTargetRouteKey{ + Kind: string(policy.Spec.TargetRef.Kind), + Name: string(policy.Spec.TargetRef.Name), + Namespace: string(*targetNs), + } + route, ok := routes[key] + + // Route not found + if !ok { + message := fmt.Sprintf( + "%s/%s/%s not found.", + policy.Spec.TargetRef.Kind, + string(*targetNs), policy.Spec.TargetRef.Name) + + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + return nil + } + + // Check if another policy targeting the same xRoute exists + if route.attached { + message := fmt.Sprintf( + "Unable to target %s, another SecurityPolicy has already attached to it", + string(policy.Spec.TargetRef.Kind)) + + status.SetSecurityPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonConflicted, + message, + ) + return nil + } + + // Set context and save + route.attached = true + routes[key] = route + + return route.RouteContext +} + +func (t *Translator) translateSecurityPolicyForRoute( + policy *egv1a1.SecurityPolicy, route RouteContext, + resources *Resources, xdsIR XdsIRMap) error { + // Build IR + var ( + cors *ir.CORS + jwt *ir.JWT + oidc *ir.OIDC + basicAuth *ir.BasicAuth + err, errs error + ) + + if policy.Spec.CORS != nil { + if cors, err = t.buildCORS(policy.Spec.CORS); err != nil { + errs = multierror.Append(errs, err) + } + } + + if policy.Spec.JWT != nil { + jwt = t.buildJWT(policy.Spec.JWT) + } + + if policy.Spec.OIDC != nil { + if oidc, err = t.buildOIDC(policy, resources); err != nil { + errs = multierror.Append(errs, err) + } + } + + if policy.Spec.BasicAuth != nil { + if basicAuth, err = t.buildBasicAuth(policy, resources); err != nil { + errs = multierror.Append(errs, err) + } + } + + // Apply IR to all relevant routes + // Note: there are multiple features in a security policy, even if some of them + // are invalid, we still want to apply the valid ones. + prefix := irRoutePrefix(route) + for _, ir := range xdsIR { + for _, http := range ir.HTTP { + for _, r := range http.Routes { + // Apply if there is a match + // TODO zhaohuabing: extract a utils function to check if an HTTP + // route is associated with a Gateway API xRoute + if strings.HasPrefix(r.Name, prefix) { + r.CORS = cors + r.JWT = jwt + r.OIDC = oidc + r.BasicAuth = basicAuth + } + } + } + } + return errs +} + +func (t *Translator) translateSecurityPolicyForGateway( + policy *egv1a1.SecurityPolicy, gateway *GatewayContext, + resources *Resources, xdsIR XdsIRMap) error { + // Build IR + var ( + cors *ir.CORS + jwt *ir.JWT + oidc *ir.OIDC + basicAuth *ir.BasicAuth + err, errs error + ) + + if policy.Spec.CORS != nil { + cors, err = t.buildCORS(policy.Spec.CORS) + if err != nil { + errs = multierror.Append(errs, err) + } + } + + if policy.Spec.JWT != nil { + jwt = t.buildJWT(policy.Spec.JWT) + } + + if policy.Spec.OIDC != nil { + if oidc, err = t.buildOIDC(policy, resources); err != nil { + errs = multierror.Append(errs, err) + } + } + + if policy.Spec.BasicAuth != nil { + if basicAuth, err = t.buildBasicAuth(policy, resources); err != nil { + errs = multierror.Append(errs, err) + } + } + + // Apply IR to all the routes within the specific Gateway + // If the feature is already set, then skip it, since it must have be + // set by a policy attaching to the route + // + // Note: there are multiple features in a security policy, even if some of them + // are invalid, we still want to apply the valid ones. + irKey := t.getIRKey(gateway.Gateway) + // Should exist since we've validated this + ir := xdsIR[irKey] + + for _, http := range ir.HTTP { + for _, r := range http.Routes { + // Apply if not already set + if r.CORS == nil { + r.CORS = cors + } + if r.JWT == nil { + r.JWT = jwt + } + if r.OIDC == nil { + r.OIDC = oidc + } + if r.BasicAuth == nil { + r.BasicAuth = basicAuth + } + } + } + return errs +} + +func (t *Translator) buildCORS(cors *egv1a1.CORS) (*ir.CORS, error) { + var allowOrigins []*ir.StringMatch + + for _, origin := range cors.AllowOrigins { + origin := origin.DeepCopy() + + // matchType default to exact + matchType := egv1a1.StringMatchExact + if origin.Type != nil { + matchType = *origin.Type + } + + // TODO zhaohuabing: extract a utils function to build StringMatch + switch matchType { + case egv1a1.StringMatchExact: + allowOrigins = append(allowOrigins, &ir.StringMatch{ + Exact: &origin.Value, + }) + case egv1a1.StringMatchPrefix: + allowOrigins = append(allowOrigins, &ir.StringMatch{ + Prefix: &origin.Value, + }) + case egv1a1.StringMatchSuffix: + allowOrigins = append(allowOrigins, &ir.StringMatch{ + Suffix: &origin.Value, + }) + case egv1a1.StringMatchRegularExpression: + if err := validateRegex(origin.Value); err != nil { + return nil, err // TODO zhaohuabing: also check regex in other places + } + allowOrigins = append(allowOrigins, &ir.StringMatch{ + SafeRegex: &origin.Value, + }) + } + } + + return &ir.CORS{ + AllowOrigins: allowOrigins, + AllowMethods: cors.AllowMethods, + AllowHeaders: cors.AllowHeaders, + ExposeHeaders: cors.ExposeHeaders, + MaxAge: cors.MaxAge, + AllowCredentials: cors.AllowCredentials != nil && *cors.AllowCredentials, + }, nil +} + +func (t *Translator) buildJWT(jwt *egv1a1.JWT) *ir.JWT { + return &ir.JWT{ + Providers: jwt.Providers, + } +} + +func (t *Translator) buildOIDC( + policy *egv1a1.SecurityPolicy, + resources *Resources) (*ir.OIDC, error) { + var ( + oidc = policy.Spec.OIDC + clientSecret *v1.Secret + provider *ir.OIDCProvider + err error + ) + + from := crossNamespaceFrom{ + group: egv1a1.GroupName, + kind: KindSecurityPolicy, + namespace: policy.Namespace, + } + if clientSecret, err = t.validateSecretRef( + false, from, oidc.ClientSecret, resources); err != nil { + return nil, err + } + + clientSecretBytes, ok := clientSecret.Data[egv1a1.OIDCClientSecretKey] + if !ok || len(clientSecretBytes) == 0 { + return nil, fmt.Errorf( + "client secret not found in secret %s/%s", + clientSecret.Namespace, clientSecret.Name) + } + + // Discover the token and authorization endpoints from the issuer's + // well-known url if not explicitly specified + if provider, err = discoverEndpointsFromIssuer(&oidc.Provider); err != nil { + return nil, err + } + + if err := validateTokenEndpoint(provider.TokenEndpoint); err != nil { + return nil, err + } + scopes := appendOpenidScopeIfNotExist(oidc.Scopes) + + return &ir.OIDC{ + Provider: *provider, + ClientID: oidc.ClientID, + ClientSecret: clientSecretBytes, + Scopes: scopes, + }, nil +} + +// appendOpenidScopeIfNotExist appends the openid scope to the provided scopes +// if it is not already present. +// `openid` is a required scope for OIDC. +// see https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims +func appendOpenidScopeIfNotExist(scopes []string) []string { + const authScopeOpenID = "openid" + + hasOpenIDScope := false + for _, scope := range scopes { + if scope == authScopeOpenID { + hasOpenIDScope = true + } + } + if !hasOpenIDScope { + scopes = append(scopes, authScopeOpenID) + } + return scopes +} + +type OpenIDConfig struct { + TokenEndpoint string `json:"token_endpoint"` + AuthorizationEndpoint string `json:"authorization_endpoint"` +} + +// discoverEndpointsFromIssuer discovers the token and authorization endpoints from the issuer's well-known url +// return error if failed to fetch the well-known configuration +func discoverEndpointsFromIssuer(provider *egv1a1.OIDCProvider) (*ir.OIDCProvider, error) { + if provider.TokenEndpoint == nil || provider.AuthorizationEndpoint == nil { + tokenEndpoint, authorizationEndpoint, err := fetchEndpointsFromIssuer(provider.Issuer) + if err != nil { + return nil, fmt.Errorf("error fetching endpoints from issuer: %w", err) + } + return &ir.OIDCProvider{ + TokenEndpoint: tokenEndpoint, + AuthorizationEndpoint: authorizationEndpoint, + }, nil + } + + return &ir.OIDCProvider{ + TokenEndpoint: *provider.TokenEndpoint, + AuthorizationEndpoint: *provider.AuthorizationEndpoint, + }, nil +} + +func fetchEndpointsFromIssuer(issuerURL string) (string, string, error) { + // Fetch the OpenID configuration from the issuer URL + resp, err := http.Get(fmt.Sprintf("%s/.well-known/openid-configuration", issuerURL)) + if err != nil { + return "", "", err + } + defer resp.Body.Close() + + // Parse the OpenID configuration response + var config OpenIDConfig + err = json.NewDecoder(resp.Body).Decode(&config) + if err != nil { + return "", "", err + } + + return config.TokenEndpoint, config.AuthorizationEndpoint, nil +} + +// validateTokenEndpoint validates the token endpoint URL +func validateTokenEndpoint(tokenEndpoint string) error { + parsedURL, err := url.Parse(tokenEndpoint) + if err != nil { + return fmt.Errorf("error parsing token endpoint URL: %w", err) + } + + if parsedURL.Scheme != "https" { + return fmt.Errorf("token endpoint URL scheme must be https: %s", tokenEndpoint) + } + + if ip := net.ParseIP(parsedURL.Hostname()); ip != nil { + if v4 := ip.To4(); v4 != nil { + return fmt.Errorf("token endpoint URL must be a domain name: %s", tokenEndpoint) + } + } + + if parsedURL.Port() != "" { + _, err = strconv.Atoi(parsedURL.Port()) + if err != nil { + return fmt.Errorf("error parsing token endpoint URL port: %w", err) + } + } + return nil +} + +func (t *Translator) buildBasicAuth( + policy *egv1a1.SecurityPolicy, + resources *Resources) (*ir.BasicAuth, error) { + var ( + basicAuth = policy.Spec.BasicAuth + usersSecret *v1.Secret + err error + ) + + from := crossNamespaceFrom{ + group: egv1a1.GroupName, + kind: KindSecurityPolicy, + namespace: policy.Namespace, + } + if usersSecret, err = t.validateSecretRef( + false, from, basicAuth.Users, resources); err != nil { + return nil, err + } + + usersSecretBytes, ok := usersSecret.Data[egv1a1.BasicAuthUsersSecretKey] + if !ok || len(usersSecretBytes) == 0 { + return nil, fmt.Errorf( + "users secret not found in secret %s/%s", + usersSecret.Namespace, usersSecret.Name) + } + + if err != nil { + return nil, err + } + + return &ir.BasicAuth{Users: usersSecretBytes}, nil +} diff --git a/internal/gatewayapi/sort.go b/internal/gatewayapi/sort.go index 0fed937d42c..00a5fc6389d 100644 --- a/internal/gatewayapi/sort.go +++ b/internal/gatewayapi/sort.go @@ -16,7 +16,42 @@ type XdsIRRoutes []*ir.HTTPRoute func (x XdsIRRoutes) Len() int { return len(x) } func (x XdsIRRoutes) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x XdsIRRoutes) Less(i, j int) bool { - // 1. Sort based on characters in a matching path. + + // 1. Sort based on path match type + // Exact > PathPrefix > RegularExpression + if x[i].PathMatch != nil && x[i].PathMatch.Exact != nil { + if x[j].PathMatch != nil { + if x[j].PathMatch.Prefix != nil { + return false + } + if x[j].PathMatch.SafeRegex != nil { + return false + } + } + } + if x[i].PathMatch != nil && x[i].PathMatch.Prefix != nil { + if x[j].PathMatch != nil { + if x[j].PathMatch.Exact != nil { + return true + } + if x[j].PathMatch.SafeRegex != nil { + return false + } + } + } + if x[i].PathMatch != nil && x[i].PathMatch.SafeRegex != nil { + if x[j].PathMatch != nil { + if x[j].PathMatch.Exact != nil { + return true + } + if x[j].PathMatch.Prefix != nil { + return true + } + } + } + // Equal case + + // 2. Sort based on characters in a matching path. pCountI := pathMatchCount(x[i].PathMatch) pCountJ := pathMatchCount(x[j].PathMatch) if pCountI < pCountJ { @@ -27,7 +62,7 @@ func (x XdsIRRoutes) Less(i, j int) bool { } // Equal case - // 2. Sort based on the number of Header matches. + // 3. Sort based on the number of Header matches. hCountI := len(x[i].HeaderMatches) hCountJ := len(x[j].HeaderMatches) if hCountI < hCountJ { @@ -38,7 +73,7 @@ func (x XdsIRRoutes) Less(i, j int) bool { } // Equal case - // 3. Sort based on the number of Query param matches. + // 4. Sort based on the number of Query param matches. qCountI := len(x[i].QueryParamMatches) qCountJ := len(x[j].QueryParamMatches) return qCountI < qCountJ @@ -46,7 +81,7 @@ func (x XdsIRRoutes) Less(i, j int) bool { // sortXdsIR sorts the xdsIR based on the match precedence // defined in the Gateway API spec. -// https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteRule +// https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule func sortXdsIRMap(xdsIR XdsIRMap) { for _, irItem := range xdsIR { for _, http := range irItem.HTTP { diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml new file mode 100644 index 00000000000..ddd71197828 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml @@ -0,0 +1,133 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-as-well + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: also-target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-grpcroute-in-gateway-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: envoy-gateway + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + rules: + - matches: + - headers: + - type: Exact + name: magic + value: foo + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + - name: tcp + protocol: TCP + port: 53 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml new file mode 100644 index 00000000000..d8fe370ea6a --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml @@ -0,0 +1,382 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: also-target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: Unable to target HTTPRoute, another BackendTrafficPolicy has already + attached to it + reason: Conflicted + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-grpcroute-in-gateway-2 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-as-well + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: Unable to target Gateway, another BackendTrafficPolicy has already + attached to it + reason: Conflicted + status: "False" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + - allowedRoutes: + namespaces: + from: Same + name: tcp + port: 53 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must have TLS set when protocol is HTTPS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: magic + type: Exact + value: foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - containerPort: 10053 + name: tcp + protocol: TCP + servicePort: 53 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + headerMatches: + - distinct: false + exact: foo + name: magic + hostname: '*' + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/* diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.in.yaml new file mode 100644 index 00000000000..5f8b1a5f236 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.in.yaml @@ -0,0 +1,142 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/test2" + backendRefs: + - name: service-2 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + loadBalancer: + type: Random +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + loadBalancer: + type: ConsistentHash + consistentHash: + type: SourceIP +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + loadBalancer: + type: RoundRobin + slowStart: + window: 300s +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + loadBalancer: + type: LeastRequest + slowStart: + window: 300s diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml new file mode 100755 index 00000000000..1d680b3123a --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml @@ -0,0 +1,396 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + loadBalancer: + consistentHash: + type: SourceIP + type: ConsistentHash + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route2 + namespace: default + spec: + loadBalancer: + slowStart: + window: 5m0s + type: LeastRequest + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + loadBalancer: + type: Random + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway2 + namespace: envoy-gateway + spec: + loadBalancer: + slowStart: + window: 5m0s + type: RoundRobin + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /test2 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + loadBalancer: + random: {} + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + loadBalancer: + leastRequest: + slowStart: + window: 5m0s + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /test2 + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + loadBalancer: + consistentHash: + sourceIP: true + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.in.yaml new file mode 100644 index 00000000000..93d2e980837 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.in.yaml @@ -0,0 +1,91 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + proxyProtocol: + version: V1 +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + proxyProtocol: + version: V2 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml new file mode 100755 index 00000000000..7fcde2d8d79 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml @@ -0,0 +1,287 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + proxyProtocol: + version: V2 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + proxyProtocol: + version: V1 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + proxyProtocol: + version: V1 + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + proxyProtocol: + version: V2 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml new file mode 100644 index 00000000000..cc6fa9714ed --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml @@ -0,0 +1,111 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - sourceCIDR: + type: "Distinct" + value: 192.168.0.0/16 + limit: + requests: 20 + unit: Hour diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml new file mode 100755 index 00000000000..9a4bacb0e02 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml @@ -0,0 +1,327 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + rateLimit: + global: + rules: + - clientSelectors: + - sourceCIDR: + type: Distinct + value: 192.168.0.0/16 + limit: + requests: 20 + unit: Hour + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: BackendTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + rateLimit: + global: + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + - distinct: true + name: x-org-id + limit: + requests: 10 + unit: Hour + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + rateLimit: + global: + rules: + - cidrMatch: + cidr: 192.168.0.0/16 + distinct: true + ipv6: false + maskLen: 16 + headerMatches: [] + limit: + requests: 20 + unit: Hour diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-proxyprotocol.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-proxyprotocol.in.yaml new file mode 100644 index 00000000000..5198e82d098 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-proxyprotocol.in.yaml @@ -0,0 +1,35 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-section-http-1 + spec: + enableProxyProtocol: true + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + sectionName: http-1 + namespace: envoy-gateway +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: http-2 + protocol: HTTP + port: 8080 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-proxyprotocol.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-proxyprotocol.out.yaml new file mode 100755 index 00000000000..f1541f231b7 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-proxyprotocol.out.yaml @@ -0,0 +1,130 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-section-http-1 + namespace: envoy-gateway + spec: + enableProxyProtocol: true + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http-1 + status: + conditions: + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: http-2 + port: 8080 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + - containerPort: 8080 + name: http-2 + protocol: HTTP + servicePort: 8080 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + enableProxyProtocol: true + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + port: 10080 + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + port: 8080 diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.in.yaml new file mode 100644 index 00000000000..f0ba0ecc141 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.in.yaml @@ -0,0 +1,111 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-as-well + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-2-all-sections + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-2-http + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-2-https + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: https +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-2-https-as-well + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: https +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + - name: tcp + protocol: TCP + port: 53 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml new file mode 100644 index 00000000000..413b622c743 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml @@ -0,0 +1,314 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-2-http + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + status: + conditions: + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-2-https + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: https + status: + conditions: + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-2-https-as-well + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: https + status: + conditions: + - lastTransitionTime: null + message: Unable to target section, another ClientTrafficPolicy has already attached + to it + reason: Conflicted + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-as-well + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: Unable to target Gateway, another ClientTrafficPolicy has already attached + to it + reason: Conflicted + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-2-all-sections + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: There are existing ClientTrafficPolicies that are overriding these + sections [http https] + reason: Overridden + status: "True" + type: Overridden + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + - allowedRoutes: + namespaces: + from: Same + name: tcp + port: 53 + protocol: TCP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Listener must have TLS set when protocol is HTTPS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - containerPort: 10053 + name: tcp + protocol: TCP + servicePort: 53 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml new file mode 100644 index 00000000000..d263b45df77 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml @@ -0,0 +1,50 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + tcpKeepalive: {} + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-section-http-1 + spec: + tcpKeepalive: + probes: 3 + idleTime: 20m + interval: 60s + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + sectionName: http-1 + namespace: envoy-gateway +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: http-2 + protocol: HTTP + port: 8080 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml new file mode 100755 index 00000000000..df173dba7d5 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml @@ -0,0 +1,163 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-section-http-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http-1 + tcpKeepalive: + idleTime: 20m + interval: 60s + probes: 3 + status: + conditions: + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + tcpKeepalive: {} + status: + conditions: + - lastTransitionTime: null + message: There are existing ClientTrafficPolicies that are overriding these + sections [http-1] + reason: Overridden + status: "True" + type: Overridden + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: http-2 + port: 8080 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + - containerPort: 8080 + name: http-2 + protocol: HTTP + servicePort: 8080 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + port: 10080 + tcpKeepalive: + idleTime: 1200 + interval: 60 + probes: 3 + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + port: 8080 + tcpKeepalive: {} diff --git a/internal/gatewayapi/testdata/disable-accesslog.in.yaml b/internal/gatewayapi/testdata/disable-accesslog.in.yaml index f7375a882d1..206f0d46b2c 100644 --- a/internal/gatewayapi/testdata/disable-accesslog.in.yaml +++ b/internal/gatewayapi/testdata/disable-accesslog.in.yaml @@ -1,5 +1,5 @@ envoyproxy: - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: namespace: envoy-gateway-system @@ -57,7 +57,7 @@ envoyproxy: secret: secretName: envoy-cert gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/disable-accesslog.out.yaml b/internal/gatewayapi/testdata/disable-accesslog.out.yaml index 65984f568ad..39e8d26d571 100644 --- a/internal/gatewayapi/testdata/disable-accesslog.out.yaml +++ b/internal/gatewayapi/testdata/disable-accesslog.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -38,7 +43,7 @@ infraIR: envoy-gateway/gateway-1: proxy: config: - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml index e44bc28e254..ee6050b5118 100644 --- a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml @@ -19,7 +19,7 @@ envoyPatchPolicies: path: "/per_connection_buffer_limit_bytes" value: "1024" gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml index 52ea692fbde..f7293e04c96 100755 --- a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml index 7506737e8c2..57a5d4abd14 100644 --- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml @@ -19,7 +19,7 @@ envoyPatchPolicies: path: "/per_connection_buffer_limit_bytes" value: "1024" gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml index 17fb062c207..b5d451c716b 100755 --- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-valid.in.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-valid.in.yaml index 59c899834de..4cbe335873b 100644 --- a/internal/gatewayapi/testdata/envoypatchpolicy-valid.in.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-valid.in.yaml @@ -40,7 +40,7 @@ envoyPatchPolicies: path: "/ignore_global_conn_limit" value: "true" gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml index 06fc756204e..5c75624e136 100644 --- a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.in.yaml new file mode 100644 index 00000000000..3b1940f1fbd --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.in.yaml @@ -0,0 +1,79 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + sinks: + - type: File + file: + path: /dev/stdout + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy-dev:latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml new file mode 100644 index 00000000000..d7032df5b1a --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml @@ -0,0 +1,133 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy-dev:latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + type: JSON + sinks: + - file: + path: /dev/stdout + type: File + status: {} + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: {} + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json.in.yaml new file mode 100644 index 00000000000..2bf2dc6ae59 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json.in.yaml @@ -0,0 +1,82 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + json: + protocol: "%PROTOCOL%" + duration: "%DURATION%" + sinks: + - type: File + file: + path: /dev/stdout + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy-dev:latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json.out.yaml new file mode 100644 index 00000000000..4e3a60ba8f3 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json.out.yaml @@ -0,0 +1,141 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy-dev:latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + json: + duration: '%DURATION%' + protocol: '%PROTOCOL%' + type: JSON + sinks: + - file: + path: /dev/stdout + type: File + status: {} + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - json: + duration: '%DURATION%' + protocol: '%PROTOCOL%' + path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-with-bad-sinks.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-with-bad-sinks.in.yaml new file mode 100644 index 00000000000..4cfbeea2e6d --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-with-bad-sinks.in.yaml @@ -0,0 +1,80 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + sinks: + - type: File + - type: OpenTelemetry + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy-dev:latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-with-bad-sinks.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-with-bad-sinks.out.yaml new file mode 100644 index 00000000000..b4611b897da --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-with-bad-sinks.out.yaml @@ -0,0 +1,134 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy-dev:latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + type: Text + sinks: + - type: File + - type: OpenTelemetry + status: {} + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: {} + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog.in.yaml index 7ecc903328c..fe311ea0a59 100644 --- a/internal/gatewayapi/testdata/envoyproxy-accesslog.in.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog.in.yaml @@ -1,5 +1,5 @@ envoyproxy: - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: namespace: envoy-gateway-system @@ -71,7 +71,7 @@ envoyproxy: secret: secretName: envoy-cert gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml index c87b1e8d279..01428c425e2 100644 --- a/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -38,7 +43,7 @@ infraIR: envoy-gateway/gateway-1: proxy: config: - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/envoyproxy-valid.in.yaml b/internal/gatewayapi/testdata/envoyproxy-valid.in.yaml index 2c1f82273ad..172af475b30 100644 --- a/internal/gatewayapi/testdata/envoyproxy-valid.in.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-valid.in.yaml @@ -1,5 +1,5 @@ envoyproxy: - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: namespace: envoy-gateway-system @@ -54,7 +54,7 @@ envoyproxy: secret: secretName: envoy-cert gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/envoyproxy-valid.out.yaml b/internal/gatewayapi/testdata/envoyproxy-valid.out.yaml index 3deb8244e45..9157ee69062 100644 --- a/internal/gatewayapi/testdata/envoyproxy-valid.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-valid.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -38,7 +43,7 @@ infraIR: envoy-gateway/gateway-1: proxy: config: - apiVersion: config.gateway.envoyproxy.io/v1alpha1 + apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: creationTimestamp: null @@ -94,7 +99,6 @@ infraIR: envoyService: type: LoadBalancer type: Kubernetes - telemetry: {} status: {} listeners: - address: "" diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml index 99c9bcac4f7..ba7eec6c05d 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml index 6cb42b8480a..07b9b2ebd37 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml index 41bc18284be..d4f0fc1f9e5 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml index 58be9d1583b..cabc3be656a 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml index 7fadc617592..5d73bfa1b4c 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml index 306004acac6..6aa7961381b 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.in.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.in.yaml index 2b236fd55da..d44fa65086b 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.in.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.out.yaml b/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.out.yaml index 156d913fc18..08c8a7db6c3 100644 --- a/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.out.yaml +++ b/internal/gatewayapi/testdata/extensions/httproute-with-valid-extension-filter.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -111,10 +116,15 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 extensionRefs: - object: apiVersion: foo.example.io/v1alpha1 @@ -124,11 +134,8 @@ xdsIR: namespace: default spec: data: stuff - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml index 0d10f99dfda..0dd2519dc9d 100644 --- a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml +++ b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: Same httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: envoy-gateway @@ -37,6 +37,27 @@ services: namespace: envoy-gateway name: service-1 spec: - clusterIP: 7.7.7.7 + clusterIP: 1.1.1.1 ports: - - port: 8080 + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: envoy-gateway + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml index a92d9e5c43d..31f8b24bd78 100644 --- a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml +++ b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -100,11 +105,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: envoy-gateway/httproute-1/rule/0/match/0-* + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml index 8ba0171af8b..40c54eff3c3 100644 --- a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml +++ b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: Same httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml index e5741015029..0c17d8dd89f 100644 --- a/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml +++ b/internal/gatewayapi/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-infrastructure.in.yaml b/internal/gatewayapi/testdata/gateway-infrastructure.in.yaml new file mode 100644 index 00000000000..0ce21aaf14e --- /dev/null +++ b/internal/gatewayapi/testdata/gateway-infrastructure.in.yaml @@ -0,0 +1,76 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + infrastructure: + labels: + infra-label1: infra-val1 + infra-label2: infra-val2 + annotations: + infra-anno-key1: infra-anno-val1 + infra-anno-key2: infra-anno-val2 + listeners: + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + status: + listeners: + - name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated + - name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + diff --git a/internal/gatewayapi/testdata/gateway-infrastructure.out.yaml b/internal/gatewayapi/testdata/gateway-infrastructure.out.yaml new file mode 100644 index 00000000000..381fa22a6ce --- /dev/null +++ b/internal/gatewayapi/testdata/gateway-infrastructure.out.yaml @@ -0,0 +1,144 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + infrastructure: + annotations: + infra-anno-key1: infra-anno-val1 + infra-anno-key2: infra-anno-val2 + labels: + infra-label1: infra-val1 + infra-label2: infra-val2 + listeners: + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default +infraIR: + default/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10443 + name: https + protocol: HTTPS + servicePort: 443 + metadata: + annotations: + infra-anno-key1: infra-anno-val1 + infra-anno-key2: infra-anno-val2 + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: default + infra-label1: infra-val1 + infra-label2: infra-val2 + name: default/gateway-1 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/https + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + - name: default-tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.in.yaml b/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.in.yaml index 1be00724e66..e4194dd153c 100644 --- a/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.out.yaml b/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.out.yaml index bd6fa4c9d37..160fdae760e 100644 --- a/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-addresses-with-ipaddress.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -32,6 +32,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml index dace4dd1e13..7c71b1eaa65 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -20,7 +20,7 @@ gateways: values: - bar httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml index a5ff0f7fd11..815c262ebd9 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -31,6 +31,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -38,7 +43,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -59,8 +64,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml index bb2bf841df6..e31eab26962 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -17,7 +17,7 @@ gateways: - group: foo.io kind: HTTPRoute httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml index d58e4016331..272e1e59d1d 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,7 +34,7 @@ gateways: name: http supportedKinds: [] httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -55,8 +55,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml index eaa066350a7..3c1d72a73fa 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -19,7 +19,7 @@ gateways: - group: kind: HTTPRoute httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml index 7743c4dbdee..abb272c5906 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -36,7 +36,7 @@ gateways: supportedKinds: - kind: HTTPRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -57,8 +57,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml index 70c85d2b781..dd3061ecf2a 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -17,7 +17,7 @@ gateways: - group: gateway.networking.k8s.io kind: FooRoute httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml index fff2f75e09f..26d322683a2 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,7 +34,7 @@ gateways: name: http supportedKinds: [] httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -55,8 +55,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml index c5762afbaff..ee989f7fa0f 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml index 5bbb66e3139..9d4732d0c8f 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -65,8 +65,8 @@ tlsRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml index 6418ac1093d..c0239e032f6 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -48,7 +48,7 @@ secrets: data: tls.key: YmFyCg== httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml index af5098c227e..c9bae2909cc 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,11 +28,11 @@ gateways: mode: Terminate status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Secret envoy-gateway/tls-secret-ecdsa-2 public key algorithm must - be unique, matched cerificate FQDN [foo.bar.com] has a conficting algorithm + be unique, matched certificate FQDN [foo.bar.com] has a conflicting algorithm [ECDSA]. reason: InvalidCertificateRef status: "False" @@ -49,7 +49,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml index a4793ddeff2..e0f69d01a32 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -29,7 +29,7 @@ secrets: tls.crt: Zm9vCg== tls.key: YmFyCg== httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml index 02bfac71a4c..70682af8625 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -23,13 +23,18 @@ gateways: mode: Passthrough status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: TLS Passthrough mode is not supported, TLS mode must be Terminate. reason: UnsupportedTLSMode status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -37,7 +42,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml index d60c39b55c6..54feff74698 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -26,7 +26,7 @@ secrets: tls.crt: Zm9vCg== tls.key: YmFyCg== httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml index 031633dc5a7..159e8c72d9c 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -18,13 +18,18 @@ gateways: mode: Terminate status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Listener must have at least 1 TLS certificate ref reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -32,7 +37,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml index 5689e9fedea..1833950ff0c 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -29,7 +29,7 @@ secrets: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml index 6bd21582eb4..aa5914796dd 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -23,7 +23,7 @@ gateways: mode: Terminate status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Secret envoy-gateway/tls-secret-1 must contain valid tls.crt and @@ -44,7 +44,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml index 0b68948ea73..95bffc38bbe 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -18,7 +18,7 @@ gateways: certificateRefs: - name: tls-secret-1 httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml index c58c5f1a2ed..ad32e3bdaf2 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -22,7 +22,7 @@ gateways: mode: Terminate status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Secret envoy-gateway/tls-secret-1 does not exist. @@ -41,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml index 0bee2931391..caab5079f9c 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -29,7 +29,7 @@ secrets: tls.crt: Zm9vCg== tls.key: YmFyCg== httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml index 9975d4e9c27..688333bf458 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -23,7 +23,7 @@ gateways: mode: Terminate status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Certificate ref to secret default/tls-secret-1 not permitted by any @@ -43,7 +43,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml index c1ff4fb2c61..75e6051a21b 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -27,7 +27,7 @@ secrets: data: tls.key: YmFyCg== httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml index b50ec90c584..3223d24b870 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -22,7 +22,7 @@ gateways: mode: Terminate status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Secret envoy-gateway/tls-secret-1 must contain tls.crt and tls.key. @@ -41,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml index 6d2bed7a339..f24ab41fe4d 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: Selector httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml index 350d400c6c1..92069b626a0 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -24,6 +24,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -31,7 +36,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -52,8 +57,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml index 1be8e3b6948..f027c23e67f 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml index 8f26e8bea8e..d6d72c55452 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -24,6 +24,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml index 049bb0ea059..1cc29af9d38 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml index 273b92ef18e..b3adde9cae1 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: TCP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml index 3e5271710c3..f0291a53162 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml index 3e89dd2c013..010f8b93234 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: TCP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml index 52cfed69655..179769001d8 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml index 1d4c80ca131..074594d2e87 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: TCP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml index f61fc1b98a6..702318e694c 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -29,7 +29,7 @@ secrets: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml index 341a97b69e1..16ddceed2d2 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -35,6 +35,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -42,7 +47,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -107,11 +112,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml index 910faa3e9ef..233c609eea1 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -42,7 +42,7 @@ tlsRoutes: - name: service-2 port: 8080 httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml index 2b8b024d9d5..8be3e07c12d 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -44,6 +44,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls-passthrough supportedKinds: - group: gateway.networking.k8s.io @@ -60,6 +65,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls-terminate supportedKinds: - group: gateway.networking.k8s.io @@ -67,7 +77,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -168,11 +178,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo.bar.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.bar.com + name: httproute/default/httproute-1/rule/0/match/0/foo_bar_com pathMatch: distinct: false name: "" @@ -183,10 +199,15 @@ xdsIR: serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls-passthrough/tlsroute-1 port: 10090 tls: diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml index 5da384704ec..f8e9d3859d8 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml index e537dfd6acc..cdec1d56490 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -24,6 +24,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml index 3f59d61eb8e..440e8c9fd55 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml index 9d113ae74e7..543cc4143a9 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: UDP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml index 7b8bad988c2..6dfe97fba5e 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml index 8e913bbf902..7c438f3b264 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: UDP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml index 1cf9356840b..ecb2d33e09f 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml index 838561f4103..04160fa4e4d 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: UDP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml index dc4f8e80521..bba95249356 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml index ce090506b4a..16f687f3ce2 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml index a1699083121..2f7fa01b58c 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml index 6aba5cb0bfd..f5d768207dc 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml index 7fe1283b99c..12598d5c297 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml index 64ca07aeec0..b13be2100ce 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,10 +28,15 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: unsupported supportedKinds: null httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -52,8 +57,8 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: There are no ready listeners for this parent ref - reason: NoReadyListeners + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml index 4bb8c828d3d..2d042047881 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -38,7 +38,7 @@ secrets: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZakNCNlFJSkFNcDhYTGNsWGJ2NU1Bb0dDQ3FHU000OUJBTUNNQnN4R1RBWEJnTlZCQU1NRUhSbGMzUXUKWlhoaGJYQnNaUzVqYjIwd0hoY05Nak13TlRJMU1UUXhNRFF5V2hjTk1qUXdOVEkwTVRReE1EUXlXakFiTVJrdwpGd1lEVlFRRERCQjBaWE4wTG1WNFlXMXdiR1V1WTI5dE1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFCmVNdEhDM2hJZXEyUXNka3RTaS9aTlIvTUtOYXZTbTNITm43dEdvZ2ZxYXFuOEFTZ0hJYTd5VVUrK1Bzb0RENmsKZisrQ2U3dXNkMG1RTzFTbmRZSVdqMlFMaUl6ME5aSDhCL1FyNGk3SjBJS3dzUWxVbnVuWnF2NUtZOVRMUWEwbgpNQW9HQ0NxR1NNNDlCQU1DQTJnQU1HVUNNUUNHVGpQa2hqZE1KcWUrVjFGRWpteUk2andEV0FDalhucFRuaXhVCnM2dUNKZjVNNUw1TmpGYmkydGplMGlES0UxVUNNQXdxSjZmOUs2bUhUd2JxVGkzTVNFMmQxODl6aUcyWVZCaUQKYmVXcHc0UjF5ZTdHRFJvVm9veG9ob2lERjdsNm13PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1JR2tBZ0VCQkRDUUE5VWo0SkR5c0Q0MlJIMGI2cjU5NTlXTmlXU2ZKZlMxK2RvTjk0TzZCUGdaQUJiUTI4eTIKUTZsM3pZdklLeFNnQndZRks0RUVBQ0toWkFOaUFBUjR5MGNMZUVoNnJaQ3gyUzFLTDlrMUg4d28xcTlLYmNjMgpmdTBhaUIrcHFxZndCS0FjaHJ2SlJUNzQreWdNUHFSLzc0Sjd1NngzU1pBN1ZLZDFnaGFQWkF1SWpQUTFrZndICjlDdmlMc25RZ3JDeENWU2U2ZG1xL2twajFNdEJyU2M9Ci0tLS0tRU5EIEVDIFBSSVZBVEUgS0VZLS0tLS0K httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml index 3ce47ff4f41..9334f4bdedf 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -37,6 +37,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -44,7 +49,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -109,11 +114,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml index 36725caf65c..940d551a501 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -38,7 +38,7 @@ secrets: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJHekNCd2dJSkFJL3gxc0NEL0lSa01Bb0dDQ3FHU000OUJBTUNNQll4RkRBU0JnTlZCQU1NQzJadmJ5NWkKWVhJdVkyOXRNQjRYRFRJek1ERXdOVEl4TlRNeU9Wb1hEVEkwTURFd05USXhOVE15T1Zvd0ZqRVVNQklHQTFVRQpBd3dMWm05dkxtSmhjaTVqYjIwd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRZmxXMnR6T3M4Cm82Nk5USVRmYUhucEc2UzI3Y2hkSldzallKdWJISkFtT2tYdS8rVGxLTXhGUzl4R2tHK09BQjAyR0ZQaHhQVzcKRnFaTFJoOEp0YlZkTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFEdlplU1pBZ1VWV2VpM3o0ZEhOTEd0aHpiaQoxRHZ0anRQekhYZ1R3WS92YmdJZ05KWStNcTRweFJnNit3eU04R1R4czVUV3k5Zml5RGhMUEU5QnhlbEsxSjQ9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxEbnZNM1RKM3NHYm9EeTF4T3dqSVppVFNWeWZXVWF5YVExcWdrdUdacEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSDVWdHJjenJQS091alV5RTMyaDU2UnVrdHUzSVhTVnJJMkNibXh5UUpqcEY3di9rNVNqTQpSVXZjUnBCdmpnQWROaGhUNGNUMXV4YW1TMFlmQ2JXMVhRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml index c07cecf90b3..6fa27470212 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -37,6 +37,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -44,7 +49,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -109,11 +114,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml index 58feff7c6ed..e85e71e7482 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -28,7 +28,7 @@ secrets: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml index 5ec1bf518d9..fb67c97d083 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,6 +34,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -41,7 +46,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -106,11 +111,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.in.yaml b/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.in.yaml index 1e3c087a464..88a3c06d4ba 100644 --- a/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: default @@ -26,7 +26,7 @@ gateways: reason: Programmed message: Listener has been successfully translated httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.out.yaml b/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.out.yaml index 46d29ce1318..debddc26ef1 100644 --- a/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-preexisting-status-condition.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -100,11 +105,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml index 13195729173..27fc4622a0d 100644 --- a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml index 57a1e355642..05834897096 100644 --- a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: TCP status: listeners: - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io @@ -117,9 +122,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp/tcproute-1 port: 10162 tls: {} diff --git a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml index a5c8561df8e..0d8be290550 100644 --- a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml index 493a997b45a..6960c6dd1c9 100644 --- a/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: UDP status: listeners: - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io @@ -117,8 +122,14 @@ xdsIR: - path: /dev/stdout udp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp/udproute-1 port: 10162 diff --git a/internal/gatewayapi/testdata/gateway-with-stale-status-condition.in.yaml b/internal/gatewayapi/testdata/gateway-with-stale-status-condition.in.yaml index 96b48f3e7b7..e1020d2369a 100644 --- a/internal/gatewayapi/testdata/gateway-with-stale-status-condition.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-stale-status-condition.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: default @@ -40,7 +40,7 @@ gateways: reason: Programmed message: Listener has been successfully translated httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-stale-status-condition.out.yaml b/internal/gatewayapi/testdata/gateway-with-stale-status-condition.out.yaml index c6ea094bc25..3a2252557e1 100644 --- a/internal/gatewayapi/testdata/gateway-with-stale-status-condition.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-stale-status-condition.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,6 +34,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: https supportedKinds: - group: gateway.networking.k8s.io @@ -41,7 +46,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -106,11 +111,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml index fc0744606b6..049c7974138 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml index 1b8999d17c6..0d516c4f69c 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,11 +34,16 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp1 supportedKinds: - group: gateway.networking.k8s.io kind: TCPRoute - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Only one TCP/TLS listener is allowed in a given port @@ -50,6 +55,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp2 supportedKinds: - group: gateway.networking.k8s.io @@ -110,9 +120,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp1/tcproute-1 port: 10162 tls: {} diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml index b76a4678043..50601282fce 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml index e6a9a148b64..374211cbfe6 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,11 +34,16 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp1 supportedKinds: - group: gateway.networking.k8s.io kind: UDPRoute - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Only one UDP listener is allowed in a given port @@ -50,6 +55,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp2 supportedKinds: - group: gateway.networking.k8s.io @@ -108,8 +118,14 @@ xdsIR: - path: /dev/stdout udp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp1/udproute-1 port: 10162 diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml index e3bfa502356..90fe76523f8 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -24,7 +24,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml index 7354c4bca16..5667423dfce 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -26,7 +26,7 @@ gateways: mode: Passthrough status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: All listeners for a given port must use a unique hostname @@ -38,13 +38,18 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io kind: HTTPRoute - group: gateway.networking.k8s.io kind: GRPCRoute - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: All listeners for a given port must use a unique hostname @@ -56,12 +61,17 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls-1 supportedKinds: - group: gateway.networking.k8s.io kind: TLSRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml index 65b6ed82eba..d0cadfda02c 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -22,7 +22,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default @@ -38,7 +38,7 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml index 275f2ef6036..d189fd83504 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -36,6 +36,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io @@ -54,6 +59,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -61,7 +71,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -95,7 +105,7 @@ httpRoutes: parentRef: name: gateway-1 namespace: envoy-gateway -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -164,11 +174,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-2/rule/0/match/0-foo.com + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + name: httproute/default/httproute-2/rule/0/match/0/foo_com pathMatch: distinct: false name: "" @@ -176,11 +192,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + name: httproute/default/httproute-1/rule/0/match/0/foo_com pathMatch: distinct: false name: "" @@ -195,11 +217,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-2/rule/0/match/0-bar.com + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + name: httproute/default/httproute-2/rule/0/match/0/bar_com pathMatch: distinct: false name: "" @@ -207,11 +235,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + name: httproute/default/httproute-1/rule/0/match/0/bar_com pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml index 69b6f58c0b8..69b93f3b1a6 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -22,7 +22,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml index 1db35a718b7..5ef4071203b 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -24,7 +24,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: All listeners for a given port must use a unique hostname @@ -36,13 +36,18 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io kind: HTTPRoute - group: gateway.networking.k8s.io kind: GRPCRoute - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: All listeners for a given port must use a unique hostname @@ -54,6 +59,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -61,7 +71,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml index 981e2604510..37c0a342f3b 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -22,7 +22,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml index bf38a499518..381b05384f0 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -24,7 +24,7 @@ gateways: protocol: HTTPS status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: All listeners for a given port must use a compatible protocol @@ -36,13 +36,18 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io kind: HTTPRoute - group: gateway.networking.k8s.io kind: GRPCRoute - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: All listeners for a given port must use a compatible protocol @@ -54,6 +59,11 @@ gateways: reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -61,7 +71,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml index a7296205001..34bd7a43e6f 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -21,7 +21,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml index aaa119a831e..548a88854f7 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -35,6 +35,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -53,12 +58,17 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp supportedKinds: - group: gateway.networking.k8s.io kind: TCPRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -159,20 +169,32 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" prefix: / tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp/tcproute-1 port: 10080 tls: {} diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml index 9fc9e95903f..d4ef5be9e3f 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -21,7 +21,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml index 281b56f0183..b13db8d577a 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -35,6 +35,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -53,12 +58,17 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp supportedKinds: - group: gateway.networking.k8s.io kind: UDPRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -159,19 +169,31 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" prefix: / udp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp/udproute-1 port: 10080 diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml index dbdd26a3d06..a4bb69bb764 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml index 7b9b015a0c7..8ac3af7eec4 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,6 +34,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp1 supportedKinds: - group: gateway.networking.k8s.io @@ -50,6 +55,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp2 supportedKinds: - group: gateway.networking.k8s.io @@ -147,16 +157,28 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp1/tcproute-1 port: 10162 tls: {} - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-2/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp2/tcproute-2 port: 10163 tls: {} diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml index 773c926237d..1ba01ba6cd5 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml index b07f780ddb7..2d7dee34f08 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -22,7 +22,7 @@ gateways: protocol: TCP status: listeners: - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -34,11 +34,16 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp1 supportedKinds: - group: gateway.networking.k8s.io kind: TCPRoute - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -50,6 +55,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tcp2 supportedKinds: - group: gateway.networking.k8s.io @@ -143,16 +153,28 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp1/tcproute-1 port: 10161 tls: {} - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8163 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tcp2/tcproute-1 port: 10162 tls: {} diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml index ab1cc8facb1..dc3a42ab705 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml index d75610332ae..903811f8453 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,6 +34,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp1 supportedKinds: - group: gateway.networking.k8s.io @@ -50,6 +55,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp2 supportedKinds: - group: gateway.networking.k8s.io @@ -147,14 +157,26 @@ xdsIR: - path: /dev/stdout udp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp1/udproute-1 port: 10162 - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-2/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp2/udproute-2 port: 10163 diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml index 1922d9353d1..2638f0f19de 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml index dc5bb894ffe..07d09bb7745 100644 --- a/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -22,7 +22,7 @@ gateways: protocol: UDP status: listeners: - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -34,11 +34,16 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp1 supportedKinds: - group: gateway.networking.k8s.io kind: UDPRoute - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -50,6 +55,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: udp2 supportedKinds: - group: gateway.networking.k8s.io @@ -143,14 +153,26 @@ xdsIR: - path: /dev/stdout udp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp1/udproute-1 port: 10161 - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8162 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 name: envoy-gateway/gateway-1/udp2/udproute-1 port: 10162 diff --git a/internal/gatewayapi/testdata/grpcroute-with-header-match.in.yaml b/internal/gatewayapi/testdata/grpcroute-with-header-match.in.yaml index b6078734a8d..ae2baa8dca1 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-header-match.in.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-header-match.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/grpcroute-with-header-match.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-header-match.out.yaml index 7c0fe46dba3..1d159ce0a3d 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-header-match.out.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-header-match.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -104,12 +109,18 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 headerMatches: - distinct: false exact: foo name: magic - name: default/grpcroute-1/rule/0/match/0-* + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/0/* diff --git a/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.in.yaml b/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.in.yaml index b7da2bc9300..82e319fd264 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.in.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.out.yaml index 2260eefaea6..c7967051d4f 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.out.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-method-and-service-match.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -108,11 +113,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/grpcroute-1/rule/0/match/0-* + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/0/* pathMatch: distinct: false exact: /com.example/Example @@ -120,11 +131,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/grpcroute-1/rule/0/match/1-* + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/1/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/grpcroute-with-method-match.in.yaml b/internal/gatewayapi/testdata/grpcroute-with-method-match.in.yaml index a95d5fab433..e2a374d149e 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-method-match.in.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-method-match.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/grpcroute-with-method-match.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-method-match.out.yaml index 8984e6aa5c3..68fa394e108 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-method-match.out.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-method-match.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -106,11 +111,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/grpcroute-1/rule/0/match/1-* + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/1/* pathMatch: distinct: false name: "" @@ -118,12 +129,18 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 headerMatches: - distinct: false name: :path suffix: /ExampleExact - name: default/grpcroute-1/rule/0/match/0-* + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/0/* diff --git a/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.in.yaml b/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.in.yaml index 56dbf4ed110..2c48dad582e 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.in.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.out.yaml index 9b19d6268d3..7e38ea0d9bc 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.out.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-request-header-modifier.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -109,8 +114,14 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/grpcroute-1/rule/0/match/-1-* + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* diff --git a/internal/gatewayapi/testdata/grpcroute-with-service-match.in.yaml b/internal/gatewayapi/testdata/grpcroute-with-service-match.in.yaml index 912c2f9c4b4..ad245cb182c 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-service-match.in.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-service-match.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/grpcroute-with-service-match.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-service-match.out.yaml index 2f3d10b2a92..498488b8af9 100644 --- a/internal/gatewayapi/testdata/grpcroute-with-service-match.out.yaml +++ b/internal/gatewayapi/testdata/grpcroute-with-service-match.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -106,24 +111,36 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/grpcroute-1/rule/0/match/1-* + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/0/* pathMatch: distinct: false name: "" - safeRegex: /com.[A-Z]+/[A-Za-z_][A-Za-z_0-9]* + prefix: /com.ExampleExact - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/grpcroute-1/rule/0/match/0-* + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/1/* pathMatch: distinct: false name: "" - prefix: /com.ExampleExact + safeRegex: /com.[A-Z]+/[A-Za-z_][A-Za-z_0-9]* diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml index 7ec5ee38330..d44d02d168f 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -64,7 +64,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml index 6969c22af33..c439cfe5eed 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -78,6 +78,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io @@ -96,6 +101,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -114,6 +124,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-3 supportedKinds: - group: gateway.networking.k8s.io @@ -132,6 +147,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-4 supportedKinds: - group: gateway.networking.k8s.io @@ -150,6 +170,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-5 supportedKinds: - group: gateway.networking.k8s.io @@ -168,6 +193,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-6 supportedKinds: - group: gateway.networking.k8s.io @@ -186,6 +216,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-7 supportedKinds: - group: gateway.networking.k8s.io @@ -204,6 +239,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-8 supportedKinds: - group: gateway.networking.k8s.io @@ -211,7 +251,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -304,11 +344,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + name: httproute/default/httproute-1/rule/0/match/0/foo_com pathMatch: distinct: false name: "" @@ -323,11 +369,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + name: httproute/default/httproute-1/rule/0/match/0/bar_com pathMatch: distinct: false name: "" @@ -342,11 +394,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo1.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo1.com + name: httproute/default/httproute-1/rule/0/match/0/foo1_com pathMatch: distinct: false name: "" @@ -361,11 +419,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar1.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar1.com + name: httproute/default/httproute-1/rule/0/match/0/bar1_com pathMatch: distinct: false name: "" @@ -380,11 +444,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo2.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo2.com + name: httproute/default/httproute-1/rule/0/match/0/foo2_com pathMatch: distinct: false name: "" @@ -399,11 +469,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar2.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar2.com + name: httproute/default/httproute-1/rule/0/match/0/bar2_com pathMatch: distinct: false name: "" @@ -418,11 +494,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo3.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo3.com + name: httproute/default/httproute-1/rule/0/match/0/foo3_com pathMatch: distinct: false name: "" @@ -437,11 +519,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar3.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar3.com + name: httproute/default/httproute-1/rule/0/match/0/bar3_com pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml index 6d6d34395df..d2078deb76b 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -64,7 +64,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml index 7102df2c5c2..10becd27249 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -78,6 +78,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io @@ -96,6 +101,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -114,6 +124,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-3 supportedKinds: - group: gateway.networking.k8s.io @@ -132,6 +147,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-4 supportedKinds: - group: gateway.networking.k8s.io @@ -150,6 +170,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-5 supportedKinds: - group: gateway.networking.k8s.io @@ -168,6 +193,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-6 supportedKinds: - group: gateway.networking.k8s.io @@ -186,6 +216,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-7 supportedKinds: - group: gateway.networking.k8s.io @@ -204,6 +239,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-8 supportedKinds: - group: gateway.networking.k8s.io @@ -211,7 +251,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -276,11 +316,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + name: httproute/default/httproute-1/rule/0/match/0/foo_com pathMatch: distinct: false name: "" @@ -295,11 +341,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + name: httproute/default/httproute-1/rule/0/match/0/bar_com pathMatch: distinct: false name: "" @@ -314,11 +366,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo1.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo1.com + name: httproute/default/httproute-1/rule/0/match/0/foo1_com pathMatch: distinct: false name: "" @@ -333,11 +391,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar1.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar1.com + name: httproute/default/httproute-1/rule/0/match/0/bar1_com pathMatch: distinct: false name: "" @@ -352,11 +416,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo2.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo2.com + name: httproute/default/httproute-1/rule/0/match/0/foo2_com pathMatch: distinct: false name: "" @@ -371,11 +441,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar2.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar2.com + name: httproute/default/httproute-1/rule/0/match/0/bar2_com pathMatch: distinct: false name: "" @@ -390,11 +466,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo3.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo3.com + name: httproute/default/httproute-1/rule/0/match/0/foo3_com pathMatch: distinct: false name: "" @@ -409,11 +491,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar3.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar3.com + name: httproute/default/httproute-1/rule/0/match/0/bar3_com pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml index 26afb769e00..8674e7a9633 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -34,7 +34,7 @@ secrets: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml index ad2a2be63fb..a8361009bfe 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -40,6 +40,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -58,6 +63,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -65,7 +75,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -134,11 +144,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" @@ -153,11 +169,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml index 42319322961..8b11c8da7bf 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -22,7 +22,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml index decc633fb17..f90af488f5b 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -36,6 +36,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io @@ -54,6 +59,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -61,7 +71,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -126,11 +136,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-foo.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + name: httproute/default/httproute-1/rule/0/match/0/foo_com pathMatch: distinct: false name: "" @@ -145,11 +161,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + name: httproute/default/httproute-1/rule/0/match/0/bar_com pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway.in.yaml index 9c0f4d4e1a6..2946dfeb105 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-gateway.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-gateway.out.yaml index 74cf2100f07..51638092e37 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-gateway.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-gateway.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -100,11 +105,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.in.yaml index b40ea29ee4d..ec02f88815e 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.out.yaml index b4f94fe50cb..15c5d61deff 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-matching-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -104,11 +109,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml index 6e11d814f18..bc9aba4dd28 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -22,7 +22,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml index 0014413ced3..a5578a823eb 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -36,6 +36,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io @@ -54,6 +59,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-2 supportedKinds: - group: gateway.networking.k8s.io @@ -61,7 +71,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -134,11 +144,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-bar.com + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + name: httproute/default/httproute-1/rule/0/match/0/bar_com pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.in.yaml new file mode 100644 index 00000000000..0c00de607f1 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.in.yaml @@ -0,0 +1,99 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-2 + spec: + ips: + - 8.8.8.8 + ports: + - port: 8081 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-2 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-2 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "1.2.3.4" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml new file mode 100644 index 00000000000..e06b9481218 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml @@ -0,0 +1,136 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Mixed endpointslice address type between backendRefs is not supported + reason: ResolvedRefs + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: FQDN + endpoints: + - host: foo.bar + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 1.2.3.4 + port: 8081 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.in.yaml new file mode 100644 index 00000000000..8ad5fa12906 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.in.yaml @@ -0,0 +1,99 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-2 + spec: + ips: + - 8.8.8.8 + ports: + - port: 8081 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-2 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-2 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "bar.foo" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.out.yaml new file mode 100644 index 00000000000..3c3f35857fb --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.out.yaml @@ -0,0 +1,136 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: FQDN + endpoints: + - host: foo.bar + port: 8080 + protocol: HTTP + weight: 1 + - addressType: FQDN + endpoints: + - host: bar.foo + port: 8081 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.in.yaml new file mode 100644 index 00000000000..7fa56a4ae0d --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.in.yaml @@ -0,0 +1,66 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.out.yaml new file mode 100644 index 00000000000..e18c999c9b1 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.out.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: FQDN + endpoints: + - host: foo.bar + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.in.yaml new file mode 100644 index 00000000000..c58ed15d3f8 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.in.yaml @@ -0,0 +1,83 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "1.2.3.4" + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-2 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml new file mode 100644 index 00000000000..9e8f4dc9b43 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml @@ -0,0 +1,128 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Mixed endpointslice address type for the same backendRef is not supported + reason: ResolvedRefs + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: Mixed + endpoints: + - host: 1.2.3.4 + port: 8080 + - host: foo.bar + port: 8081 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.in.yaml new file mode 100644 index 00000000000..26a816b93c8 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.in.yaml @@ -0,0 +1,66 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "8.8.8.8" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.out.yaml new file mode 100644 index 00000000000..73ddc48011b --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.out.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener.in.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener.in.yaml index 34751d39697..6766b18f543 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener.in.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener.out.yaml index 387b6266a45..246f7843cc7 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -102,11 +107,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-backend-request-timeout.in.yaml b/internal/gatewayapi/testdata/httproute-backend-request-timeout.in.yaml new file mode 100644 index 00000000000..f16077de329 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-backend-request-timeout.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + timeouts: + backendRequest: 1s + backendRefs: + - name: service-1 + port: 8080 diff --git a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml new file mode 100755 index 00000000000..e410e752b70 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + backendRequest: 1s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + timeout: 1s diff --git a/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml b/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml index 42d894b2639..0060019963e 100644 --- a/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml +++ b/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml b/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml index 48977213c42..4cd3e58a774 100644 --- a/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http-1 supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-request-timeout.in.yaml b/internal/gatewayapi/testdata/httproute-request-timeout.in.yaml new file mode 100644 index 00000000000..6cf2210aa3f --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-request-timeout.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + timeouts: + request: 5s + backendRefs: + - name: service-1 + port: 8080 diff --git a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml new file mode 100644 index 00000000000..6500ec7b1db --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + request: 5s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + timeout: 5s diff --git a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml index df9b9983a82..2e9cddb5809 100644 --- a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml +++ b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml index 3dc83340d1a..ab2c49ecd5b 100644 --- a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml +++ b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -104,17 +109,29 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - - host: 7.7.7.7 - port: 8080 - weight: 1 - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml index 3f6730de32a..36acae06f4d 100644 --- a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml +++ b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml index bbff88ba679..f7fc2fafe4b 100644 --- a/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml +++ b/internal/gatewayapi/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -107,17 +112,29 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - - host: 7.7.7.7 - port: 8080 - weight: 2 - - host: 7.7.7.7 - port: 8080 - weight: 3 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 2 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 3 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml b/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml index b15249881b1..c1d5723e744 100644 --- a/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default @@ -39,9 +39,30 @@ services: namespace: backends name: service-1 spec: - clusterIP: 7.7.7.7 + clusterIP: 1.1.1.1 ports: - - port: 8080 + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: backends + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true referenceGrants: - apiVersion: gateway.networking.k8s.io/v1alpha2 kind: ReferenceGrant diff --git a/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml b/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml index 5315861283e..de7926e711a 100644 --- a/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -102,11 +107,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.in.yaml b/internal/gatewayapi/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.in.yaml new file mode 100644 index 00000000000..e445c56366d --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.in.yaml @@ -0,0 +1,81 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + namespace: backends + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: backends + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: backends + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "8.8.8.8" + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: backends + name: referencegrant-1 + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: default + to: + - group: multicluster.x-k8s.io + kind: ServiceImport diff --git a/internal/gatewayapi/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.out.yaml b/internal/gatewayapi/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.out.yaml new file mode 100644 index 00000000000..eb897022902 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.out.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-empty-matches.in.yaml b/internal/gatewayapi/testdata/httproute-with-empty-matches.in.yaml index 9961e34afc7..14755bccba1 100644 --- a/internal/gatewayapi/testdata/httproute-with-empty-matches.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-empty-matches.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-empty-matches.out.yaml b/internal/gatewayapi/testdata/httproute-with-empty-matches.out.yaml index 4f7ba21c5a3..3fbb6cd826d 100644 --- a/internal/gatewayapi/testdata/httproute-with-empty-matches.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-empty-matches.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -99,8 +104,14 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/-1-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/-1/* diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml index 741dd1ab74d..06888796590 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml index 8ce62218e6b..c2c6d385ee8 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -130,15 +135,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.in.yaml index 7a721b54686..d9dcdc3b289 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.out.yaml index 339a539fb90..0f9c470d2a9 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-adds.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -146,15 +151,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml index f8a8ed02266..754fc2d9730 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml index b026511d039..a3be9067335 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -116,15 +121,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.in.yaml index 432dc059e55..08fc178a22a 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.out.yaml index da3fff34719..590f02627a5 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-duplicate-removes.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -111,15 +116,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml index 7c22d7c62fe..c965d329683 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml index e35f415742e..3da7f9b3fad 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -121,15 +126,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.in.yaml index 978ef416a6d..1b47bb7f4ff 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml index f8959f3325a..a4a5974005a 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml index ecdaf8f42dd..aaf5bad87ff 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml index 42d01206c05..d9467efdb75 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.in.yaml index 9ff34332afe..3b0ede69aee 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.out.yaml index 7f0211f31b1..02140bee7b5 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-no-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -108,15 +113,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.in.yaml index be2cdcaafd2..f2bc3162ff0 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml index 6b63414fb98..5936881f5c5 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-remove.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-remove.in.yaml index 7bbbf94f8c5..09e21329020 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-remove.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-remove.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-remove.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-remove.out.yaml index da63e852ae1..020ec1f5ffc 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-filter-remove.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-filter-remove.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -112,15 +117,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml index 7fbaac57602..85d2eabc26e 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml index 7a6ddfb9813..e75b60882e6 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -103,7 +108,8 @@ xdsIR: valid: 0 directResponse: statusCode: 500 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml index fdb538528ef..66905669ee7 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml index f848fc9df70..50079248c8a 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -65,7 +70,8 @@ httpRoutes: type: Accepted - lastTransitionTime: null message: Group is invalid, only the core API group (specified by omitting - the group field or setting it to an empty string) is supported + the group field or setting it to an empty string) and multicluster.x-k8s.io + is supported reason: InvalidKind status: "False" type: ResolvedRefs @@ -106,7 +112,8 @@ xdsIR: valid: 0 directResponse: statusCode: 500 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml index a543072c269..8914c502c54 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml index 3a16290309d..6781dcc65b7 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -63,7 +68,7 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Kind is invalid, only Service is supported + message: Kind is invalid, only Service and MCS ServiceImport is supported reason: InvalidKind status: "False" type: ResolvedRefs @@ -104,7 +109,8 @@ xdsIR: valid: 0 directResponse: statusCode: 500 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml index 6a4116f6c45..90664bbd470 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml index 11aa9a94ba3..020a7bf3f96 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -103,7 +108,8 @@ xdsIR: valid: 0 directResponse: statusCode: 500 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.in.yaml new file mode 100644 index 00000000000..35593cde200 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-that-doesnt-exist + port: 8080 diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml new file mode 100644 index 00000000000..669c5afcdab --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml @@ -0,0 +1,118 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-that-doesnt-exist + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: ServiceImport default/service-import-that-doesnt-exist not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml index 509869600a7..d56d23d0c94 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml index c5a3d9b61e0..9e4963f1127 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -103,7 +108,8 @@ xdsIR: valid: 0 directResponse: statusCode: 500 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml index b0f788c5207..833ca4ad07b 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml index 0871775896c..9543bab4beb 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -63,7 +68,7 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Backend ref to service backends/service-1 not permitted by any ReferenceGrant. + message: Backend ref to Service backends/service-1 not permitted by any ReferenceGrant. reason: RefNotPermitted status: "False" type: ResolvedRefs @@ -104,7 +109,8 @@ xdsIR: valid: 0 directResponse: statusCode: 500 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-ratelimitfilter.in.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-ratelimitfilter.in.yaml deleted file mode 100644 index 4883307c8e7..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-invalid-ratelimitfilter.in.yaml +++ /dev/null @@ -1,60 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - value: "/" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: test -rateLimitFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: RateLimitFilter - metadata: - name: test - namespace: default - spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - type: Distinct - name: x-user-id - value: one - limit: - requests: 10 - unit: Hour diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-ratelimitfilter.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-ratelimitfilter.out.yaml deleted file mode 100644 index aca06ffd49b..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-invalid-ratelimitfilter.out.yaml +++ /dev/null @@ -1,111 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: test - type: ExtensionRef - matches: - - path: - value: / - status: - parents: - - conditions: - - lastTransitionTime: null - message: 'Unable to translate RateLimitFilter. Either the header.Type is not - valid or the header is missing a value: default/test' - reason: UnsupportedValue - status: "False" - type: Accepted - - lastTransitionTime: null - message: 'Unable to translate RateLimitFilter. Either the header.Type is not - valid or the header is missing a value: default/test' - reason: BackendNotFound - status: "False" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.in.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.in.yaml index 9bd0ed504de..f366ee752cb 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.out.yaml index 51e2b1f0ec7..2fc54e2eb1b 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-duplicates.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -118,19 +123,34 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io mirrors: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + - name: httproute/default/httproute-1/rule/0-mirror-0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - name: httproute/default/httproute-1/rule/0-mirror-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.in.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.in.yaml index 77bee0e1ee0..16cc895b808 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default @@ -35,6 +35,18 @@ httpRoutes: - name: service-1 port: 8080 filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: X-Header-Set + value: set-overwrites-values + add: + - name: X-Header-Add + value: header-val-1 + - name: X-Header-Add-Append + value: header-val-2 + remove: + - X-Header-Remove - type: RequestMirror requestMirror: backendRef: diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.out.yaml index 2f91c02e790..1b8c98e5438 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-multiple.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -54,6 +59,18 @@ httpRoutes: - name: service-1 port: 8080 filters: + - requestHeaderModifier: + add: + - name: X-Header-Add + value: header-val-1 + - name: X-Header-Add-Append + value: header-val-2 + remove: + - X-Header-Remove + set: + - name: X-Header-Set + value: set-overwrites-values + type: RequestHeaderModifier - requestMirror: backendRef: kind: Service @@ -115,26 +132,50 @@ xdsIR: name: envoy-gateway/gateway-1/http port: 10080 routes: - - backendWeights: + - addRequestHeaders: + - append: true + name: X-Header-Add + value: header-val-1 + - append: true + name: X-Header-Add-Append + value: header-val-2 + - append: false + name: X-Header-Set + value: set-overwrites-values + backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io mirrors: - - host: 7.7.7.7 - port: 8080 - weight: 1 - - host: 7.6.5.4 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + - name: httproute/default/httproute-1/rule/0-mirror-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - name: httproute/default/httproute-1/rule/0-mirror-2 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + protocol: HTTP + weight: 1 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" prefix: / + removeRequestHeaders: + - X-Header-Remove diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.in.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.in.yaml index 3873206e923..d14154f4a67 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml index 2175c52420b..2088aa42ac2 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -112,15 +117,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.in.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.in.yaml index b3e9b7f624e..d2d41de93b1 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml index b7394358b40..b11b9294278 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -112,15 +117,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter.in.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter.in.yaml index c624835c1e7..f428cd3665e 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter.out.yaml index 6a76c871887..347945a0f69 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -112,19 +117,26 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io mirrors: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + - name: httproute/default/httproute-1/rule/0-mirror-0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-non-existent-authenfilter-ref.in.yaml b/internal/gatewayapi/testdata/httproute-with-non-existent-authenfilter-ref.in.yaml deleted file mode 100644 index da672ca499e..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-non-existent-authenfilter-ref.in.yaml +++ /dev/null @@ -1,43 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - value: "/" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: non-exist - diff --git a/internal/gatewayapi/testdata/httproute-with-non-existent-authenfilter-ref.out.yaml b/internal/gatewayapi/testdata/httproute-with-non-existent-authenfilter-ref.out.yaml deleted file mode 100644 index c420d7a3ef1..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-non-existent-authenfilter-ref.out.yaml +++ /dev/null @@ -1,109 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: non-exist - type: ExtensionRef - matches: - - path: - value: / - status: - parents: - - conditions: - - lastTransitionTime: null - message: 'Reference default/non-exist not found for filter type: AuthenticationFilter' - reason: UnsupportedValue - status: "False" - type: Accepted - - lastTransitionTime: null - message: 'Reference default/non-exist not found for filter type: AuthenticationFilter' - reason: BackendNotFound - status: "False" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 diff --git a/internal/gatewayapi/testdata/httproute-with-non-matching-authenfilter-ref.in.yaml b/internal/gatewayapi/testdata/httproute-with-non-matching-authenfilter-ref.in.yaml deleted file mode 100644 index cdf5030c62f..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-non-matching-authenfilter-ref.in.yaml +++ /dev/null @@ -1,57 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - value: "/" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test -authenticationFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: AuthenticationFilter - metadata: - name: example1 - namespace: default - spec: - type: JWT - jwtProviders: - - name: example1 - issuer: https://www.example1.com - audiences: - - foo.com - remoteJwks: - uri: https://foo.com/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-non-matching-authenfilter-ref.out.yaml b/internal/gatewayapi/testdata/httproute-with-non-matching-authenfilter-ref.out.yaml deleted file mode 100644 index fe5b1467ced..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-non-matching-authenfilter-ref.out.yaml +++ /dev/null @@ -1,109 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test - type: ExtensionRef - matches: - - path: - value: / - status: - parents: - - conditions: - - lastTransitionTime: null - message: 'Reference default/test not found for filter type: AuthenticationFilter' - reason: UnsupportedValue - status: "False" - type: Accepted - - lastTransitionTime: null - message: 'Reference default/test not found for filter type: AuthenticationFilter' - reason: BackendNotFound - status: "False" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 diff --git a/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml index 99f6ba02fdb..51dd2e3d2c2 100644 --- a/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml index 48a6b7d2ac7..922220aa0a0 100644 --- a/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml index 491cf3d4cfd..d36b84da5fb 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml index 182c75a8307..3b0c94febaa 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -113,11 +118,8 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" @@ -127,6 +129,6 @@ xdsIR: path: fullReplace: /redirected prefixMatchReplace: null - port: null + port: 443 scheme: https statusCode: 301 diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.in.yaml index bebd2d3e688..7eac1daafb2 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml index be344d64268..effccbc5fa3 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -111,11 +116,8 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" @@ -123,6 +125,6 @@ xdsIR: redirect: hostname: redirected.com path: null - port: null + port: 443 scheme: https statusCode: 301 diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml index e40076f5b56..41653f4d9b9 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml index f40b5b4879b..c74ed5a3f53 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml index fcc91ae6054..ef891674a55 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml index ca0ab708b80..4a5305be5d9 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.in.yaml index 45d029b7ea4..02986648009 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml index ce8da17a08a..af22cb4bf50 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml index a683b7d7634..34c0b50fe8c 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml index 33dae3591db..e87602dc49c 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -114,11 +119,8 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.in.yaml index cea778dd95b..40cf83e0d7f 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.out.yaml index 5c9e64aea25..f1932c0b172 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-adds.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -142,15 +147,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml index 12197e4d164..ac534639c68 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml index 2ab46fce181..3c233ff3605 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -130,15 +135,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml index 42e37c41aaa..0cebadee072 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml index 10ca6e97637..e8eb1694853 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -146,15 +151,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml index d3374ed743a..7624edfcc8e 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml index f12fd5227b3..8f0d821cb69 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -116,15 +121,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml index 6d234bcf5f2..4cfd6396767 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml index ab5fb68d059..218314dba5e 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -111,15 +116,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml index d6e1c760913..1c0016800d3 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml index e81020cf1d1..8a8178a3710 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -121,15 +126,17 @@ xdsIR: backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.in.yaml index 2eb722baea5..cca451186c8 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.out.yaml index 9743c18b702..7d6662a0708 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml index 4c1a29d288e..6e1e57425bf 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml index 659ac2f68aa..e319b1caee8 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.in.yaml index 2841ea22237..0f1e790b9d6 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.out.yaml index 17e5963495b..43e425d9a1d 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -108,15 +113,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml index 1851a22c97d..8120906a4b7 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml index 70eb6d8119e..4c77af97993 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.in.yaml index 1e83e0d15a2..413ec03c8b1 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.out.yaml index 2a35ec4efda..247a70498f4 100644 --- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-remove.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -112,15 +117,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml index ce166fe9411..af7ae362824 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml index 7ac8233ab51..10a36ffe59d 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -101,11 +106,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.in.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.in.yaml index 3d372e82c8d..ee032081642 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.out.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.out.yaml index cc52748f376..79cc9551888 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-http-method-match.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -99,12 +104,18 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 headerMatches: - distinct: false exact: POST name: :method - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml index 9fd68520b16..2ec00c37a8f 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml index efc61eeda05..8ef1a2b9d9c 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -131,31 +136,43 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 headerMatches: - distinct: false + exact: exact name: Header-1 - safeRegex: '*regex*' - name: default/httproute-1/rule/2/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false + exact: /exact name: "" - safeRegex: '*regex*' queryParamMatches: - distinct: false + exact: exact name: QueryParam-1 - safeRegex: '*regex*' - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/1/match/0-* + destination: + name: httproute/default/httproute-1/rule/1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/1/match/0/* pathMatch: distinct: false name: "" @@ -163,20 +180,26 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: httproute/default/httproute-1/rule/2 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 headerMatches: - distinct: false - exact: exact name: Header-1 - name: default/httproute-1/rule/0/match/0-* + safeRegex: '*regex*' + hostname: '*' + name: httproute/default/httproute-1/rule/2/match/0/* pathMatch: distinct: false - exact: /exact name: "" + safeRegex: '*regex*' queryParamMatches: - distinct: false - exact: exact name: QueryParam-1 + safeRegex: '*regex*' diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml index 3c8a4d791a3..7709a6945d0 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml index 23d6b34aa75..2b02c931900 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -105,10 +110,15 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 headerMatches: - distinct: false exact: Val-1 @@ -116,7 +126,8 @@ xdsIR: - distinct: false exact: Val-2 name: Header-2 - name: default/httproute-1/rule/0/match/0-* + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml b/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml index b11330744b1..dbd94a92b56 100644 --- a/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml b/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml index 02cb80660d7..ffdd8c56724 100644 --- a/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -105,11 +110,17 @@ xdsIR: - backendWeights: invalid: 2 valid: 1 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: default/httproute-1/rule/0/match/0-* + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* pathMatch: distinct: false exact: /exact diff --git a/internal/gatewayapi/testdata/httproute-with-sourceip-ratelimit.in.yaml b/internal/gatewayapi/testdata/httproute-with-sourceip-ratelimit.in.yaml deleted file mode 100644 index 7046b141e08..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-sourceip-ratelimit.in.yaml +++ /dev/null @@ -1,57 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - value: "/" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: test -rateLimitFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: RateLimitFilter - metadata: - name: test - namespace: default - spec: - type: Global - global: - rules: - - clientSelectors: - - sourceIP: 192.168.0.0/16 - limit: - requests: 10 - unit: Hour diff --git a/internal/gatewayapi/testdata/httproute-with-sourceip-ratelimit.out.yaml b/internal/gatewayapi/testdata/httproute-with-sourceip-ratelimit.out.yaml deleted file mode 100644 index e9f515f3391..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-sourceip-ratelimit.out.yaml +++ /dev/null @@ -1,138 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: test - type: ExtensionRef - matches: - - path: - value: / - status: - parents: - - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 - routes: - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - name: "" - prefix: / - rateLimit: - global: - rules: - - cidrMatch: - cidr: 192.168.0.0/16 - distinct: false - ipv6: false - maskLen: 16 - headerMatches: [] - limit: - requests: 10 - unit: Hour diff --git a/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml index 7396ea600d8..87e9eedbb95 100644 --- a/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml index 6912916b73f..3f137028b62 100644 --- a/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -103,15 +108,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml index 34c4cf7c439..fb2e342f211 100644 --- a/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml index f7aeb4c2928..371c57b11a2 100644 --- a/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -104,15 +109,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" @@ -120,15 +127,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: whales.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-whales.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: whales.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/whales_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml index a9d0eed0a4d..d31a159837f 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml index f08414c2dcb..94eb7bf953f 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -111,15 +116,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml index 81b7145ebc4..38e9a2f2ffc 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml index 121a34bf503..5be780f9667 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -112,15 +117,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml index 20c01dad056..d9b220fd34c 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml index 2df22c78724..49b64d6a68b 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -109,15 +114,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml index 17a0293da6b..dbfc732c089 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml index 288a89d94c6..136f0c1bf2b 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -109,15 +114,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml index 9aef23666fd..bcd778f9c29 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml index c5aa3d6938b..007a3ef1e54 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml index 4a80b8390a4..1b8366455e2 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml index 607d3377fc1..d774cf7d858 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml index 8feeaffc603..11b7ff298c7 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml index 9b343f3a519..c42b3ccaffb 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml index fb33211a086..555a6dbf5da 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml index 01f58b98374..d2f50fb22d0 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml index 99c0c5f6b80..a89a9e7722e 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml index 06180b84afa..1afb98e60a7 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -17,7 +17,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml index a11190c3195..c4597755cc3 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -15,7 +15,7 @@ gateways: namespaces: from: All httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml index 3321abd3cdc..e57189f02f3 100644 --- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -29,6 +29,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -36,7 +41,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -111,15 +116,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproute-with-valid-authenfilter.in.yaml b/internal/gatewayapi/testdata/httproute-with-valid-authenfilter.in.yaml deleted file mode 100644 index 5c7e5acfa0b..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-authenfilter.in.yaml +++ /dev/null @@ -1,55 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - value: "/" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test -authenticationFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: AuthenticationFilter - metadata: - namespace: default - name: test - spec: - type: JWT - jwtProviders: - - name: test - issuer: https://www.test.local - remoteJWKS: - uri: https://test.local/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-valid-authenfilter.out.yaml b/internal/gatewayapi/testdata/httproute-with-valid-authenfilter.out.yaml deleted file mode 100644 index a4894147510..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-authenfilter.out.yaml +++ /dev/null @@ -1,133 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test - type: ExtensionRef - matches: - - path: - value: / - status: - parents: - - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 - routes: - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - name: "" - prefix: / - requestAuthentication: - jwt: - providers: - - issuer: https://www.test.local - name: test - remoteJWKS: - uri: https://test.local/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-authenfilter.in.yaml b/internal/gatewayapi/testdata/httproute-with-valid-multi-match-authenfilter.in.yaml deleted file mode 100644 index 23c61e764c4..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-authenfilter.in.yaml +++ /dev/null @@ -1,69 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - type: Exact - value: "/test/path/1" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test - - matches: - - path: - type: Exact - value: "/test/path/2" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test -authenticationFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: AuthenticationFilter - metadata: - namespace: default - name: test - spec: - type: JWT - jwtProviders: - - name: test - issuer: https://www.test.local - remoteJWKS: - uri: https://test.local/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-authenfilter.out.yaml b/internal/gatewayapi/testdata/httproute-with-valid-multi-match-authenfilter.out.yaml deleted file mode 100644 index c795dd089d7..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-authenfilter.out.yaml +++ /dev/null @@ -1,170 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test - type: ExtensionRef - matches: - - path: - type: Exact - value: /test/path/1 - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test - type: ExtensionRef - matches: - - path: - type: Exact - value: /test/path/2 - status: - parents: - - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 - routes: - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - exact: /test/path/1 - name: "" - requestAuthentication: - jwt: - providers: - - issuer: https://www.test.local - name: test - remoteJWKS: - uri: https://test.local/jwt/public-key/jwks.json - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/1/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - exact: /test/path/2 - name: "" - requestAuthentication: - jwt: - providers: - - issuer: https://www.test.local - name: test - remoteJWKS: - uri: https://test.local/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-multi-authenfilter.in.yaml b/internal/gatewayapi/testdata/httproute-with-valid-multi-match-multi-authenfilter.in.yaml deleted file mode 100644 index eef5dbfa36e..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-multi-authenfilter.in.yaml +++ /dev/null @@ -1,85 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - type: Exact - value: "/test/path/1" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test1 - - matches: - - path: - type: Exact - value: "/test/path/2" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test2 -authenticationFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: AuthenticationFilter - metadata: - namespace: default - name: test1 - spec: - type: JWT - jwtProviders: - - name: test1 - issuer: https://www.test1.local - remoteJWKS: - uri: https://test1.local/jwt/public-key/jwks.json -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: AuthenticationFilter - metadata: - namespace: default - name: test2 - spec: - type: JWT - jwtProviders: - - name: test2 - issuer: https://www.test2.local - remoteJWKS: - uri: https://test2.local/jwt/public-key/jwks.json - - name: test3 - issuer: https://www.test3.local - remoteJWKS: - uri: https://test3.local/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-multi-authenfilter.out.yaml b/internal/gatewayapi/testdata/httproute-with-valid-multi-match-multi-authenfilter.out.yaml deleted file mode 100644 index c45699a2332..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-multi-match-multi-authenfilter.out.yaml +++ /dev/null @@ -1,174 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test1 - type: ExtensionRef - matches: - - path: - type: Exact - value: /test/path/1 - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: test2 - type: ExtensionRef - matches: - - path: - type: Exact - value: /test/path/2 - status: - parents: - - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 - routes: - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - exact: /test/path/1 - name: "" - requestAuthentication: - jwt: - providers: - - issuer: https://www.test1.local - name: test1 - remoteJWKS: - uri: https://test1.local/jwt/public-key/jwks.json - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/1/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - exact: /test/path/2 - name: "" - requestAuthentication: - jwt: - providers: - - issuer: https://www.test2.local - name: test2 - remoteJWKS: - uri: https://test2.local/jwt/public-key/jwks.json - - issuer: https://www.test3.local - name: test3 - remoteJWKS: - uri: https://test3.local/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/httproute-with-valid-ratelimitfilter.in.yaml b/internal/gatewayapi/testdata/httproute-with-valid-ratelimitfilter.in.yaml deleted file mode 100644 index 5640fd55a44..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-ratelimitfilter.in.yaml +++ /dev/null @@ -1,61 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - namespace: envoy-gateway - name: gateway-1 - spec: - gatewayClassName: envoy-gateway-class - listeners: - - name: http - protocol: HTTP - port: 80 - hostname: "*.envoyproxy.io" - allowedRoutes: - namespaces: - from: All -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - namespace: default - name: httproute-1 - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - namespace: envoy-gateway - name: gateway-1 - sectionName: http - rules: - - matches: - - path: - value: "/" - backendRefs: - - name: service-1 - port: 8080 - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: test -rateLimitFilters: -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: RateLimitFilter - metadata: - name: test - namespace: default - spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - name: x-user-id - value: one - - name: x-org-id - type: Distinct - limit: - requests: 10 - unit: Hour diff --git a/internal/gatewayapi/testdata/httproute-with-valid-ratelimitfilter.out.yaml b/internal/gatewayapi/testdata/httproute-with-valid-ratelimitfilter.out.yaml deleted file mode 100644 index 278c67aec8e..00000000000 --- a/internal/gatewayapi/testdata/httproute-with-valid-ratelimitfilter.out.yaml +++ /dev/null @@ -1,138 +0,0 @@ -gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: Gateway - metadata: - creationTimestamp: null - name: gateway-1 - namespace: envoy-gateway - spec: - gatewayClassName: envoy-gateway-class - listeners: - - allowedRoutes: - namespaces: - from: All - hostname: '*.envoyproxy.io' - name: http - port: 80 - protocol: HTTP - status: - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: null - message: Sending translated listener configuration to the data plane - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: null - message: Listener has been successfully translated - reason: Accepted - status: "True" - type: Accepted - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute -httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 - kind: HTTPRoute - metadata: - creationTimestamp: null - name: httproute-1 - namespace: default - spec: - hostnames: - - gateway.envoyproxy.io - parentRefs: - - name: gateway-1 - namespace: envoy-gateway - sectionName: http - rules: - - backendRefs: - - name: service-1 - port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: test - type: ExtensionRef - matches: - - path: - value: / - status: - parents: - - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - controllerName: gateway.envoyproxy.io/gatewayclass-controller - parentRef: - name: gateway-1 - namespace: envoy-gateway - sectionName: http -infraIR: - envoy-gateway/gateway-1: - proxy: - listeners: - - address: "" - ports: - - containerPort: 10080 - name: http - protocol: HTTP - servicePort: 80 - metadata: - labels: - gateway.envoyproxy.io/owning-gateway-name: gateway-1 - gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway - name: envoy-gateway/gateway-1 -xdsIR: - envoy-gateway/gateway-1: - accessLog: - text: - - path: /dev/stdout - http: - - address: 0.0.0.0 - hostnames: - - '*.envoyproxy.io' - isHTTP2: false - name: envoy-gateway/gateway-1/http - port: 10080 - routes: - - backendWeights: - invalid: 0 - valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: gateway.envoyproxy.io - name: :authority - name: default/httproute-1/rule/0/match/0-gateway.envoyproxy.io - pathMatch: - distinct: false - name: "" - prefix: / - rateLimit: - global: - rules: - - headerMatches: - - distinct: false - exact: one - name: x-user-id - - distinct: true - name: x-org-id - limit: - requests: 10 - unit: Hour diff --git a/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml b/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml index 91a92c15c6f..d61cae0499e 100644 --- a/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: All httpRoutes: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: default diff --git a/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml index 28cbca568f5..cfc4c8c7b68 100644 --- a/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -102,15 +107,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - name: :authority - suffix: envoyproxy.io - name: default/httproute-1/rule/0/match/0-*.envoyproxy.io + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*.envoyproxy.io' + name: httproute/default/httproute-1/rule/0/match/0/*_envoyproxy_io pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/httproutes-with-multiple-matches.in.yaml b/internal/gatewayapi/testdata/httproutes-with-multiple-matches.in.yaml index 8edfe68340b..7315f3f4faa 100644 --- a/internal/gatewayapi/testdata/httproutes-with-multiple-matches.in.yaml +++ b/internal/gatewayapi/testdata/httproutes-with-multiple-matches.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -14,7 +14,7 @@ gateways: namespaces: from: Same httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: envoy-gateway @@ -30,7 +30,26 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - "*.com" + - "*.net" + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: envoy-gateway @@ -51,7 +70,7 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: envoy-gateway @@ -69,7 +88,7 @@ httpRoutes: backendRefs: - name: service-2 port: 8080 -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: envoy-gateway @@ -90,7 +109,7 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: envoy-gateway @@ -115,15 +134,56 @@ services: namespace: envoy-gateway name: service-1 spec: - clusterIP: 7.7.7.7 + clusterIP: 1.1.1.1 ports: - - port: 8080 + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 - apiVersion: v1 kind: Service metadata: namespace: envoy-gateway name: service-2 spec: - clusterIP: 8.8.8.8 + clusterIP: 2.2.2.2 ports: - - port: 8080 + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: envoy-gateway + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-2 + namespace: envoy-gateway + labels: + kubernetes.io/service-name: service-2 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "8.8.8.8" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproutes-with-multiple-matches.out.yaml b/internal/gatewayapi/testdata/httproutes-with-multiple-matches.out.yaml index 130eae2c876..1eed612d234 100644 --- a/internal/gatewayapi/testdata/httproutes-with-multiple-matches.out.yaml +++ b/internal/gatewayapi/testdata/httproutes-with-multiple-matches.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,7 +16,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 5 + - attachedRoutes: 6 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -28,6 +28,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: http supportedKinds: - group: gateway.networking.k8s.io @@ -35,7 +40,7 @@ gateways: - group: gateway.networking.k8s.io kind: GRPCRoute httpRoutes: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -69,7 +74,44 @@ httpRoutes: parentRef: name: gateway-1 namespace: envoy-gateway -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + hostnames: + - '*.com' + - '*.net' + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -108,7 +150,7 @@ httpRoutes: parentRef: name: gateway-1 namespace: envoy-gateway -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -144,7 +186,7 @@ httpRoutes: parentRef: name: gateway-1 namespace: envoy-gateway -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -183,7 +225,7 @@ httpRoutes: parentRef: name: gateway-1 namespace: envoy-gateway -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: creationTimestamp: null @@ -250,15 +292,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: example.com - name: :authority - name: envoy-gateway/httproute-2/rule/0/match/0-example.com + destination: + name: httproute/envoy-gateway/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.com + name: httproute/envoy-gateway/httproute-2/rule/0/match/0/example_com pathMatch: distinct: false name: "" @@ -270,15 +314,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 8.8.8.8 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: example.com - name: :authority - name: envoy-gateway/httproute-3/rule/0/match/0-example.com + destination: + name: httproute/envoy-gateway/httproute-3/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.com + name: httproute/envoy-gateway/httproute-3/rule/0/match/0/example_com pathMatch: distinct: false name: "" @@ -286,18 +332,21 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: httproute/envoy-gateway/httproute-4/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 headerMatches: - - distinct: false - exact: example.net - name: :authority - distinct: false exact: one name: version - name: envoy-gateway/httproute-4/rule/0/match/0-example.net + hostname: example.net + name: httproute/envoy-gateway/httproute-4/rule/0/match/0/example_net pathMatch: distinct: false name: "" @@ -305,15 +354,17 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 8.8.8.8 - port: 8080 - weight: 1 - headerMatches: - - distinct: false - exact: example.net - name: :authority - name: envoy-gateway/httproute-5/rule/0/match/0-example.net + destination: + name: httproute/envoy-gateway/httproute-5/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.net + name: httproute/envoy-gateway/httproute-5/rule/0/match/0/example_net pathMatch: distinct: false name: "" @@ -321,11 +372,53 @@ xdsIR: - backendWeights: invalid: 0 valid: 0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 - name: envoy-gateway/httproute-1/rule/0/match/0-* + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*.com' + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/*_com + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*.net' + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/*_net + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/merge-invalid-multiple-gateways.in.yaml b/internal/gatewayapi/testdata/merge-invalid-multiple-gateways.in.yaml new file mode 100644 index 00000000000..a109f785281 --- /dev/null +++ b/internal/gatewayapi/testdata/merge-invalid-multiple-gateways.in.yaml @@ -0,0 +1,34 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + mergeGateways: true +gateways: + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + - name: udp + port: 80 + protocol: UDP diff --git a/internal/gatewayapi/testdata/merge-invalid-multiple-gateways.out.yaml b/internal/gatewayapi/testdata/merge-invalid-multiple-gateways.out.yaml new file mode 100755 index 00000000000..46f9e3d125b --- /dev/null +++ b/internal/gatewayapi/testdata/merge-invalid-multiple-gateways.out.yaml @@ -0,0 +1,140 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + - name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Port, protocol and hostname tuple must be unique for every listener + reason: HostnameConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: "" + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - containerPort: 10080 + name: envoy-gateway/gateway-2/udp + protocol: UDP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/merge-valid-multiple-gateways-multiple-routes.in.yaml b/internal/gatewayapi/testdata/merge-valid-multiple-gateways-multiple-routes.in.yaml new file mode 100644 index 00000000000..b88c25a8808 --- /dev/null +++ b/internal/gatewayapi/testdata/merge-valid-multiple-gateways-multiple-routes.in.yaml @@ -0,0 +1,81 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + mergeGateways: true +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-2 + port: 8888 + protocol: HTTP + - name: http-3 + hostname: example.com + port: 8888 + protocol: HTTP + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - example.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http-3 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-2 + port: 8080 diff --git a/internal/gatewayapi/testdata/merge-valid-multiple-gateways-multiple-routes.out.yaml b/internal/gatewayapi/testdata/merge-valid-multiple-gateways-multiple-routes.out.yaml new file mode 100755 index 00000000000..01649c779a4 --- /dev/null +++ b/internal/gatewayapi/testdata/merge-valid-multiple-gateways-multiple-routes.out.yaml @@ -0,0 +1,277 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-2 + port: 8888 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: example.com + name: http-3 + port: 8888 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - example.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http-3 + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http-3 +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: "" + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - containerPort: 8888 + name: envoy-gateway/gateway-2/http-2 + protocol: HTTP + servicePort: 8888 + metadata: + labels: + gateway.envoyproxy.io/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http-2 + port: 8888 + - address: 0.0.0.0 + hostnames: + - example.com + isHTTP2: false + name: envoy-gateway/gateway-2/http-3 + port: 8888 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.com + name: httproute/default/httproute-2/rule/0/match/0/example_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/merge-valid-multiple-gateways.in.yaml b/internal/gatewayapi/testdata/merge-valid-multiple-gateways.in.yaml new file mode 100644 index 00000000000..aad24f222ea --- /dev/null +++ b/internal/gatewayapi/testdata/merge-valid-multiple-gateways.in.yaml @@ -0,0 +1,41 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + mergeGateways: true +gateways: + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-2 + port: 8888 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: http-3 + hostname: example.com + port: 8888 + protocol: HTTP diff --git a/internal/gatewayapi/testdata/merge-valid-multiple-gateways.out.yaml b/internal/gatewayapi/testdata/merge-valid-multiple-gateways.out.yaml new file mode 100755 index 00000000000..8a1c6976844 --- /dev/null +++ b/internal/gatewayapi/testdata/merge-valid-multiple-gateways.out.yaml @@ -0,0 +1,161 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-2 + port: 8888 + protocol: HTTP + - hostname: example.com + name: http-3 + port: 8888 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: "" + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - containerPort: 8888 + name: envoy-gateway/gateway-2/http-2 + protocol: HTTP + servicePort: 8888 + metadata: + labels: + gateway.envoyproxy.io/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http-2 + port: 8888 + - address: 0.0.0.0 + hostnames: + - example.com + isHTTP2: false + name: envoy-gateway/gateway-2/http-3 + port: 8888 diff --git a/internal/gatewayapi/testdata/securitypolicy-status-conditions.in.yaml b/internal/gatewayapi/testdata/securitypolicy-status-conditions.in.yaml new file mode 100644 index 00000000000..bdc74d838b1 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-status-conditions.in.yaml @@ -0,0 +1,133 @@ +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-as-well + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: also-target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-grpcroute-in-gateway-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: envoy-gateway + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + rules: + - matches: + - headers: + - type: Exact + name: magic + value: foo + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + - name: tcp + protocol: TCP + port: 53 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml new file mode 100755 index 00000000000..4a2610a0084 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml @@ -0,0 +1,382 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + - allowedRoutes: + namespaces: + from: Same + name: tcp + port: 53 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must have TLS set when protocol is HTTPS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: magic + type: Exact + value: foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - containerPort: 10053 + name: tcp + protocol: TCP + servicePort: 53 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: also-target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: Unable to target HTTPRoute, another SecurityPolicy has already attached + to it + reason: Conflicted + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-grpcroute-in-gateway-2 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-as-well + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: Unable to target Gateway, another SecurityPolicy has already attached + to it + reason: Conflicted + status: "False" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + headerMatches: + - distinct: false + exact: foo + name: magic + hostname: '*' + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/* diff --git a/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.in.yaml new file mode 100644 index 00000000000..619425c9bdb --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.in.yaml @@ -0,0 +1,58 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: users-secret + data: + .htpasswd: "dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + basicAuth: + users: + name: "users-secret" diff --git a/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml new file mode 100755 index 00000000000..4865ac2d186 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml @@ -0,0 +1,153 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + basicAuth: + users: + group: null + kind: null + name: users-secret + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + basicAuth: + users: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo= + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo diff --git a/internal/gatewayapi/testdata/securitypolicy-with-cors.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-cors.in.yaml new file mode 100644 index 00000000000..3cc8004e602 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-cors.in.yaml @@ -0,0 +1,119 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + cors: + allowOrigins: + - type: RegularExpression + value: "FooBar[0-9]+" + - type: Exact + value: foo.bar.com + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" + maxAge: 1000s +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + cors: + allowOrigins: + - type: Prefix + value: example + - type: Suffix + value: bar.org + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-5" + - "x-header-6" + exposeHeaders: + - "x-header-7" + - "x-header-8" + maxAge: 2000s diff --git a/internal/gatewayapi/testdata/securitypolicy-with-cors.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-cors.out.yaml new file mode 100755 index 00000000000..36b1715670f --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-cors.out.yaml @@ -0,0 +1,347 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - type: Prefix + value: example + - type: Suffix + value: bar.org + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - type: RegularExpression + value: FooBar[0-9]+ + - type: Exact + value: foo.bar.com + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + safeRegex: FooBar[0-9]+ + - distinct: false + exact: foo.bar.com + name: "" + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + prefix: example + - distinct: false + name: "" + suffix: bar.org + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt-and-invalid-oidc.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt-and-invalid-oidc.in.yaml new file mode 100644 index 00000000000..833c2b70c13 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt-and-invalid-oidc.in.yaml @@ -0,0 +1,111 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/bar" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway # This policy should attach httproute-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 + oidc: + provider: + issuer: "https://accounts.google.com" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route # This policy should attach httproute-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + jwt: + providers: + - name: example2 + issuer: https://two.example.com + audiences: + - two.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim2 + oidc: + provider: + issuer: "https://accounts.google.com" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client2-secret" diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt-and-invalid-oidc.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt-and-invalid-oidc.out.yaml new file mode 100755 index 00000000000..04b6d65085d --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt-and-invalid-oidc.out.yaml @@ -0,0 +1,281 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + jwt: + providers: + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: one-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client2-secret + provider: + issuer: https://accounts.google.com + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: Secret default/client2-secret does not exist. + reason: Invalid + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + issuer: https://accounts.google.com + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: Secret envoy-gateway/client1-secret does not exist. + reason: Invalid + status: "False" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + jwt: + providers: + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: one-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /bar diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt-with-custom-extractor.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt-with-custom-extractor.in.yaml new file mode 100644 index 00000000000..96580c84971 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt-with-custom-extractor.in.yaml @@ -0,0 +1,121 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 + - name: example2 + issuer: https://two.example.com + audiences: + - two.foo.com + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: two-route-example-key + claim: claim2 +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + jwt: + providers: + - name: example3 + issuer: https://three.example.com + audiences: + - three.foo.com + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: three-route-example-key + claim: claim3 + extractFrom: + cookies: + - session_access_token diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt-with-custom-extractor.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt-with-custom-extractor.out.yaml new file mode 100755 index 00000000000..ca5d2dc9d3f --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt-with-custom-extractor.out.yaml @@ -0,0 +1,347 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + extractFrom: + cookies: + - session_access_token + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + extractFrom: + cookies: + - session_access_token + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt.in.yaml new file mode 100644 index 00000000000..b7eec111123 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt.in.yaml @@ -0,0 +1,118 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 + - name: example2 + issuer: https://two.example.com + audiences: + - two.foo.com + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: two-route-example-key + claim: claim2 +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + jwt: + providers: + - name: example3 + issuer: https://three.example.com + audiences: + - three.foo.com + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: three-route-example-key + claim: claim3 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt.out.yaml new file mode 100755 index 00000000000..9d28cafb19a --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt.out.yaml @@ -0,0 +1,341 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-issuer.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-issuer.in.yaml new file mode 100644 index 00000000000..9f49012c528 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-issuer.in.yaml @@ -0,0 +1,40 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client1-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-non-exist-secretRef + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + oidc: + provider: + issuer: "https://httpbin.org/" + clientID: "client1.apps.foo.bar.com" + clientSecret: + name: "client1-secret" diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-issuer.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-issuer.out.yaml new file mode 100755 index 00000000000..8e21d7060ec --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-issuer.out.yaml @@ -0,0 +1,96 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + default/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-non-exist-secretRef + namespace: default + spec: + oidc: + clientID: client1.apps.foo.bar.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + issuer: https://httpbin.org/ + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + conditions: + - lastTransitionTime: null + message: 'Error fetching endpoints from issuer: invalid character ''<'' looking + for beginning of value.' + reason: Invalid + status: "False" + type: Accepted +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-secretref.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-secretref.in.yaml new file mode 100644 index 00000000000..c3c86142e0b --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-secretref.in.yaml @@ -0,0 +1,116 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: client2-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client3-secret + data: + invalid_client_secret_key: Y2xpZW50MTpzZWNyZXQK + +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-3 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-non-exist-secretRef + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + oidc: + provider: + issuer: "https://accounts.google.com" + authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth" + tokenEndpoint: "https://oauth2.googleapis.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-no-referenceGrant + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + oidc: + provider: + issuer: "https://accounts.google.com" + authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth" + tokenEndpoint: "https://oauth2.googleapis.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + namespace: envoy-gateway + name: "client2-secret" +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-no-client-secret-key + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + oidc: + provider: + issuer: "https://accounts.google.com" + authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth" + tokenEndpoint: "https://oauth2.googleapis.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + namespace: default + name: "client3-secret" diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-secretref.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-secretref.out.yaml new file mode 100755 index 00000000000..5a30d9f3c6b --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-invalid-secretref.out.yaml @@ -0,0 +1,285 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-3 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + default/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-1 + default/gateway-2: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-2 + default/gateway-3: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-3 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-3 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-non-exist-secretRef + namespace: default + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + issuer: https://accounts.google.com + tokenEndpoint: https://oauth2.googleapis.com/token + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + conditions: + - lastTransitionTime: null + message: Secret default/client1-secret does not exist. + reason: Invalid + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-no-referenceGrant + namespace: default + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client2-secret + namespace: envoy-gateway + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + issuer: https://accounts.google.com + tokenEndpoint: https://oauth2.googleapis.com/token + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + status: + conditions: + - lastTransitionTime: null + message: Secret ref namespace must be unspecified/empty or default. + reason: Invalid + status: "False" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-no-client-secret-key + namespace: default + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client3-secret + namespace: default + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + issuer: https://accounts.google.com + tokenEndpoint: https://oauth2.googleapis.com/token + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + status: + conditions: + - lastTransitionTime: null + message: Client secret not found in secret default/client3-secret. + reason: Invalid + status: "False" + type: Accepted +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + port: 10080 + default/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-2/http + port: 10080 + default/gateway-3: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-3/http + port: 10080 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml new file mode 100644 index 00000000000..889fbfbfc8e --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml @@ -0,0 +1,148 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: client1-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client2-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client3-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/bar" + backendRefs: + - name: service-1 + port: 8080 +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway-discover-endpoints # This policy should attach httproute-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + oidc: + provider: + issuer: "https://accounts.google.com" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route # This policy should attach httproute-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + oidc: + provider: + issuer: "https://oauth.foo.com" + authorizationEndpoint: "https://oauth.foo.com/oauth2/v2/auth" + tokenEndpoint: "https://oauth.foo.com/token" + clientID: "client2.oauth.foo.com" + clientSecret: + name: "client2-secret" + scopes: ["openid", "email", "profile"] +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-cross-namespace-secretRef # This policy should attach grpcroute-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + oidc: + provider: + issuer: "https://oauth.bar.com" + authorizationEndpoint: "https://oauth.bar.com/oauth2/v2/auth" + tokenEndpoint: "https://oauth.bar.com/token" + clientID: "client3.bar.foo.com" + clientSecret: + namespace: default + name: "client3-secret" diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml new file mode 100755 index 00000000000..8091312d89d --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml @@ -0,0 +1,346 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 3 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + oidc: + clientID: client2.oauth.foo.com + clientSecret: + group: null + kind: null + name: client2-secret + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + issuer: https://oauth.foo.com + tokenEndpoint: https://oauth.foo.com/token + scopes: + - openid + - email + - profile + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-cross-namespace-secretRef + namespace: default + spec: + oidc: + clientID: client3.bar.foo.com + clientSecret: + group: null + kind: null + name: client3-secret + namespace: default + provider: + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + issuer: https://oauth.bar.com + tokenEndpoint: https://oauth.bar.com/token + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-discover-endpoints + namespace: envoy-gateway + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + issuer: https://accounts.google.com + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: SecurityPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + oidc: + clientID: client2.oauth.foo.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + tokenEndpoint: https://oauth.foo.com/token + scopes: + - openid + - email + - profile + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + tokenEndpoint: https://oauth2.googleapis.com/token + scopes: + - openid + pathMatch: + distinct: false + name: "" + prefix: /bar + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + oidc: + clientID: client3.bar.foo.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + tokenEndpoint: https://oauth.bar.com/token + scopes: + - openid diff --git a/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml index 92954ea1b89..ec0c3d12932 100644 --- a/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml +++ b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml index 814dd1783c2..f559411d4f2 100644 --- a/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml +++ b/internal/gatewayapi/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -34,6 +34,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -92,9 +97,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: TCP + weight: 1 name: envoy-gateway/gateway-1/tls/tcproute-1 port: 10090 tls: diff --git a/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.in.yaml b/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.in.yaml index 71db6c0ece6..a10119662dd 100644 --- a/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml b/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml index 18d26c52cd5..6ca40cecbe8 100644 --- a/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-attaching-to-gateway.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -31,6 +31,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -89,10 +94,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls/tlsroute-1 port: 10090 tls: diff --git a/internal/gatewayapi/testdata/tlsroute-multiple.in.yaml b/internal/gatewayapi/testdata/tlsroute-multiple.in.yaml index f7c756f0cca..e7d44bece2b 100644 --- a/internal/gatewayapi/testdata/tlsroute-multiple.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-multiple.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -46,13 +46,3 @@ tlsRoutes: - backendRefs: - name: service-1 port: 8080 -services: -- apiVersion: v1 - kind: Service - metadata: - namespace: default - name: service-1 - spec: - clusterIP: 7.7.7.7 - ports: - - port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml b/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml index b49cfb0be41..8d81524ea90 100644 --- a/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-multiple.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -30,6 +30,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -123,10 +128,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls/tlsroute-1 port: 10091 tls: @@ -134,10 +144,15 @@ xdsIR: snis: - foo.com - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-2/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls/tlsroute-2 port: 10091 tls: diff --git a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml index 38395404cd8..f37773dbd8d 100644 --- a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml index 64956827b81..a6855fec83d 100644 --- a/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -16,13 +16,18 @@ gateways: protocol: TLS status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Listener must have TLS set when protocol is TLS. reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml b/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml index d18aca10b82..00e85a87370 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -38,9 +38,30 @@ services: namespace: test-service-namespace name: service-1 spec: - clusterIP: 7.7.7.7 + clusterIP: 1.1.1.1 ports: - - port: 8080 + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: test-service-namespace + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true referenceGrants: - apiVersion: gateway.networking.k8s.io/v1alpha2 kind: ReferenceGrant diff --git a/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml index 3844ee9783d..e65dda7e64b 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -31,6 +31,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -90,10 +95,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls/tlsroute-1 port: 10090 tls: diff --git a/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.in.yaml b/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.in.yaml index 071fa048f72..76f60b0752e 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -29,13 +29,3 @@ tlsRoutes: - backendRefs: - name: service-1 port: 8080 -services: -- apiVersion: v1 - kind: Service - metadata: - namespace: default - name: service-1 - spec: - clusterIP: 7.7.7.7 - ports: - - port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml index cf23a735a30..f843877510f 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-empty-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -30,6 +30,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -88,10 +93,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls/tlsroute-1 port: 10091 tls: diff --git a/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.in.yaml b/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.in.yaml index f5c8e1349a6..621f046a9b4 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.in.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway @@ -31,13 +31,3 @@ tlsRoutes: - backendRefs: - name: service-1 port: 8080 -services: -- apiVersion: v1 - kind: Service - metadata: - namespace: default - name: service-1 - spec: - clusterIP: 7.7.7.7 - ports: - - port: 8080 diff --git a/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml index 884544ed3a1..8c6b78ec44c 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-empty-listener-hostname.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -30,6 +30,11 @@ gateways: reason: Accepted status: "True" type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io @@ -90,10 +95,15 @@ xdsIR: - path: /dev/stdout tcp: - address: 0.0.0.0 - destinations: - - host: 7.7.7.7 - port: 8080 - weight: 1 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 name: envoy-gateway/gateway-1/tls/tlsroute-1 port: 10091 tls: diff --git a/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml b/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml index 81afa2331b1..26a91e256a0 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml @@ -1,5 +1,5 @@ gateways: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: envoy-gateway diff --git a/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml index b3358757136..3f70d651a8c 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml @@ -1,5 +1,5 @@ gateways: -- apiVersion: gateway.networking.k8s.io/v1beta1 +- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: creationTimestamp: null @@ -23,13 +23,18 @@ gateways: mode: Passthrough status: listeners: - - attachedRoutes: 0 + - attachedRoutes: 1 conditions: - lastTransitionTime: null message: Listener must not have TLS certificate refs set for TLS mode Passthrough. reason: Invalid status: "False" type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs name: tls supportedKinds: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/tls.go b/internal/gatewayapi/tls.go index c04dc967d97..356b152b770 100644 --- a/internal/gatewayapi/tls.go +++ b/internal/gatewayapi/tls.go @@ -11,12 +11,12 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) // validateTLSSecretData ensures the cert and key provided in a secret // is not malformed and can be properly parsed -func validateTLSSecretsData(secrets []*corev1.Secret, host *v1beta1.Hostname) error { +func validateTLSSecretsData(secrets []*corev1.Secret, host *v1.Hostname) error { var publicKeyAlgorithm string var parseErr error @@ -50,7 +50,7 @@ func validateTLSSecretsData(secrets []*corev1.Secret, host *v1beta1.Hostname) er // Check whether the public key algorithm and matched certificate FQDN in the referenced secrets are unique. if matchedFQDN, ok := pkaSecretSet[pkaSecretKey]; ok { - return fmt.Errorf("%s/%s public key algorithm must be unique, matched cerificate FQDN %s has a conficting algorithm [%s]", + return fmt.Errorf("%s/%s public key algorithm must be unique, matched certificate FQDN %s has a conflicting algorithm [%s]", secret.Namespace, secret.Name, matchedFQDN, publicKeyAlgorithm) } @@ -81,7 +81,7 @@ func validateTLSSecretsData(secrets []*corev1.Secret, host *v1beta1.Hostname) er } // verifyHostname checks if the listener Hostname matches any domain in the certificate, returns a list of matched hosts. -func verifyHostname(cert *x509.Certificate, host *v1beta1.Hostname) ([]string, error) { +func verifyHostname(cert *x509.Certificate, host *v1.Hostname) ([]string, error) { var matchedHosts []string if len(cert.DNSNames) > 0 { diff --git a/internal/gatewayapi/tls_test.go b/internal/gatewayapi/tls_test.go index 94098730179..f1d1d2706f6 100644 --- a/internal/gatewayapi/tls_test.go +++ b/internal/gatewayapi/tls_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) const ( @@ -83,7 +83,7 @@ func TestValidateTLSSecretsData(t *testing.T) { Name string CertFile string KeyFile string - Domain v1beta1.Hostname + Domain v1.Hostname ExpectedErr error } diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index 689d7d34763..418f653fe1e 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -6,27 +6,35 @@ package gatewayapi import ( + "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" ) const ( - KindEnvoyProxy = "EnvoyProxy" - KindGateway = "Gateway" - KindGatewayClass = "GatewayClass" - KindGRPCRoute = "GRPCRoute" - KindHTTPRoute = "HTTPRoute" - KindNamespace = "Namespace" - KindTLSRoute = "TLSRoute" - KindTCPRoute = "TCPRoute" - KindUDPRoute = "UDPRoute" - KindService = "Service" - KindSecret = "Secret" - + KindEnvoyProxy = "EnvoyProxy" + KindGateway = "Gateway" + KindGatewayClass = "GatewayClass" + KindGRPCRoute = "GRPCRoute" + KindHTTPRoute = "HTTPRoute" + KindNamespace = "Namespace" + KindTLSRoute = "TLSRoute" + KindTCPRoute = "TCPRoute" + KindUDPRoute = "UDPRoute" + KindService = "Service" + KindServiceImport = "ServiceImport" + KindSecret = "Secret" + KindSecurityPolicy = "SecurityPolicy" + + GroupMultiClusterService = "multicluster.x-k8s.io" // OwningGatewayNamespaceLabel is the owner reference label used for managed infra. // The value should be the namespace of the accepted Envoy Gateway. OwningGatewayNamespaceLabel = "gateway.envoyproxy.io/owning-gateway-namespace" + OwningGatewayClassLabel = "gateway.envoyproxy.io/owning-gatewayclass" // OwningGatewayNameLabel is the owner reference label used for managed infra. // The value should be the name of the accepted Envoy Gateway. OwningGatewayNameLabel = "gateway.envoyproxy.io/owning-gateway-name" @@ -42,7 +50,7 @@ var _ TranslatorManager = (*Translator)(nil) type TranslatorManager interface { Translate(resources *Resources) *TranslateResult - GetRelevantGateways(gateways []*v1beta1.Gateway) []*GatewayContext + GetRelevantGateways(gateways []*gwapiv1.Gateway) []*GatewayContext RoutesTranslator ListenersTranslator @@ -58,12 +66,21 @@ type Translator struct { // GatewayClassName is the name of the GatewayClass // to process Gateways for. - GatewayClassName v1beta1.ObjectName + GatewayClassName gwapiv1.ObjectName // GlobalRateLimitEnabled is true when global // ratelimiting has been configured by the admin. GlobalRateLimitEnabled bool + // EndpointRoutingDisabled can be set to true to use + // the Service Cluster IP for routing to the backend + // instead. + EndpointRoutingDisabled bool + + // MergeGateways is true when all Gateway Listeners + // should be merged under the parent GatewayClass. + MergeGateways bool + // ExtensionGroupKinds stores the group/kind for all resources // introduced by an Extension so that the translator can // store referenced resources in the IR for later use. @@ -82,6 +99,9 @@ func newTranslateResult(gateways []*GatewayContext, tlsRoutes []*TLSRouteContext, tcpRoutes []*TCPRouteContext, udpRoutes []*UDPRouteContext, + clientTrafficPolicies []*egv1a1.ClientTrafficPolicy, + backendTrafficPolicies []*egv1a1.BackendTrafficPolicy, + securityPolicies []*egv1a1.SecurityPolicy, xdsIR XdsIRMap, infraIR InfraIRMap) *TranslateResult { translateResult := &TranslateResult{ XdsIR: xdsIR, @@ -107,22 +127,29 @@ func newTranslateResult(gateways []*GatewayContext, translateResult.UDPRoutes = append(translateResult.UDPRoutes, udpRoute.UDPRoute) } + translateResult.ClientTrafficPolicies = append(translateResult.ClientTrafficPolicies, clientTrafficPolicies...) + translateResult.BackendTrafficPolicies = append(translateResult.BackendTrafficPolicies, backendTrafficPolicies...) + translateResult.SecurityPolicies = append(translateResult.SecurityPolicies, securityPolicies...) + return translateResult } func (t *Translator) Translate(resources *Resources) *TranslateResult { - xdsIR := make(XdsIRMap) - infraIR := make(InfraIRMap) - // Get Gateways belonging to our GatewayClass. gateways := t.GetRelevantGateways(resources.Gateways) + // Build IR maps. + xdsIR, infraIR := t.InitIRs(gateways, resources) + // Process all Listeners for all relevant Gateways. t.ProcessListeners(gateways, xdsIR, infraIR, resources) // Process EnvoyPatchPolicies t.ProcessEnvoyPatchPolicies(resources.EnvoyPatchPolicies, xdsIR) + // Process ClientTrafficPolicies + clientTrafficPolicies := ProcessClientTrafficPolicies(resources.ClientTrafficPolicies, gateways, xdsIR) + // Process all Addresses for all relevant Gateways. t.ProcessAddresses(gateways, xdsIR, infraIR, resources) @@ -141,15 +168,44 @@ func (t *Translator) Translate(resources *Resources) *TranslateResult { // Process all relevant UDPRoutes. udpRoutes := t.ProcessUDPRoutes(resources.UDPRoutes, gateways, resources, xdsIR) + // Process BackendTrafficPolicies + routes := []RouteContext{} + for _, h := range httpRoutes { + routes = append(routes, h) + } + for _, g := range grpcRoutes { + routes = append(routes, g) + } + for _, t := range tlsRoutes { + routes = append(routes, t) + } + for _, t := range tcpRoutes { + routes = append(routes, t) + } + for _, u := range udpRoutes { + routes = append(routes, u) + } + + // Process BackendTrafficPolicies + backendTrafficPolicies := t.ProcessBackendTrafficPolicies( + resources.BackendTrafficPolicies, gateways, routes, xdsIR) + + // Process SecurityPolicies + securityPolicies := t.ProcessSecurityPolicies( + resources.SecurityPolicies, gateways, routes, resources, xdsIR) + // Sort xdsIR based on the Gateway API spec sortXdsIRMap(xdsIR) - return newTranslateResult(gateways, httpRoutes, grpcRoutes, tlsRoutes, tcpRoutes, udpRoutes, xdsIR, infraIR) + return newTranslateResult(gateways, httpRoutes, grpcRoutes, tlsRoutes, + tcpRoutes, udpRoutes, clientTrafficPolicies, backendTrafficPolicies, + securityPolicies, xdsIR, infraIR) + } // GetRelevantGateways returns GatewayContexts, containing a copy of the original // Gateway with the Listener statuses reset. -func (t *Translator) GetRelevantGateways(gateways []*v1beta1.Gateway) []*GatewayContext { +func (t *Translator) GetRelevantGateways(gateways []*gwapiv1.Gateway) []*GatewayContext { var relevant []*GatewayContext for _, gateway := range gateways { @@ -169,3 +225,69 @@ func (t *Translator) GetRelevantGateways(gateways []*v1beta1.Gateway) []*Gateway return relevant } + +// InitIRs checks if mergeGateways is enabled in EnvoyProxy config and initializes XdsIR and InfraIR maps with adequate keys. +func (t *Translator) InitIRs(gateways []*GatewayContext, resources *Resources) (map[string]*ir.Xds, map[string]*ir.Infra) { + xdsIR := make(XdsIRMap) + infraIR := make(InfraIRMap) + + var irKey string + for _, gateway := range gateways { + gwXdsIR := &ir.Xds{} + gwInfraIR := ir.NewInfra() + labels := infrastructureLabels(gateway.Gateway) + annotations := infrastructureAnnotations(gateway.Gateway) + gwInfraIR.Proxy.GetProxyMetadata().Annotations = annotations + + if isMergeGatewaysEnabled(resources) { + t.MergeGateways = true + irKey = string(t.GatewayClassName) + + maps.Copy(labels, GatewayClassOwnerLabel(string(t.GatewayClassName))) + gwInfraIR.Proxy.GetProxyMetadata().Labels = labels + } else { + irKey = irStringKey(gateway.Gateway.Namespace, gateway.Gateway.Name) + + maps.Copy(labels, GatewayOwnerLabels(gateway.Namespace, gateway.Name)) + gwInfraIR.Proxy.GetProxyMetadata().Labels = labels + } + + gwInfraIR.Proxy.Name = irKey + // save the IR references in the map before the translation starts + xdsIR[irKey] = gwXdsIR + infraIR[irKey] = gwInfraIR + } + + return xdsIR, infraIR +} + +func infrastructureAnnotations(gtw *gwapiv1.Gateway) map[string]string { + if gtw.Spec.Infrastructure != nil && len(gtw.Spec.Infrastructure.Annotations) > 0 { + res := make(map[string]string) + for k, v := range gtw.Spec.Infrastructure.Annotations { + res[string(k)] = string(v) + } + return res + } + return nil +} + +func infrastructureLabels(gtw *gwapiv1.Gateway) map[string]string { + res := make(map[string]string) + if gtw.Spec.Infrastructure != nil { + for k, v := range gtw.Spec.Infrastructure.Labels { + res[string(k)] = string(v) + } + } + return res +} + +// XdsIR and InfraIR map keys by default are {GatewayNamespace}/{GatewayName}, but if mergeGateways is set, they are merged under {GatewayClassName} key. +func (t *Translator) getIRKey(gateway *gwapiv1.Gateway) string { + irKey := irStringKey(gateway.Namespace, gateway.Name) + if t.MergeGateways { + return string(t.GatewayClassName) + } + + return irKey +} diff --git a/internal/gatewayapi/translator_test.go b/internal/gatewayapi/translator_test.go index eff48d06296..1887f39ecc1 100644 --- a/internal/gatewayapi/translator_test.go +++ b/internal/gatewayapi/translator_test.go @@ -20,13 +20,16 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/gateway-api/apis/v1alpha2" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + "sigs.k8s.io/gateway-api/apis/v1beta1" "sigs.k8s.io/yaml" - egv1alpha1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/utils/field" "github.com/envoyproxy/gateway/internal/utils/file" ) @@ -53,26 +56,92 @@ func TestTranslate(t *testing.T) { mustUnmarshal(t, input, resources) translator := &Translator{ - GatewayControllerName: egv1alpha1.GatewayControllerName, + GatewayControllerName: egv1a1.GatewayControllerName, GatewayClassName: "envoy-gateway-class", GlobalRateLimitEnabled: true, } // Add common test fixtures for i := 1; i <= 3; i++ { + svcName := "service-" + strconv.Itoa(i) + epSliceName := "endpointslice-" + strconv.Itoa(i) resources.Services = append(resources.Services, - &v1.Service{ + &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", - Name: "service-" + strconv.Itoa(i), + Name: svcName, }, - Spec: v1.ServiceSpec{ - ClusterIP: "7.7.7.7", - Ports: []v1.ServicePort{ - {Port: 8080, Protocol: v1.ProtocolTCP}, - {Port: 8443, Protocol: v1.ProtocolTCP}, - {Port: 8163, Protocol: v1.ProtocolTCP}, - {Port: 8162, Protocol: v1.ProtocolUDP}, + Spec: corev1.ServiceSpec{ + ClusterIP: "1.1.1.1", + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 8080, + TargetPort: intstr.IntOrString{IntVal: 8080}, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "https", + Port: 8443, + TargetPort: intstr.IntOrString{IntVal: 8443}, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "tcp", + Port: 8163, + TargetPort: intstr.IntOrString{IntVal: 8163}, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "udp", + Port: 8162, + TargetPort: intstr.IntOrString{IntVal: 8162}, + Protocol: corev1.ProtocolUDP, + }, + }, + }, + }, + ) + resources.EndpointSlices = append(resources.EndpointSlices, + &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: epSliceName, + Namespace: "default", + Labels: map[string]string{ + discoveryv1.LabelServiceName: svcName, + }, + }, + AddressType: discoveryv1.AddressTypeIPv4, + Ports: []discoveryv1.EndpointPort{ + { + Name: ptr.To("http"), + Port: ptr.To[int32](8080), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + { + Name: ptr.To("https"), + Port: ptr.To[int32](8443), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + { + Name: ptr.To("tcp"), + Port: ptr.To[int32](8163), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + { + Name: ptr.To("udp"), + Port: ptr.To[int32](8162), + Protocol: ptr.To(corev1.ProtocolUDP), + }, + }, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{ + "7.7.7.7", + }, + Conditions: discoveryv1.EndpointConditions{ + Ready: ptr.To(true), + }, }, }, }, @@ -80,25 +149,59 @@ func TestTranslate(t *testing.T) { } resources.Services = append(resources.Services, - &v1.Service{ + &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "mirror-service", }, - Spec: v1.ServiceSpec{ - ClusterIP: "7.6.5.4", - Ports: []v1.ServicePort{ - {Port: 8080, Protocol: v1.ProtocolTCP}, + Spec: corev1.ServiceSpec{ + ClusterIP: "2.2.2.2", + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 8080, + TargetPort: intstr.IntOrString{IntVal: 8080}, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + }, + ) + resources.EndpointSlices = append(resources.EndpointSlices, + &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mirror-service-endpointslice", + Namespace: "default", + Labels: map[string]string{ + discoveryv1.LabelServiceName: "mirror-service", + }, + }, + AddressType: discoveryv1.AddressTypeIPv4, + Ports: []discoveryv1.EndpointPort{ + { + Name: ptr.To("http"), + Port: ptr.To[int32](8080), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + }, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{ + "7.6.5.4", + }, + Conditions: discoveryv1.EndpointConditions{ + Ready: ptr.To(true), + }, }, }, }, ) - resources.Namespaces = append(resources.Namespaces, &v1.Namespace{ + resources.Namespaces = append(resources.Namespaces, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "envoy-gateway", }, - }, &v1.Namespace{ + }, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "default", }, @@ -140,7 +243,7 @@ func TestTranslateWithExtensionKinds(t *testing.T) { mustUnmarshal(t, input, resources) translator := &Translator{ - GatewayControllerName: egv1alpha1.GatewayControllerName, + GatewayControllerName: egv1a1.GatewayControllerName, GatewayClassName: "envoy-gateway-class", GlobalRateLimitEnabled: true, ExtensionGroupKinds: []schema.GroupKind{{Group: "foo.example.io", Kind: "Foo"}}, @@ -148,19 +251,85 @@ func TestTranslateWithExtensionKinds(t *testing.T) { // Add common test fixtures for i := 1; i <= 3; i++ { + svcName := "service-" + strconv.Itoa(i) + epSliceName := "endpointslice-" + strconv.Itoa(i) resources.Services = append(resources.Services, - &v1.Service{ + &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", - Name: "service-" + strconv.Itoa(i), + Name: svcName, }, - Spec: v1.ServiceSpec{ - ClusterIP: "7.7.7.7", - Ports: []v1.ServicePort{ - {Port: 8080, Protocol: v1.ProtocolTCP}, - {Port: 8443, Protocol: v1.ProtocolTCP}, - {Port: 8163, Protocol: v1.ProtocolTCP}, - {Port: 8162, Protocol: v1.ProtocolUDP}, + Spec: corev1.ServiceSpec{ + ClusterIP: "1.1.1.1", + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 8080, + TargetPort: intstr.IntOrString{IntVal: 8080}, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "https", + Port: 8443, + TargetPort: intstr.IntOrString{IntVal: 8443}, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "tcp", + Port: 8163, + TargetPort: intstr.IntOrString{IntVal: 8163}, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "udp", + Port: 8162, + TargetPort: intstr.IntOrString{IntVal: 8162}, + Protocol: corev1.ProtocolUDP, + }, + }, + }, + }, + ) + resources.EndpointSlices = append(resources.EndpointSlices, + &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: epSliceName, + Namespace: "default", + Labels: map[string]string{ + discoveryv1.LabelServiceName: svcName, + }, + }, + AddressType: discoveryv1.AddressTypeIPv4, + Ports: []discoveryv1.EndpointPort{ + { + Name: ptr.To("http"), + Port: ptr.To[int32](8080), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + { + Name: ptr.To("https"), + Port: ptr.To[int32](8443), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + { + Name: ptr.To("tcp"), + Port: ptr.To[int32](8163), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + { + Name: ptr.To("udp"), + Port: ptr.To[int32](8162), + Protocol: ptr.To(corev1.ProtocolUDP), + }, + }, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{ + "7.7.7.7", + }, + Conditions: discoveryv1.EndpointConditions{ + Ready: ptr.To(true), + }, }, }, }, @@ -168,25 +337,58 @@ func TestTranslateWithExtensionKinds(t *testing.T) { } resources.Services = append(resources.Services, - &v1.Service{ + &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "mirror-service", }, - Spec: v1.ServiceSpec{ - ClusterIP: "7.6.5.4", - Ports: []v1.ServicePort{ - {Port: 8080, Protocol: v1.ProtocolTCP}, + Spec: corev1.ServiceSpec{ + ClusterIP: "2.2.2.2", + Ports: []corev1.ServicePort{ + { + Port: 8080, + TargetPort: intstr.IntOrString{IntVal: 8080}, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + }, + ) + resources.EndpointSlices = append(resources.EndpointSlices, + &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mirror-service-endpointslice", + Namespace: "default", + Labels: map[string]string{ + discoveryv1.LabelServiceName: "mirror-service", + }, + }, + AddressType: discoveryv1.AddressTypeIPv4, + Ports: []discoveryv1.EndpointPort{ + { + Name: ptr.To("http"), + Port: ptr.To[int32](8080), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + }, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{ + "7.6.5.4", + }, + Conditions: discoveryv1.EndpointConditions{ + Ready: ptr.To(true), + }, }, }, }, ) - resources.Namespaces = append(resources.Namespaces, &v1.Namespace{ + resources.Namespaces = append(resources.Namespaces, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "envoy-gateway", }, - }, &v1.Namespace{ + }, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "default", }, @@ -335,7 +537,7 @@ func TestIsValidCrossNamespaceRef(t *testing.T) { name string from crossNamespaceFrom to crossNamespaceTo - referenceGrant *v1alpha2.ReferenceGrant + referenceGrant *v1beta1.ReferenceGrant want bool } @@ -357,20 +559,20 @@ func TestIsValidCrossNamespaceRef(t *testing.T) { namespace: "default", name: "tls-secret-1", }, - referenceGrant: &v1alpha2.ReferenceGrant{ + referenceGrant: &v1beta1.ReferenceGrant{ ObjectMeta: metav1.ObjectMeta{ Name: "referencegrant-1", Namespace: "default", }, - Spec: v1alpha2.ReferenceGrantSpec{ - From: []v1alpha2.ReferenceGrantFrom{ + Spec: v1beta1.ReferenceGrantSpec{ + From: []v1beta1.ReferenceGrantFrom{ { Group: "gateway.networking.k8s.io", Kind: "Gateway", Namespace: "envoy-gateway-system", }, }, - To: []v1alpha2.ReferenceGrantTo{ + To: []v1beta1.ReferenceGrantTo{ { Group: "", Kind: "Secret", @@ -440,7 +642,7 @@ func TestIsValidCrossNamespaceRef(t *testing.T) { for _, tc := range testcases { tc := tc t.Run(tc.name, func(t *testing.T) { - var referenceGrants []*v1alpha2.ReferenceGrant + var referenceGrants []*v1beta1.ReferenceGrant if tc.referenceGrant != nil { referenceGrants = append(referenceGrants, tc.referenceGrant) } diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 921eb51edb9..8acc278a46d 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -6,20 +6,23 @@ package gatewayapi import ( + "errors" "fmt" "net/netip" + "regexp" "strings" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -func (t *Translator) validateBackendRef(backendRef *v1alpha2.BackendRef, parentRef *RouteParentContext, route RouteContext, - resources *Resources, serviceNamespace string, routeKind v1beta1.Kind) bool { +func (t *Translator) validateBackendRef(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, + resources *Resources, backendNamespace string, routeKind gwapiv1.Kind) bool { if !t.validateBackendRefGroup(backendRef, parentRef, route) { return false } @@ -36,60 +39,68 @@ func (t *Translator) validateBackendRef(backendRef *v1alpha2.BackendRef, parentR if routeKind == KindUDPRoute { protocol = v1.ProtocolUDP } - if !t.validateBackendService(backendRef, parentRef, resources, serviceNamespace, route, protocol) { - return false + backendRefKind := KindDerefOr(backendRef.Kind, KindService) + switch backendRefKind { + case KindService: + if !t.validateBackendService(backendRef, parentRef, resources, backendNamespace, route, protocol) { + return false + } + case KindServiceImport: + if !t.validateBackendServiceImport(backendRef, parentRef, resources, backendNamespace, route, protocol) { + return false + } } return true } -func (t *Translator) validateBackendRefGroup(backendRef *v1alpha2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { - if backendRef.Group != nil && *backendRef.Group != "" { +func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { + if backendRef.Group != nil && *backendRef.Group != "" && *backendRef.Group != GroupMultiClusterService { parentRef.SetCondition(route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.RouteReasonInvalidKind, - "Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported", + gwapiv1.RouteReasonInvalidKind, + fmt.Sprintf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) and %s is supported", GroupMultiClusterService), ) return false } return true } -func (t *Translator) validateBackendRefKind(backendRef *v1alpha2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { - if backendRef.Kind != nil && *backendRef.Kind != KindService { +func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { + if backendRef.Kind != nil && *backendRef.Kind != KindService && *backendRef.Kind != KindServiceImport { parentRef.SetCondition(route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.RouteReasonInvalidKind, - "Kind is invalid, only Service is supported", + gwapiv1.RouteReasonInvalidKind, + "Kind is invalid, only Service and MCS ServiceImport is supported", ) return false } return true } -func (t *Translator) validateBackendNamespace(backendRef *v1alpha2.BackendRef, parentRef *RouteParentContext, route RouteContext, - resources *Resources, routeKind v1beta1.Kind) bool { +func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, + resources *Resources, routeKind gwapiv1.Kind) bool { if backendRef.Namespace != nil && string(*backendRef.Namespace) != "" && string(*backendRef.Namespace) != route.GetNamespace() { if !t.validateCrossNamespaceRef( crossNamespaceFrom{ - group: v1beta1.GroupName, + group: gwapiv1.GroupName, kind: string(routeKind), namespace: route.GetNamespace(), }, crossNamespaceTo{ - group: "", - kind: KindService, + group: GroupDerefOr(backendRef.Group, ""), + kind: KindDerefOr(backendRef.Kind, KindService), namespace: string(*backendRef.Namespace), name: string(backendRef.Name), }, resources.ReferenceGrants, ) { parentRef.SetCondition(route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.RouteReasonRefNotPermitted, - fmt.Sprintf("Backend ref to service %s/%s not permitted by any ReferenceGrant.", *backendRef.Namespace, backendRef.Name), + gwapiv1.RouteReasonRefNotPermitted, + fmt.Sprintf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", KindDerefOr(backendRef.Kind, KindService), *backendRef.Namespace, backendRef.Name), ) return false } @@ -97,10 +108,10 @@ func (t *Translator) validateBackendNamespace(backendRef *v1alpha2.BackendRef, p return true } -func (t *Translator) validateBackendPort(backendRef *v1alpha2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { +func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { if backendRef.Port == nil { parentRef.SetCondition(route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, "PortNotSpecified", "A valid port number corresponding to a port on the Service must be specified", @@ -109,14 +120,14 @@ func (t *Translator) validateBackendPort(backendRef *v1alpha2.BackendRef, parent } return true } -func (t *Translator) validateBackendService(backendRef *v1alpha2.BackendRef, parentRef *RouteParentContext, resources *Resources, +func (t *Translator) validateBackendService(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, resources *Resources, serviceNamespace string, route RouteContext, protocol v1.Protocol) bool { service := resources.GetService(serviceNamespace, string(backendRef.Name)) if service == nil { parentRef.SetCondition(route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.RouteReasonBackendNotFound, + gwapiv1.RouteReasonBackendNotFound, fmt.Sprintf("Service %s/%s not found", NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()), string(backendRef.Name)), ) @@ -136,7 +147,7 @@ func (t *Translator) validateBackendService(backendRef *v1alpha2.BackendRef, par if !portFound { parentRef.SetCondition(route, - v1beta1.RouteConditionResolvedRefs, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, "PortNotFound", fmt.Sprintf(string(protocol)+" Port %d not found on service %s/%s", *backendRef.Port, serviceNamespace, @@ -147,34 +158,86 @@ func (t *Translator) validateBackendService(backendRef *v1alpha2.BackendRef, par return true } +func (t *Translator) validateBackendServiceImport(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, resources *Resources, + serviceImportNamespace string, route RouteContext, protocol v1.Protocol) bool { + serviceImport := resources.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) + if serviceImport == nil { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonBackendNotFound, + fmt.Sprintf("ServiceImport %s/%s not found", NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()), + string(backendRef.Name)), + ) + return false + } + var portFound bool + for _, port := range serviceImport.Spec.Ports { + portProtocol := port.Protocol + if port.Protocol == "" { // Default protocol is TCP + portProtocol = v1.ProtocolTCP + } + if port.Port == int32(*backendRef.Port) && portProtocol == protocol { + portFound = true + break + } + } + + if !portFound { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "PortNotFound", + fmt.Sprintf(string(protocol)+" Port %d not found on ServiceImport %s/%s", *backendRef.Port, serviceImportNamespace, + string(backendRef.Name)), + ) + return false + } + return true +} + func (t *Translator) validateListenerConditions(listener *ListenerContext) (isReady bool) { lConditions := listener.GetConditions() if len(lConditions) == 0 { - listener.SetCondition(v1beta1.ListenerConditionProgrammed, metav1.ConditionTrue, v1beta1.ListenerReasonProgrammed, + listener.SetCondition(gwapiv1.ListenerConditionProgrammed, metav1.ConditionTrue, gwapiv1.ListenerReasonProgrammed, "Sending translated listener configuration to the data plane") - listener.SetCondition(v1beta1.ListenerConditionAccepted, metav1.ConditionTrue, v1beta1.ListenerReasonAccepted, + listener.SetCondition(gwapiv1.ListenerConditionAccepted, metav1.ConditionTrue, gwapiv1.ListenerReasonAccepted, "Listener has been successfully translated") + listener.SetCondition(gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionTrue, gwapiv1.ListenerReasonResolvedRefs, + "Listener references have been resolved") return true } // Any condition on the listener apart from Programmed=true indicates an error. - if !(lConditions[0].Type == string(v1beta1.ListenerConditionProgrammed) && lConditions[0].Status == metav1.ConditionTrue) { - // set "Programmed: false" if it's not set already. - var hasReadyCond bool + if !(lConditions[0].Type == string(gwapiv1.ListenerConditionProgrammed) && lConditions[0].Status == metav1.ConditionTrue) { + hasProgrammedCond := false + hasRefsCond := false for _, existing := range lConditions { - if existing.Type == string(v1beta1.ListenerConditionProgrammed) { - hasReadyCond = true - break + if existing.Type == string(gwapiv1.ListenerConditionProgrammed) { + hasProgrammedCond = true + } + if existing.Type == string(gwapiv1.ListenerConditionResolvedRefs) { + hasRefsCond = true } } - if !hasReadyCond { + // set "Programmed: false" if it's not set already. + if !hasProgrammedCond { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, "Listener is invalid, see other Conditions for details.", ) } + // set "ResolvedRefs: true" if it's not set already. + if !hasRefsCond { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.ListenerReasonResolvedRefs, + "Listener references have been resolved", + ) + } // skip computing IR return false } @@ -185,21 +248,21 @@ func (t *Translator) validateAllowedNamespaces(listener *ListenerContext) { if listener.AllowedRoutes != nil && listener.AllowedRoutes.Namespaces != nil && listener.AllowedRoutes.Namespaces.From != nil && - *listener.AllowedRoutes.Namespaces.From == v1beta1.NamespacesFromSelector { + *listener.AllowedRoutes.Namespaces.From == gwapiv1.NamespacesFromSelector { if listener.AllowedRoutes.Namespaces.Selector == nil { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, "The allowedRoutes.namespaces.selector field must be specified when allowedRoutes.namespaces.from is set to \"Selector\".", ) } else { selector, err := metav1.LabelSelectorAsSelector(listener.AllowedRoutes.Namespaces.Selector) if err != nil { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, fmt.Sprintf("The allowedRoutes.namespaces.selector could not be parsed: %v.", err), ) } @@ -212,9 +275,9 @@ func (t *Translator) validateAllowedNamespaces(listener *ListenerContext) { func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerContext, resources *Resources) []*v1.Secret { if len(listener.TLS.CertificateRefs) == 0 { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, "Listener must have at least 1 TLS certificate ref", ) return nil @@ -222,11 +285,12 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon secrets := make([]*v1.Secret, 0) for _, certificateRef := range listener.TLS.CertificateRefs { + // TODO zhaohuabing: reuse validateSecretRef if certificateRef.Group != nil && string(*certificateRef.Group) != "" { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, + gwapiv1.ListenerReasonInvalidCertificateRef, "Listener's TLS certificate ref group must be unspecified/empty.", ) break @@ -234,9 +298,9 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon if certificateRef.Kind != nil && string(*certificateRef.Kind) != KindSecret { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, + gwapiv1.ListenerReasonInvalidCertificateRef, fmt.Sprintf("Listener's TLS certificate ref kind must be %s.", KindSecret), ) break @@ -247,7 +311,7 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon if certificateRef.Namespace != nil && string(*certificateRef.Namespace) != "" && string(*certificateRef.Namespace) != listener.gateway.Namespace { if !t.validateCrossNamespaceRef( crossNamespaceFrom{ - group: v1beta1.GroupName, + group: gwapiv1.GroupName, kind: KindGateway, namespace: listener.gateway.Namespace, }, @@ -260,9 +324,9 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon resources.ReferenceGrants, ) { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonRefNotPermitted, + gwapiv1.ListenerReasonRefNotPermitted, fmt.Sprintf("Certificate ref to secret %s/%s not permitted by any ReferenceGrant.", *certificateRef.Namespace, certificateRef.Name), ) break @@ -275,9 +339,9 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon if secret == nil { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, + gwapiv1.ListenerReasonInvalidCertificateRef, fmt.Sprintf("Secret %s/%s does not exist.", listener.gateway.Namespace, certificateRef.Name), ) break @@ -285,9 +349,9 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon if secret.Type != v1.SecretTypeTLS { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, + gwapiv1.ListenerReasonInvalidCertificateRef, fmt.Sprintf("Secret %s/%s must be of type %s.", listener.gateway.Namespace, certificateRef.Name, v1.SecretTypeTLS), ) break @@ -295,9 +359,9 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon if len(secret.Data[v1.TLSCertKey]) == 0 || len(secret.Data[v1.TLSPrivateKeyKey]) == 0 { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, + gwapiv1.ListenerReasonInvalidCertificateRef, fmt.Sprintf("Secret %s/%s must contain %s and %s.", listener.gateway.Namespace, certificateRef.Name, v1.TLSCertKey, v1.TLSPrivateKeyKey), ) break @@ -309,9 +373,9 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon err := validateTLSSecretsData(secrets, listener.Hostname) if err != nil { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidCertificateRef, + gwapiv1.ListenerReasonInvalidCertificateRef, fmt.Sprintf("Secret %s.", err.Error()), ) } @@ -321,29 +385,29 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resources *Resources) { switch listener.Protocol { - case v1beta1.HTTPProtocolType, v1beta1.UDPProtocolType, v1beta1.TCPProtocolType: + case gwapiv1.HTTPProtocolType, gwapiv1.UDPProtocolType, gwapiv1.TCPProtocolType: if listener.TLS != nil { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, fmt.Sprintf("Listener must not have TLS set when protocol is %s.", listener.Protocol), ) } - case v1beta1.HTTPSProtocolType: + case gwapiv1.HTTPSProtocolType: if listener.TLS == nil { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, fmt.Sprintf("Listener must have TLS set when protocol is %s.", listener.Protocol), ) break } - if listener.TLS.Mode != nil && *listener.TLS.Mode != v1beta1.TLSModeTerminate { + if listener.TLS.Mode != nil && *listener.TLS.Mode != gwapiv1.TLSModeTerminate { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, "UnsupportedTLSMode", fmt.Sprintf("TLS %s mode is not supported, TLS mode must be Terminate.", *listener.TLS.Mode), @@ -354,35 +418,35 @@ func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resourc secrets := t.validateTerminateModeAndGetTLSSecrets(listener, resources) listener.SetTLSSecrets(secrets) - case v1beta1.TLSProtocolType: + case gwapiv1.TLSProtocolType: if listener.TLS == nil { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, fmt.Sprintf("Listener must have TLS set when protocol is %s.", listener.Protocol), ) break } - if listener.TLS.Mode != nil && *listener.TLS.Mode == v1beta1.TLSModePassthrough { + if listener.TLS.Mode != nil && *listener.TLS.Mode == gwapiv1.TLSModePassthrough { if len(listener.TLS.CertificateRefs) > 0 { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, "Listener must not have TLS certificate refs set for TLS mode Passthrough.", ) break } } - if listener.TLS.Mode != nil && *listener.TLS.Mode == v1beta1.TLSModeTerminate { + if listener.TLS.Mode != nil && *listener.TLS.Mode == gwapiv1.TLSModeTerminate { if len(listener.TLS.CertificateRefs) == 0 { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, "Listener must have TLS certificate refs set for TLS mode Terminate.", ) break @@ -394,41 +458,41 @@ func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resourc } func (t *Translator) validateHostName(listener *ListenerContext) { - if listener.Protocol == v1beta1.UDPProtocolType || listener.Protocol == v1beta1.TCPProtocolType { + if listener.Protocol == gwapiv1.UDPProtocolType || listener.Protocol == gwapiv1.TCPProtocolType { if listener.Hostname != nil { listener.SetCondition( - v1beta1.ListenerConditionProgrammed, + gwapiv1.ListenerConditionProgrammed, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalid, + gwapiv1.ListenerReasonInvalid, fmt.Sprintf("Listener must not have hostname set when protocol is %s.", listener.Protocol), ) } } } -func (t *Translator) validateAllowedRoutes(listener *ListenerContext, routeKinds ...v1beta1.Kind) { - canSupportKinds := make([]v1beta1.RouteGroupKind, len(routeKinds)) +func (t *Translator) validateAllowedRoutes(listener *ListenerContext, routeKinds ...gwapiv1.Kind) { + canSupportKinds := make([]gwapiv1.RouteGroupKind, len(routeKinds)) for i, routeKind := range routeKinds { - canSupportKinds[i] = v1beta1.RouteGroupKind{Group: GroupPtr(v1beta1.GroupName), Kind: routeKind} + canSupportKinds[i] = gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: routeKind} } if listener.AllowedRoutes == nil || len(listener.AllowedRoutes.Kinds) == 0 { listener.SetSupportedKinds(canSupportKinds...) return } - supportedRouteKinds := make([]v1beta1.Kind, 0) - supportedKinds := make([]v1beta1.RouteGroupKind, 0) - unSupportedKinds := make([]v1beta1.RouteGroupKind, 0) + supportedRouteKinds := make([]gwapiv1.Kind, 0) + supportedKinds := make([]gwapiv1.RouteGroupKind, 0) + unSupportedKinds := make([]gwapiv1.RouteGroupKind, 0) for _, kind := range listener.AllowedRoutes.Kinds { // if there is a group it must match `gateway.networking.k8s.io` - if kind.Group != nil && string(*kind.Group) != v1beta1.GroupName { + if kind.Group != nil && string(*kind.Group) != gwapiv1.GroupName { listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidRouteKinds, - fmt.Sprintf("Group is not supported, group must be %s", v1beta1.GroupName), + gwapiv1.ListenerReasonInvalidRouteKinds, + fmt.Sprintf("Group is not supported, group must be %s", gwapiv1.GroupName), ) continue } @@ -449,16 +513,16 @@ func (t *Translator) validateAllowedRoutes(listener *ListenerContext, routeKinds } for _, kind := range unSupportedKinds { - var printRouteKinds []v1beta1.Kind + var printRouteKinds []gwapiv1.Kind if len(supportedKinds) == 0 { printRouteKinds = routeKinds } else { printRouteKinds = supportedRouteKinds } listener.SetCondition( - v1beta1.ListenerConditionResolvedRefs, + gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionFalse, - v1beta1.ListenerReasonInvalidRouteKinds, + gwapiv1.ListenerReasonInvalidRouteKinds, fmt.Sprintf("%s is not supported, kind must be one of %v", string(kind.Kind), printRouteKinds), ) } @@ -472,13 +536,36 @@ type portListeners struct { hostnames map[string]int } +// Port, protocol and hostname tuple should be unique across all listeners on merged Gateways. +func (t *Translator) validateConflictedMergedListeners(gateways []*GatewayContext) { + listenerSets := sets.Set[string]{} + for _, gateway := range gateways { + for _, listener := range gateway.listeners { + hostname := new(gwapiv1.Hostname) + if listener.Hostname != nil { + hostname = listener.Hostname + } + portProtocolHostname := fmt.Sprintf("%s:%s:%d", listener.Protocol, *hostname, listener.Port) + if listenerSets.Has(portProtocolHostname) { + listener.SetCondition( + gwapiv1.ListenerConditionConflicted, + metav1.ConditionTrue, + gwapiv1.ListenerReasonHostnameConflict, + "Port, protocol and hostname tuple must be unique for every listener", + ) + } + listenerSets.Insert(portProtocolHostname) + } + } +} + func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContext) { // Iterate through all layer-7 (HTTP, HTTPS, TLS) listeners and collect info about protocols // and hostnames per port. for _, gateway := range gateways { - portListenerInfo := map[v1beta1.PortNumber]*portListeners{} + portListenerInfo := map[gwapiv1.PortNumber]*portListeners{} for _, listener := range gateway.listeners { - if listener.Protocol == v1beta1.UDPProtocolType || listener.Protocol == v1beta1.TCPProtocolType { + if listener.Protocol == gwapiv1.UDPProtocolType || listener.Protocol == gwapiv1.TCPProtocolType { continue } if portListenerInfo[listener.Port] == nil { @@ -493,7 +580,7 @@ func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContex var protocol string switch listener.Protocol { // HTTPS and TLS can co-exist on the same port - case v1beta1.HTTPSProtocolType, v1beta1.TLSProtocolType: + case gwapiv1.HTTPSProtocolType, gwapiv1.TLSProtocolType: protocol = "https/tls" default: protocol = string(listener.Protocol) @@ -513,9 +600,9 @@ func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContex for _, listener := range info.listeners { if len(info.protocols) > 1 { listener.SetCondition( - v1beta1.ListenerConditionConflicted, + gwapiv1.ListenerConditionConflicted, metav1.ConditionTrue, - v1beta1.ListenerReasonProtocolConflict, + gwapiv1.ListenerReasonProtocolConflict, "All listeners for a given port must use a compatible protocol", ) } @@ -527,9 +614,9 @@ func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContex if info.hostnames[hostname] > 1 { listener.SetCondition( - v1beta1.ListenerConditionConflicted, + gwapiv1.ListenerConditionConflicted, metav1.ConditionTrue, - v1beta1.ListenerReasonHostnameConflict, + gwapiv1.ListenerReasonHostnameConflict, "All listeners for a given port must use a unique hostname", ) } @@ -538,10 +625,10 @@ func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContex } } -func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContext, protocols ...v1beta1.ProtocolType) { +func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContext, protocols ...gwapiv1.ProtocolType) { // Iterate through all layer-4(TCP UDP) listeners and check if there are more than one listener on the same port for _, gateway := range gateways { - portListenerInfo := map[v1beta1.PortNumber]*portListeners{} + portListenerInfo := map[gwapiv1.PortNumber]*portListeners{} for _, listener := range gateway.listeners { for _, protocol := range protocols { if listener.Protocol == protocol { @@ -558,9 +645,9 @@ func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContex if len(info.listeners) > 1 { for i := 1; i < len(info.listeners); i++ { info.listeners[i].SetCondition( - v1beta1.ListenerConditionConflicted, + gwapiv1.ListenerConditionConflicted, metav1.ConditionTrue, - v1beta1.ListenerReasonProtocolConflict, + gwapiv1.ListenerReasonProtocolConflict, fmt.Sprintf("Only one %s listener is allowed in a given port", strings.Join(protocolSliceToStringSlice(protocols), "/")), ) } @@ -569,7 +656,7 @@ func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContex } } -func (t *Translator) validateCrossNamespaceRef(from crossNamespaceFrom, to crossNamespaceTo, referenceGrants []*v1alpha2.ReferenceGrant) bool { +func (t *Translator) validateCrossNamespaceRef(from crossNamespaceFrom, to crossNamespaceTo, referenceGrants []*gwapiv1b1.ReferenceGrant) bool { for _, referenceGrant := range referenceGrants { // The ReferenceGrant must be defined in the namespace of // the "to" (the referent). @@ -638,3 +725,68 @@ func (t *Translator) validateHostname(hostname string) error { return nil } + +// validateSecretRef checks three things: +// 1. Does the secret reference have a valid Group and kind +// 2. If the secret reference is a cross-namespace reference, +// is it permitted by any ReferenceGrant +// 3. Does the secret exist +func (t *Translator) validateSecretRef( + allowCrossNamespace bool, + from crossNamespaceFrom, + secretRef gwapiv1b1.SecretObjectReference, + resources *Resources) (*v1.Secret, error) { + if secretRef.Group != nil && string(*secretRef.Group) != "" { + return nil, errors.New("secret ref group must be unspecified/empty") + } + + if secretRef.Kind != nil && string(*secretRef.Kind) != KindSecret { + return nil, fmt.Errorf("secret ref kind must be %s", KindSecret) + } + + secretNamespace := from.namespace + + if secretRef.Namespace != nil && + string(*secretRef.Namespace) != "" && + string(*secretRef.Namespace) != from.namespace { + if !allowCrossNamespace { + return nil, fmt.Errorf( + "secret ref namespace must be unspecified/empty or %s", + from.namespace) + } + + if !t.validateCrossNamespaceRef( + from, + crossNamespaceTo{ + group: "", + kind: KindSecret, + namespace: string(*secretRef.Namespace), + name: string(secretRef.Name), + }, + resources.ReferenceGrants, + ) { + return nil, + fmt.Errorf( + "certificate ref to secret %s/%s not permitted by any ReferenceGrant", + *secretRef.Namespace, secretRef.Name) + } + + secretNamespace = string(*secretRef.Namespace) + } + + secret := resources.GetSecret(secretNamespace, string(secretRef.Name)) + + if secret == nil { + return nil, fmt.Errorf( + "secret %s/%s does not exist", secretNamespace, secretRef.Name) + } + + return secret, nil +} + +func validateRegex(regex string) error { + if _, err := regexp.Compile(regex); err != nil { + return fmt.Errorf("regex %q is invalid: %v", regex, err) + } + return nil +} diff --git a/internal/gatewayapi/validate_test.go b/internal/gatewayapi/validate_test.go new file mode 100644 index 00000000000..86dd9317025 --- /dev/null +++ b/internal/gatewayapi/validate_test.go @@ -0,0 +1,38 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package gatewayapi + +import ( + "testing" +) + +func Test_validateRegex(t *testing.T) { + tests := []struct { + name string + regex string + wantErr bool + }{ + { + name: "valid regex", + regex: "^[a-zA-Z0-9-]+$", + wantErr: false, + }, + { + name: "invalid regex", + regex: "*.foo.com", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateRegex(tt.regex) + if (err != nil) != tt.wantErr { + t.Errorf("validateRegex() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/internal/gatewayapi/zz_generated.deepcopy.go b/internal/gatewayapi/zz_generated.deepcopy.go index fd342be5c02..56335dc5bc9 100644 --- a/internal/gatewayapi/zz_generated.deepcopy.go +++ b/internal/gatewayapi/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated // Copyright Envoy Gateway Authors // SPDX-License-Identifier: Apache-2.0 @@ -11,13 +10,14 @@ package gatewayapi import ( - configv1alpha1 "github.com/envoyproxy/gateway/api/config/v1alpha1" - "github.com/envoyproxy/gateway/api/v1alpha1" - "k8s.io/api/core/v1" + apiv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1" + corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1beta1" + "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -25,27 +25,27 @@ func (in *Resources) DeepCopyInto(out *Resources) { *out = *in if in.GatewayClass != nil { in, out := &in.GatewayClass, &out.GatewayClass - *out = new(v1beta1.GatewayClass) + *out = new(v1.GatewayClass) (*in).DeepCopyInto(*out) } if in.Gateways != nil { in, out := &in.Gateways, &out.Gateways - *out = make([]*v1beta1.Gateway, len(*in)) + *out = make([]*v1.Gateway, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1beta1.Gateway) + *out = new(v1.Gateway) (*in).DeepCopyInto(*out) } } } if in.HTTPRoutes != nil { in, out := &in.HTTPRoutes, &out.HTTPRoutes - *out = make([]*v1beta1.HTTPRoute, len(*in)) + *out = make([]*v1.HTTPRoute, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1beta1.HTTPRoute) + *out = new(v1.HTTPRoute) (*in).DeepCopyInto(*out) } } @@ -96,33 +96,44 @@ func (in *Resources) DeepCopyInto(out *Resources) { } if in.ReferenceGrants != nil { in, out := &in.ReferenceGrants, &out.ReferenceGrants - *out = make([]*v1alpha2.ReferenceGrant, len(*in)) + *out = make([]*v1beta1.ReferenceGrant, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1alpha2.ReferenceGrant) + *out = new(v1beta1.ReferenceGrant) (*in).DeepCopyInto(*out) } } } if in.Namespaces != nil { in, out := &in.Namespaces, &out.Namespaces - *out = make([]*v1.Namespace, len(*in)) + *out = make([]*corev1.Namespace, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1.Namespace) + *out = new(corev1.Namespace) (*in).DeepCopyInto(*out) } } } if in.Services != nil { in, out := &in.Services, &out.Services - *out = make([]*v1.Service, len(*in)) + *out = make([]*corev1.Service, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1.Service) + *out = new(corev1.Service) + (*in).DeepCopyInto(*out) + } + } + } + if in.ServiceImports != nil { + in, out := &in.ServiceImports, &out.ServiceImports + *out = make([]*v1alpha1.ServiceImport, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha1.ServiceImport) (*in).DeepCopyInto(*out) } } @@ -140,56 +151,67 @@ func (in *Resources) DeepCopyInto(out *Resources) { } if in.Secrets != nil { in, out := &in.Secrets, &out.Secrets - *out = make([]*v1.Secret, len(*in)) + *out = make([]*corev1.Secret, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1.Secret) + *out = new(corev1.Secret) (*in).DeepCopyInto(*out) } } } - if in.AuthenticationFilters != nil { - in, out := &in.AuthenticationFilters, &out.AuthenticationFilters - *out = make([]*v1alpha1.AuthenticationFilter, len(*in)) + if in.EnvoyProxy != nil { + in, out := &in.EnvoyProxy, &out.EnvoyProxy + *out = new(apiv1alpha1.EnvoyProxy) + (*in).DeepCopyInto(*out) + } + if in.ExtensionRefFilters != nil { + in, out := &in.ExtensionRefFilters, &out.ExtensionRefFilters + *out = make([]unstructured.Unstructured, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.EnvoyPatchPolicies != nil { + in, out := &in.EnvoyPatchPolicies, &out.EnvoyPatchPolicies + *out = make([]*apiv1alpha1.EnvoyPatchPolicy, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1alpha1.AuthenticationFilter) + *out = new(apiv1alpha1.EnvoyPatchPolicy) (*in).DeepCopyInto(*out) } } } - if in.RateLimitFilters != nil { - in, out := &in.RateLimitFilters, &out.RateLimitFilters - *out = make([]*v1alpha1.RateLimitFilter, len(*in)) + if in.ClientTrafficPolicies != nil { + in, out := &in.ClientTrafficPolicies, &out.ClientTrafficPolicies + *out = make([]*apiv1alpha1.ClientTrafficPolicy, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1alpha1.RateLimitFilter) + *out = new(apiv1alpha1.ClientTrafficPolicy) (*in).DeepCopyInto(*out) } } } - if in.EnvoyProxy != nil { - in, out := &in.EnvoyProxy, &out.EnvoyProxy - *out = new(configv1alpha1.EnvoyProxy) - (*in).DeepCopyInto(*out) - } - if in.ExtensionRefFilters != nil { - in, out := &in.ExtensionRefFilters, &out.ExtensionRefFilters - *out = make([]unstructured.Unstructured, len(*in)) + if in.BackendTrafficPolicies != nil { + in, out := &in.BackendTrafficPolicies, &out.BackendTrafficPolicies + *out = make([]*apiv1alpha1.BackendTrafficPolicy, len(*in)) for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(apiv1alpha1.BackendTrafficPolicy) + (*in).DeepCopyInto(*out) + } } } - if in.EnvoyPatchPolicies != nil { - in, out := &in.EnvoyPatchPolicies, &out.EnvoyPatchPolicies - *out = make([]*v1alpha1.EnvoyPatchPolicy, len(*in)) + if in.SecurityPolicies != nil { + in, out := &in.SecurityPolicies, &out.SecurityPolicies + *out = make([]*apiv1alpha1.SecurityPolicy, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1alpha1.EnvoyPatchPolicy) + *out = new(apiv1alpha1.SecurityPolicy) (*in).DeepCopyInto(*out) } } diff --git a/internal/globalratelimit/runner/runner.go b/internal/globalratelimit/runner/runner.go index 88acfcefc7f..a5ff5fed73a 100644 --- a/internal/globalratelimit/runner/runner.go +++ b/internal/globalratelimit/runner/runner.go @@ -24,7 +24,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" "github.com/envoyproxy/gateway/internal/ir" @@ -65,7 +65,7 @@ func New(cfg *Config) *Runner { } // Start starts the infrastructure runner -func (r *Runner) Start(ctx context.Context) error { +func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) // Set up the gRPC server and register the xDS handler. @@ -81,44 +81,51 @@ func (r *Runner) Start(ctx context.Context) error { discoveryv3.RegisterAggregatedDiscoveryServiceServer(r.grpc, serverv3.NewServer(ctx, r.cache, cb)) // Start and listen xDS gRPC config Server. - go r.serverXdsConfigServer(ctx) + go r.serveXdsConfigServer(ctx) // Start message Subscription. go r.subscribeAndTranslate(ctx) r.Logger.Info("started") - return nil + return } -func (r *Runner) serverXdsConfigServer(ctx context.Context) { +func (r *Runner) serveXdsConfigServer(ctx context.Context) { addr := net.JoinHostPort(XdsGrpcSotwConfigServerAddress, strconv.Itoa(ratelimit.XdsGrpcSotwConfigServerPort)) l, err := net.Listen("tcp", addr) if err != nil { r.Logger.Error(err, "failed to listen on address", "address", addr) return } + + go func() { + <-ctx.Done() + r.Logger.Info("grpc server shutting down") + r.grpc.Stop() + }() + if err = r.grpc.Serve(l); err != nil { r.Logger.Error(err, "failed to start grpc based xds config server") } - - <-ctx.Done() - r.Logger.Info("grpc config server shutting down") - r.grpc.Stop() } func (r *Runner) subscribeAndTranslate(ctx context.Context) { // Subscribe to resources. - message.HandleSubscription(r.XdsIR.Subscribe(ctx), - func(update message.Update[string, *ir.Xds]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentGlobalRateLimitRunner), Message: "xds-ir"}, r.XdsIR.Subscribe(ctx), + func(update message.Update[string, *ir.Xds], errChan chan error) { r.Logger.Info("received a notification") if update.Delete { if err := r.addNewSnapshot(ctx, nil); err != nil { r.Logger.Error(err, "failed to update the config snapshot") + errChan <- err } } else { // Translate to ratelimit xDS Config. - rvt := r.translate(update.Value) + rvt, err := r.translate(update.Value) + if err != nil { + r.Logger.Error(err, err.Error()) + } // Update ratelimit xDS config cache. if rvt != nil { @@ -130,17 +137,19 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { r.Logger.Info("subscriber shutting down") } -func (r *Runner) translate(xdsIR *ir.Xds) *types.ResourceVersionTable { +func (r *Runner) translate(xdsIR *ir.Xds) (*types.ResourceVersionTable, error) { resourceVT := new(types.ResourceVersionTable) for _, listener := range xdsIR.HTTP { cfg := translator.BuildRateLimitServiceConfig(listener) if cfg != nil { // Add to xDS Config resources. - resourceVT.AddXdsResource(resourcev3.RateLimitConfigType, cfg) + if err := resourceVT.AddXdsResource(resourcev3.RateLimitConfigType, cfg); err != nil { + return nil, err + } } } - return resourceVT + return resourceVT, nil } func (r *Runner) updateSnapshot(ctx context.Context, resource types.XdsResources) { diff --git a/internal/infrastructure/kubernetes/infra.go b/internal/infrastructure/kubernetes/infra.go index 632006af6aa..0d1f6e18c03 100644 --- a/internal/infrastructure/kubernetes/infra.go +++ b/internal/infrastructure/kubernetes/infra.go @@ -10,10 +10,11 @@ import ( "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" ) @@ -25,6 +26,7 @@ type ResourceRender interface { Service() (*corev1.Service, error) ConfigMap() (*corev1.ConfigMap, error) Deployment() (*appsv1.Deployment, error) + HorizontalPodAutoscaler() (*autoscalingv2.HorizontalPodAutoscaler, error) } // Infra manages the creation and deletion of Kubernetes infrastructure @@ -68,6 +70,10 @@ func (i *Infra) createOrUpdate(ctx context.Context, r ResourceRender) error { return errors.Wrapf(err, "failed to create or update service %s/%s", i.Namespace, r.Name()) } + if err := i.createOrUpdateHPA(ctx, r); err != nil { + return errors.Wrapf(err, "failed to create or update hpa %s/%s", i.Namespace, r.Name()) + } + return nil } @@ -89,5 +95,9 @@ func (i *Infra) delete(ctx context.Context, r ResourceRender) error { return errors.Wrapf(err, "failed to delete service %s/%s", i.Namespace, r.Name()) } + if err := i.deleteHPA(ctx, r); err != nil { + return errors.Wrapf(err, "failed to delete hpa %s/%s", i.Namespace, r.Name()) + } + return nil } diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go index af041ad3313..d0e21be628e 100644 --- a/internal/infrastructure/kubernetes/infra_resource.go +++ b/internal/infrastructure/kubernetes/infra_resource.go @@ -9,7 +9,10 @@ import ( "context" "reflect" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -74,8 +77,44 @@ func (i *Infra) createOrUpdateDeployment(ctx context.Context, r ResourceRender) Name: deployment.Name, } + hpa, err := r.HorizontalPodAutoscaler() + if err != nil { + return err + } + + var opts cmp.Options + if hpa != nil { + opts = append(opts, cmpopts.IgnoreFields(appsv1.DeploymentSpec{}, "Replicas")) + } + return i.Client.CreateOrUpdate(ctx, key, current, deployment, func() bool { - return !reflect.DeepEqual(deployment.Spec, current.Spec) + return !cmp.Equal(current.Spec, deployment.Spec, opts...) + }) +} + +// createOrUpdateHPA creates HorizontalPodAutoscaler object in the kube api server based on +// the provided ResourceRender, if it doesn't exist and updates it if it does, +// and delete hpa if not set. +func (i *Infra) createOrUpdateHPA(ctx context.Context, r ResourceRender) error { + hpa, err := r.HorizontalPodAutoscaler() + if err != nil { + return err + } + + // when HorizontalPodAutoscaler is not set, + // then delete the object in the kube api server if any. + if hpa == nil { + return i.deleteHPA(ctx, r) + } + + current := &autoscalingv2.HorizontalPodAutoscaler{} + key := types.NamespacedName{ + Namespace: hpa.Namespace, + Name: hpa.Name, + } + + return i.Client.CreateOrUpdate(ctx, key, current, hpa, func() bool { + return !cmp.Equal(hpa.Spec, current.Spec) }) } @@ -145,3 +184,15 @@ func (i *Infra) deleteService(ctx context.Context, r ResourceRender) error { return i.Client.Delete(ctx, svc) } + +// deleteHpa deletes the Horizontal Pod Autoscaler associated to its renderer, if it exists. +func (i *Infra) deleteHPA(ctx context.Context, r ResourceRender) error { + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: r.Name(), + }, + } + + return i.Client.Delete(ctx, hpa) +} diff --git a/internal/infrastructure/kubernetes/proxy/resource.go b/internal/infrastructure/kubernetes/proxy/resource.go index 9e5501cd9d3..b4ccc760286 100644 --- a/internal/infrastructure/kubernetes/proxy/resource.go +++ b/internal/infrastructure/kubernetes/proxy/resource.go @@ -7,14 +7,12 @@ package proxy import ( "fmt" - "sort" - "strings" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" "github.com/envoyproxy/gateway/internal/ir" @@ -52,9 +50,9 @@ var ( `"private_key":{"filename":"%s"}}}]}`, XdsTLSCertFilename, XdsTLSKeyFilename) ) -// ExpectedResourceHashedName returns expected resource hashed name. +// ExpectedResourceHashedName returns expected resource hashed name including up to the 48 characters of the original name. func ExpectedResourceHashedName(name string) string { - hashedName := providerutils.GetHashedName(name) + hashedName := providerutils.GetHashedName(name, 48) return fmt.Sprintf("%s-%s", config.EnvoyPrefix, hashedName) } @@ -86,8 +84,21 @@ func envoyLabels(extraLabels map[string]string) map[string]string { return labels } +func enablePrometheus(infra *ir.ProxyInfra) bool { + if infra.Config != nil && + infra.Config.Spec.Telemetry != nil && + infra.Config.Spec.Telemetry.Metrics != nil && + infra.Config.Spec.Telemetry.Metrics.Prometheus != nil && + infra.Config.Spec.Telemetry.Metrics.Prometheus.Disable { + return false + } + + return true +} + // expectedProxyContainers returns expected proxy containers. -func expectedProxyContainers(infra *ir.ProxyInfra, deploymentConfig *egcfgv1a1.KubernetesDeploymentSpec) ([]corev1.Container, error) { +func expectedProxyContainers(infra *ir.ProxyInfra, + deploymentConfig *egv1a1.KubernetesDeploymentSpec) ([]corev1.Container, error) { // Define slice to hold container ports var ports []corev1.ContainerPort @@ -104,7 +115,8 @@ func expectedProxyContainers(infra *ir.ProxyInfra, deploymentConfig *egcfgv1a1.K return nil, fmt.Errorf("invalid protocol %q", p.Protocol) } port := corev1.ContainerPort{ - Name: p.Name, + // hashed container port name including up to the 6 characters of the port name and the maximum of 15 characters. + Name: providerutils.GetHashedName(p.Name, 6), ContainerPort: p.ContainerPort, Protocol: protocol, } @@ -112,12 +124,7 @@ func expectedProxyContainers(infra *ir.ProxyInfra, deploymentConfig *egcfgv1a1.K } } - var proxyMetrics *egcfgv1a1.ProxyMetrics - if infra.Config != nil { - proxyMetrics = infra.Config.Spec.Telemetry.Metrics - } - - if proxyMetrics != nil && proxyMetrics.Prometheus != nil { + if enablePrometheus(infra) { ports = append(ports, corev1.ContainerPort{ Name: "metrics", ContainerPort: bootstrap.EnvoyReadinessPort, // TODO: make this configurable @@ -126,33 +133,44 @@ func expectedProxyContainers(infra *ir.ProxyInfra, deploymentConfig *egcfgv1a1.K } var bootstrapConfigurations string - // Get Bootstrap from EnvoyProxy API if set by the user - // The config should have been validated already + + var proxyMetrics *egv1a1.ProxyMetrics if infra.Config != nil && - infra.Config.Spec.Bootstrap != nil { - bootstrapConfigurations = *infra.Config.Spec.Bootstrap - } else { - var err error - // Use the default Bootstrap - bootstrapConfigurations, err = bootstrap.GetRenderedBootstrapConfig(proxyMetrics) + infra.Config.Spec.Telemetry != nil { + proxyMetrics = infra.Config.Spec.Telemetry.Metrics + } + // Get the default Bootstrap + bootstrapConfigurations, err := bootstrap.GetRenderedBootstrapConfig(proxyMetrics) + if err != nil { + return nil, err + } + + // Apply Bootstrap from EnvoyProxy API if set by the user + // The config should have been validated already + if infra.Config != nil && infra.Config.Spec.Bootstrap != nil { + bootstrapConfigurations, err = bootstrap.ApplyBootstrapConfig(infra.Config.Spec.Bootstrap, bootstrapConfigurations) if err != nil { return nil, err } } - proxyLogging := infra.Config.Spec.Logging - - logLevel := componentLogLevel(proxyLogging.Level, egcfgv1a1.LogComponentDefault, egcfgv1a1.LogLevelWarn) + logging := infra.Config.Spec.Logging args := []string{ fmt.Sprintf("--service-cluster %s", infra.Name), fmt.Sprintf("--service-node $(%s)", envoyPodEnvVar), fmt.Sprintf("--config-yaml %s", bootstrapConfigurations), - fmt.Sprintf("--log-level %s", logLevel), + fmt.Sprintf("--log-level %s", logging.DefaultEnvoyProxyLoggingLevel()), "--cpuset-threads", } - if componentLogLevel := componentLogLevelArgs(proxyLogging.Level); componentLogLevel != "" { - args = append(args, fmt.Sprintf("--component-log-level %s", componentLogLevel)) + + if infra.Config != nil && + infra.Config.Spec.Concurrency != nil { + args = append(args, fmt.Sprintf("--concurrency %d", *infra.Config.Spec.Concurrency)) + } + + if componentsLogLevel := logging.GetEnvoyProxyComponentLevel(); componentsLogLevel != "" { + args = append(args, fmt.Sprintf("--component-log-level %s", componentsLogLevel)) } containers := []corev1.Container{ @@ -188,32 +206,8 @@ func expectedProxyContainers(infra *ir.ProxyInfra, deploymentConfig *egcfgv1a1.K return containers, nil } -func componentLogLevel(levels map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel, component egcfgv1a1.LogComponent, defaultLevel egcfgv1a1.LogLevel) egcfgv1a1.LogLevel { - if level, ok := levels[component]; ok { - return level - } - - return defaultLevel -} - -func componentLogLevelArgs(levels map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel) string { - var args []string - - for component, level := range levels { - if component == egcfgv1a1.LogComponentDefault { - // Skip default component - continue - } - args = append(args, fmt.Sprintf("%s:%s", component, level)) - } - - sort.Strings(args) - - return strings.Join(args, ",") -} - // expectedContainerVolumeMounts returns expected proxy container volume mounts. -func expectedContainerVolumeMounts(deploymentSpec *egcfgv1a1.KubernetesDeploymentSpec) []corev1.VolumeMount { +func expectedContainerVolumeMounts(deploymentSpec *egv1a1.KubernetesDeploymentSpec) []corev1.VolumeMount { volumeMounts := []corev1.VolumeMount{ { Name: "certs", @@ -230,14 +224,14 @@ func expectedContainerVolumeMounts(deploymentSpec *egcfgv1a1.KubernetesDeploymen } // expectedDeploymentVolumes returns expected proxy deployment volumes. -func expectedDeploymentVolumes(name string, deploymentSpec *egcfgv1a1.KubernetesDeploymentSpec) []corev1.Volume { +func expectedDeploymentVolumes(name string, deploymentSpec *egv1a1.KubernetesDeploymentSpec) []corev1.Volume { volumes := []corev1.Volume{ { Name: "certs", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: "envoy", - DefaultMode: pointer.Int32(420), + DefaultMode: ptr.To[int32](420), }, }, }, @@ -258,8 +252,8 @@ func expectedDeploymentVolumes(name string, deploymentSpec *egcfgv1a1.Kubernetes Path: SdsCertFilename, }, }, - DefaultMode: pointer.Int32(420), - Optional: pointer.Bool(false), + DefaultMode: ptr.To[int32](420), + Optional: ptr.To(false), }, }, }, @@ -269,7 +263,7 @@ func expectedDeploymentVolumes(name string, deploymentSpec *egcfgv1a1.Kubernetes } // expectedProxyContainerEnv returns expected proxy container envs. -func expectedProxyContainerEnv(deploymentConfig *egcfgv1a1.KubernetesDeploymentSpec) []corev1.EnvVar { +func expectedProxyContainerEnv(deploymentConfig *egv1a1.KubernetesDeploymentSpec) []corev1.EnvVar { env := []corev1.EnvVar{ { Name: envoyNsEnvVar, diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go index 186c91c9f12..61a393d71a0 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go @@ -11,12 +11,13 @@ import ( "golang.org/x/exp/maps" appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" "github.com/envoyproxy/gateway/internal/ir" @@ -45,7 +46,7 @@ func (r *ResourceRender) Name() string { func (r *ResourceRender) ServiceAccount() (*corev1.ServiceAccount, error) { // Set the labels based on the owning gateway name. labels := envoyLabels(r.infra.GetProxyMetadata().Labels) - if len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 { + if (len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0) && len(labels[gatewayapi.OwningGatewayClassLabel]) == 0 { return nil, fmt.Errorf("missing owning gateway labels") } @@ -55,9 +56,10 @@ func (r *ResourceRender) ServiceAccount() (*corev1.ServiceAccount, error) { APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Namespace: r.Namespace, - Name: ExpectedResourceHashedName(r.infra.Name), - Labels: labels, + Namespace: r.Namespace, + Name: r.Name(), + Labels: labels, + Annotations: r.infra.GetProxyMetadata().Annotations, }, }, nil } @@ -72,8 +74,9 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { if port.Protocol == ir.UDPProtocolType { protocol = corev1.ProtocolUDP } + p := corev1.ServicePort{ - Name: port.Name, + Name: ExpectedResourceHashedName(port.Name), Protocol: protocol, Port: port.ServicePort, TargetPort: target, @@ -84,20 +87,25 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { // Set the labels based on the owning gatewayclass name. labels := envoyLabels(r.infra.GetProxyMetadata().Labels) - if len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 { + if (len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0) && len(labels[gatewayapi.OwningGatewayClassLabel]) == 0 { return nil, fmt.Errorf("missing owning gateway labels") } // Get annotations - var annotations map[string]string + annotations := map[string]string{} + maps.Copy(annotations, r.infra.GetProxyMetadata().Annotations) + provider := r.infra.GetProxyConfig().GetEnvoyProxyProvider() envoyServiceConfig := provider.GetEnvoyProxyKubeProvider().EnvoyService if envoyServiceConfig.Annotations != nil { - annotations = envoyServiceConfig.Annotations + maps.Copy(annotations, envoyServiceConfig.Annotations) + } + if len(annotations) == 0 { + annotations = nil } // Set the spec of gateway service - serviceSpec := resource.ExpectedServiceSpec(envoyServiceConfig.Type) + serviceSpec := resource.ExpectedServiceSpec(envoyServiceConfig) serviceSpec.Ports = ports serviceSpec.Selector = resource.GetSelector(labels).MatchLabels serviceSpec.ExternalIPs = r.infra.Addresses @@ -109,7 +117,7 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { }, ObjectMeta: metav1.ObjectMeta{ Namespace: r.Namespace, - Name: ExpectedResourceHashedName(r.infra.Name), + Name: r.Name(), Labels: labels, Annotations: annotations, }, @@ -123,7 +131,7 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { func (r *ResourceRender) ConfigMap() (*corev1.ConfigMap, error) { // Set the labels based on the owning gateway name. labels := envoyLabels(r.infra.GetProxyMetadata().Labels) - if len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 { + if (len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0) && len(labels[gatewayapi.OwningGatewayClassLabel]) == 0 { return nil, fmt.Errorf("missing owning gateway labels") } @@ -133,9 +141,10 @@ func (r *ResourceRender) ConfigMap() (*corev1.ConfigMap, error) { APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Namespace: r.Namespace, - Name: ExpectedResourceHashedName(r.infra.Name), - Labels: labels, + Namespace: r.Namespace, + Name: r.Name(), + Labels: labels, + Annotations: r.infra.GetProxyMetadata().Annotations, }, Data: map[string]string{ SdsCAFilename: SdsCAConfigMapData, @@ -148,18 +157,11 @@ func (r *ResourceRender) ConfigMap() (*corev1.ConfigMap, error) { func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { // Get the EnvoyProxy config to configure the deployment. provider := r.infra.GetProxyConfig().GetEnvoyProxyProvider() - if provider.Type != egcfgv1a1.ProviderTypeKubernetes { + if provider.Type != egv1a1.ProviderTypeKubernetes { return nil, fmt.Errorf("invalid provider type %v for Kubernetes infra manager", provider.Type) } deploymentConfig := provider.GetEnvoyProxyKubeProvider().EnvoyDeployment - enablePrometheus := false - if r.infra.Config != nil && - r.infra.Config.Spec.Telemetry.Metrics != nil && - r.infra.Config.Spec.Telemetry.Metrics.Prometheus != nil { - enablePrometheus = true - } - // Get expected bootstrap configurations rendered ProxyContainers containers, err := expectedProxyContainers(r.infra, deploymentConfig) if err != nil { @@ -167,9 +169,10 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { } // Set the labels based on the owning gateway name. + dpAnnotations := r.infra.GetProxyMetadata().Annotations labels := r.infra.GetProxyMetadata().Labels dpLabels := envoyLabels(labels) - if len(dpLabels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(dpLabels[gatewayapi.OwningGatewayNameLabel]) == 0 { + if (len(dpLabels[gatewayapi.OwningGatewayNameLabel]) == 0 || len(dpLabels[gatewayapi.OwningGatewayNamespaceLabel]) == 0) && len(dpLabels[gatewayapi.OwningGatewayClassLabel]) == 0 { return nil, fmt.Errorf("missing owning gateway labels") } @@ -178,17 +181,16 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { selector := resource.GetSelector(podLabels) // Get annotations - var annotations map[string]string - if deploymentConfig.Pod.Annotations != nil { - annotations = deploymentConfig.Pod.Annotations + podAnnotations := map[string]string{} + maps.Copy(podAnnotations, dpAnnotations) + maps.Copy(podAnnotations, deploymentConfig.Pod.Annotations) + if enablePrometheus(r.infra) { + podAnnotations["prometheus.io/path"] = "/stats/prometheus" // TODO: make this configurable + podAnnotations["prometheus.io/scrape"] = "true" + podAnnotations["prometheus.io/port"] = strconv.Itoa(bootstrap.EnvoyReadinessPort) } - if enablePrometheus { - if annotations == nil { - annotations = make(map[string]string, 2) - } - annotations["prometheus.io/path"] = "/stats/prometheus" // TODO: make this configurable - annotations["prometheus.io/scrape"] = "true" - annotations["prometheus.io/port"] = strconv.Itoa(bootstrap.EnvoyReadinessPort) + if len(podAnnotations) == 0 { + podAnnotations = nil } deployment := &appsv1.Deployment{ @@ -197,9 +199,10 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { APIVersion: "apps/v1", }, ObjectMeta: metav1.ObjectMeta{ - Namespace: r.Namespace, - Name: ExpectedResourceHashedName(r.infra.Name), - Labels: dpLabels, + Namespace: r.Namespace, + Name: r.Name(), + Labels: dpLabels, + Annotations: dpAnnotations, }, Spec: appsv1.DeploymentSpec{ Replicas: deploymentConfig.Replicas, @@ -208,26 +211,71 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: selector.MatchLabels, - Annotations: annotations, + Annotations: podAnnotations, }, Spec: corev1.PodSpec{ Containers: containers, + InitContainers: deploymentConfig.InitContainers, ServiceAccountName: ExpectedResourceHashedName(r.infra.Name), - AutomountServiceAccountToken: pointer.Bool(false), - TerminationGracePeriodSeconds: pointer.Int64(int64(300)), + AutomountServiceAccountToken: ptr.To(false), + TerminationGracePeriodSeconds: ptr.To[int64](300), DNSPolicy: corev1.DNSClusterFirst, RestartPolicy: corev1.RestartPolicyAlways, SchedulerName: "default-scheduler", SecurityContext: deploymentConfig.Pod.SecurityContext, + HostNetwork: deploymentConfig.Pod.HostNetwork, Affinity: deploymentConfig.Pod.Affinity, Tolerations: deploymentConfig.Pod.Tolerations, Volumes: expectedDeploymentVolumes(r.infra.Name, deploymentConfig), }, }, - RevisionHistoryLimit: pointer.Int32(10), - ProgressDeadlineSeconds: pointer.Int32(600), + RevisionHistoryLimit: ptr.To[int32](10), + ProgressDeadlineSeconds: ptr.To[int32](600), }, } + // omit the deployment replicas if HPA is being set + if provider.GetEnvoyProxyKubeProvider().EnvoyHpa != nil { + deployment.Spec.Replicas = nil + } + return deployment, nil } + +func (r *ResourceRender) HorizontalPodAutoscaler() (*autoscalingv2.HorizontalPodAutoscaler, error) { + provider := r.infra.GetProxyConfig().GetEnvoyProxyProvider() + if provider.Type != egv1a1.ProviderTypeKubernetes { + return nil, fmt.Errorf("invalid provider type %v for Kubernetes infra manager", provider.Type) + } + + hpaConfig := provider.GetEnvoyProxyKubeProvider().EnvoyHpa + if hpaConfig == nil { + return nil, nil + } + + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "autoscaling/v2", + Kind: "HorizontalPodAutoscaler", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.Namespace, + Name: r.Name(), + Annotations: r.infra.GetProxyMetadata().Annotations, + Labels: r.infra.GetProxyMetadata().Labels, + }, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: r.Name(), + }, + MinReplicas: hpaConfig.MinReplicas, + MaxReplicas: ptr.Deref(hpaConfig.MaxReplicas, 1), + Metrics: hpaConfig.Metrics, + Behavior: hpaConfig.Behavior, + }, + } + + return hpa, nil +} diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go index 2f08f795b55..935ac447bab 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go @@ -14,12 +14,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/yaml" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/ir" @@ -33,8 +34,20 @@ const ( ) func newTestInfra() *ir.Infra { + return newTestInfraWithAnnotations(nil) +} + +func newTestInfraWithAnnotations(annotations map[string]string) *ir.Infra { + return newTestInfraWithAnnotationsAndLabels(annotations, nil) +} + +func newTestInfraWithAnnotationsAndLabels(annotations, labels map[string]string) *ir.Infra { i := ir.NewInfra() + i.Proxy.GetProxyMetadata().Annotations = annotations + if len(labels) > 0 { + i.Proxy.GetProxyMetadata().Labels = labels + } i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = i.Proxy.Name i.Proxy.Listeners = []ir.ProxyListener{ @@ -64,10 +77,11 @@ func TestDeployment(t *testing.T) { cases := []struct { caseName string infra *ir.Infra - deploy *egcfgv1a1.KubernetesDeploymentSpec - proxyLogging map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel - bootstrap *string - telemetry *egcfgv1a1.ProxyTelemetry + deploy *egv1a1.KubernetesDeploymentSpec + proxyLogging map[egv1a1.ProxyLogComponent]egv1a1.LogLevel + bootstrap string + telemetry *egv1a1.ProxyTelemetry + concurrency *int32 }{ { caseName: "default", @@ -77,10 +91,10 @@ func TestDeployment(t *testing.T) { { caseName: "custom", infra: newTestInfra(), - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, @@ -88,11 +102,12 @@ func TestDeployment(t *testing.T) { "foo.bar": "custom-label", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, + HostNetwork: true, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ - Image: pointer.String("envoyproxy/envoy:v1.2.3"), + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("envoyproxy/envoy:v1.2.3"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -104,7 +119,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -113,23 +128,23 @@ func TestDeployment(t *testing.T) { caseName: "bootstrap", infra: newTestInfra(), deploy: nil, - bootstrap: pointer.String(`test bootstrap config`), + bootstrap: `test bootstrap config`, }, { caseName: "extension-env", infra: newTestInfra(), - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: "env_a", @@ -140,7 +155,7 @@ func TestDeployment(t *testing.T) { Value: "env_b_value", }, }, - Image: pointer.String("envoyproxy/envoy:v1.2.3"), + Image: ptr.To("envoyproxy/envoy:v1.2.3"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -152,7 +167,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -160,20 +175,20 @@ func TestDeployment(t *testing.T) { { caseName: "default-env", infra: newTestInfra(), - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: nil, - Image: pointer.String("envoyproxy/envoy:v1.2.3"), + Image: ptr.To("envoyproxy/envoy:v1.2.3"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -185,7 +200,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -193,15 +208,15 @@ func TestDeployment(t *testing.T) { { caseName: "volumes", infra: newTestInfra(), - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, Volumes: []corev1.Volume{ { @@ -209,13 +224,13 @@ func TestDeployment(t *testing.T) { VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: "custom-envoy-cert", - DefaultMode: pointer.Int32(420), + DefaultMode: ptr.To[int32](420), }, }, }, }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: "env_a", @@ -226,7 +241,7 @@ func TestDeployment(t *testing.T) { Value: "env_b_value", }, }, - Image: pointer.String("envoyproxy/envoy:v1.2.3"), + Image: ptr.To("envoyproxy/envoy:v1.2.3"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -238,7 +253,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -247,18 +262,116 @@ func TestDeployment(t *testing.T) { caseName: "component-level", infra: newTestInfra(), deploy: nil, - proxyLogging: map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel{ - egcfgv1a1.LogComponentDefault: egcfgv1a1.LogLevelError, - egcfgv1a1.LogComponentFilter: egcfgv1a1.LogLevelInfo, + proxyLogging: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ + egv1a1.LogComponentDefault: egv1a1.LogLevelError, + egv1a1.LogComponentFilter: egv1a1.LogLevelInfo, }, - bootstrap: pointer.String(`test bootstrap config`), + bootstrap: `test bootstrap config`, }, { caseName: "enable-prometheus", infra: newTestInfra(), - telemetry: &egcfgv1a1.ProxyTelemetry{ - Metrics: &egcfgv1a1.ProxyMetrics{ - Prometheus: &egcfgv1a1.PrometheusProvider{}, + telemetry: &egv1a1.ProxyTelemetry{ + Metrics: &egv1a1.ProxyMetrics{ + Prometheus: &egv1a1.ProxyPrometheusProvider{}, + }, + }, + }, + { + caseName: "with-concurrency", + infra: newTestInfra(), + deploy: nil, + concurrency: ptr.To[int32](4), + bootstrap: `test bootstrap config`, + }, + { + caseName: "custom_with_initcontainers", + infra: newTestInfra(), + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](3), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ + Annotations: map[string]string{ + "prometheus.io/scrape": "true", + }, + Labels: map[string]string{ + "foo.bar": "custom-label", + }, + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: ptr.To[int64](1000), + }, + Volumes: []corev1.Volume{ + { + Name: "custom-libs", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("envoyproxy/envoy:v1.2.3"), + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("400m"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("200m"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: ptr.To(true), + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "custom-libs", + MountPath: "/lib/filter_foo.so", + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "install-filter-foo", + Image: "alpine:3.11.3", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"echo \"Installing filter-foo\"; wget -q https://example.com/download/filter_foo_v1.0.0.tgz -O - | tar -xz --directory=/lib filter_foo.so; echo \"Done\";"}, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "custom-libs", + MountPath: "/lib", + }, + }, + }, + }, + }, + }, + { + caseName: "with-annotations", + infra: newTestInfraWithAnnotations(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }), + deploy: nil, + }, + { + caseName: "override-labels-and-annotations", + infra: newTestInfraWithAnnotationsAndLabels(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }, map[string]string{ + "label1": "value1", + "label2": "value2", + }), + deploy: &egv1a1.KubernetesDeploymentSpec{ + Pod: &egv1a1.KubernetesPodSpec{ + Annotations: map[string]string{ + "anno1": "value1-override", + }, + Labels: map[string]string{ + "label1": "value1-override", + }, }, }, }, @@ -270,20 +383,36 @@ func TestDeployment(t *testing.T) { kube.EnvoyDeployment = tc.deploy } - if tc.bootstrap != nil && *tc.bootstrap != "" { - tc.infra.Proxy.Config.Spec.Bootstrap = tc.bootstrap + replace := egv1a1.BootstrapTypeReplace + if tc.bootstrap != "" { + tc.infra.Proxy.Config.Spec.Bootstrap = &egv1a1.ProxyBootstrap{ + Type: &replace, + Value: tc.bootstrap, + } } if tc.telemetry != nil { - tc.infra.Proxy.Config.Spec.Telemetry = *tc.telemetry + tc.infra.Proxy.Config.Spec.Telemetry = tc.telemetry + } else { + tc.infra.Proxy.Config.Spec.Telemetry = &egv1a1.ProxyTelemetry{ + Metrics: &egv1a1.ProxyMetrics{ + Prometheus: &egv1a1.ProxyPrometheusProvider{ + Disable: true, + }, + }, + } } if len(tc.proxyLogging) > 0 { - tc.infra.Proxy.Config.Spec.Logging = egcfgv1a1.ProxyLogging{ + tc.infra.Proxy.Config.Spec.Logging = egv1a1.ProxyLogging{ Level: tc.proxyLogging, } } + if tc.concurrency != nil { + tc.infra.Proxy.Config.Spec.Concurrency = tc.concurrency + } + r := NewResourceRender(cfg.Namespace, tc.infra.GetProxyInfra()) dp, err := r.Deployment() require.NoError(t, err) @@ -318,11 +447,11 @@ func TestService(t *testing.T) { cfg, err := config.New() require.NoError(t, err) - svcType := egcfgv1a1.ServiceTypeClusterIP + svcType := egv1a1.ServiceTypeClusterIP cases := []struct { caseName string infra *ir.Infra - service *egcfgv1a1.KubernetesServiceSpec + service *egv1a1.KubernetesServiceSpec }{ { caseName: "default", @@ -332,13 +461,35 @@ func TestService(t *testing.T) { { caseName: "custom", infra: newTestInfra(), - service: &egcfgv1a1.KubernetesServiceSpec{ + service: &egv1a1.KubernetesServiceSpec{ Annotations: map[string]string{ "key1": "value1", }, Type: &svcType, }, }, + { + caseName: "with-annotations", + infra: newTestInfraWithAnnotations(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }), + }, + { + caseName: "override-annotations", + infra: newTestInfraWithAnnotationsAndLabels(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }, map[string]string{ + "label1": "value1", + "label2": "value2", + }), + service: &egv1a1.KubernetesServiceSpec{ + Annotations: map[string]string{ + "anno1": "value1-override", + }, + }, + }, } for _, tc := range cases { t.Run(tc.caseName, func(t *testing.T) { @@ -372,21 +523,38 @@ func loadService(caseName string) (*corev1.Service, error) { func TestConfigMap(t *testing.T) { cfg, err := config.New() require.NoError(t, err) + cases := []struct { + name string + infra *ir.Infra + }{ + { + name: "default", + infra: newTestInfra(), + }, { + name: "with-annotations", + infra: newTestInfraWithAnnotations(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }), + }, + } - infra := newTestInfra() - - r := NewResourceRender(cfg.Namespace, infra.GetProxyInfra()) - cm, err := r.ConfigMap() - require.NoError(t, err) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + r := NewResourceRender(cfg.Namespace, tc.infra.GetProxyInfra()) + cm, err := r.ConfigMap() + require.NoError(t, err) - expected, err := loadConfigmap() - require.NoError(t, err) + expected, err := loadConfigmap(tc.name) + require.NoError(t, err) - assert.Equal(t, expected, cm) + assert.Equal(t, expected, cm) + }) + } } -func loadConfigmap() (*corev1.ConfigMap, error) { - cmYAML, err := os.ReadFile("testdata/configmap.yaml") +func loadConfigmap(tc string) (*corev1.ConfigMap, error) { + cmYAML, err := os.ReadFile(fmt.Sprintf("testdata/configmap/%s.yaml", tc)) if err != nil { return nil, err } @@ -396,23 +564,41 @@ func loadConfigmap() (*corev1.ConfigMap, error) { } func TestServiceAccount(t *testing.T) { + cfg, err := config.New() require.NoError(t, err) + cases := []struct { + name string + infra *ir.Infra + }{ + { + name: "default", + infra: newTestInfra(), + }, { + name: "with-annotations", + infra: newTestInfraWithAnnotations(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }), + }, + } - infra := newTestInfra() - - r := NewResourceRender(cfg.Namespace, infra.GetProxyInfra()) - sa, err := r.ServiceAccount() - require.NoError(t, err) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + r := NewResourceRender(cfg.Namespace, tc.infra.GetProxyInfra()) + sa, err := r.ServiceAccount() + require.NoError(t, err) - expected, err := loadServiceAccount() - require.NoError(t, err) + expected, err := loadServiceAccount(tc.name) + require.NoError(t, err) - assert.Equal(t, expected, sa) + assert.Equal(t, expected, sa) + }) + } } -func loadServiceAccount() (*corev1.ServiceAccount, error) { - saYAML, err := os.ReadFile("testdata/serviceaccount.yaml") +func loadServiceAccount(tc string) (*corev1.ServiceAccount, error) { + saYAML, err := os.ReadFile(fmt.Sprintf("testdata/serviceaccount/%s.yaml", tc)) if err != nil { return nil, err } @@ -420,3 +606,85 @@ func loadServiceAccount() (*corev1.ServiceAccount, error) { _ = yaml.Unmarshal(saYAML, sa) return sa, nil } + +func TestHorizontalPodAutoscaler(t *testing.T) { + cfg, err := config.New() + require.NoError(t, err) + + cases := []struct { + caseName string + infra *ir.Infra + hpa *egv1a1.KubernetesHorizontalPodAutoscalerSpec + }{ + { + caseName: "default", + infra: newTestInfra(), + hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MaxReplicas: ptr.To[int32](1), + }, + }, + { + caseName: "custom", + infra: newTestInfra(), + hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](5), + MaxReplicas: ptr.To[int32](10), + Metrics: []autoscalingv2.MetricSpec{ + { + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: ptr.To[int32](60), + }, + }, + Type: autoscalingv2.ResourceMetricSourceType, + }, + { + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceMemory, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: ptr.To[int32](70), + }, + }, + Type: autoscalingv2.ResourceMetricSourceType, + }, + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.caseName, func(t *testing.T) { + provider := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider() + provider.Kubernetes = egv1a1.DefaultEnvoyProxyKubeProvider() + + if tc.hpa != nil { + provider.Kubernetes.EnvoyHpa = tc.hpa + } + + provider.GetEnvoyProxyKubeProvider() + + r := NewResourceRender(cfg.Namespace, tc.infra.GetProxyInfra()) + hpa, err := r.HorizontalPodAutoscaler() + require.NoError(t, err) + + want, err := loadHPA(tc.caseName) + require.NoError(t, err) + + assert.Equal(t, want, hpa) + }) + } +} + +func loadHPA(caseName string) (*autoscalingv2.HorizontalPodAutoscaler, error) { + hpaYAML, err := os.ReadFile(fmt.Sprintf("testdata/hpa/%s.yaml", caseName)) + if err != nil { + return nil, err + } + + hpa := &autoscalingv2.HorizontalPodAutoscaler{} + _ = yaml.Unmarshal(hpaYAML, hpa) + return hpa, nil +} diff --git a/internal/infrastructure/kubernetes/proxy/resource_test.go b/internal/infrastructure/kubernetes/proxy/resource_test.go index 065b04ed570..3cf71f2aea2 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_test.go @@ -9,8 +9,6 @@ import ( "testing" "github.com/stretchr/testify/require" - - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" ) func TestEnvoyPodSelector(t *testing.T) { @@ -39,78 +37,3 @@ func TestEnvoyPodSelector(t *testing.T) { }) } } - -func TestComponentLogLevel(t *testing.T) { - cases := []struct { - levels map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel - component egcfgv1a1.LogComponent - level egcfgv1a1.LogLevel - - expected egcfgv1a1.LogLevel - }{ - { - component: egcfgv1a1.LogComponentDefault, - level: egcfgv1a1.LogLevelInfo, - expected: egcfgv1a1.LogLevelInfo, - }, - { - component: egcfgv1a1.LogComponentDefault, - level: egcfgv1a1.LogLevelWarn, - expected: egcfgv1a1.LogLevelWarn, - }, - { - component: egcfgv1a1.LogComponentDefault, - levels: map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel{ - egcfgv1a1.LogComponentDefault: egcfgv1a1.LogLevelInfo, - }, - level: egcfgv1a1.LogLevelWarn, - expected: egcfgv1a1.LogLevelInfo, - }, - } - - for _, tc := range cases { - t.Run("", func(t *testing.T) { - got := componentLogLevel(tc.levels, tc.component, tc.level) - require.Equal(t, tc.expected, got) - }) - } -} - -func TestComponentLogLevelArgs(t *testing.T) { - cases := []struct { - levels map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel - expected string - }{ - { - expected: "", - }, - { - levels: map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel{ - egcfgv1a1.LogComponentDefault: egcfgv1a1.LogLevelInfo, - }, - expected: "", - }, - { - levels: map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel{ - egcfgv1a1.LogComponentDefault: egcfgv1a1.LogLevelInfo, - egcfgv1a1.LogComponentAdmin: egcfgv1a1.LogLevelWarn, - }, - expected: "admin:warn", - }, - { - levels: map[egcfgv1a1.LogComponent]egcfgv1a1.LogLevel{ - egcfgv1a1.LogComponentDefault: egcfgv1a1.LogLevelInfo, - egcfgv1a1.LogComponentAdmin: egcfgv1a1.LogLevelWarn, - egcfgv1a1.LogComponentFilter: egcfgv1a1.LogLevelDebug, - }, - expected: "admin:warn,filter:debug", - }, - } - - for _, tc := range cases { - t.Run("", func(t *testing.T) { - got := componentLogLevelArgs(tc.levels) - require.Equal(t, tc.expected, got) - }) - } -} diff --git a/internal/infrastructure/kubernetes/proxy/testdata/configmap.yaml b/internal/infrastructure/kubernetes/proxy/testdata/configmap.yaml deleted file mode 100644 index bd3d48db8fe..00000000000 --- a/internal/infrastructure/kubernetes/proxy/testdata/configmap.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - app.kubernetes.io/name: envoy - app.kubernetes.io/component: proxy - app.kubernetes.io/managed-by: envoy-gateway - gateway.envoyproxy.io/owning-gateway-name: default - gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 - namespace: envoy-gateway-system -data: - xds-certificate.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"/certs/tls.crt"},"private_key":{"filename":"/certs/tls.key"}}}]}' - xds-trusted-ca.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"/certs/ca.crt"},"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}' diff --git a/internal/infrastructure/kubernetes/proxy/testdata/configmap/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/configmap/default.yaml new file mode 100644 index 00000000000..536dec8b77c --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/configmap/default.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +data: + xds-certificate.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"/certs/tls.crt"},"private_key":{"filename":"/certs/tls.key"}}}]}' + xds-trusted-ca.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"/certs/ca.crt"},"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}' diff --git a/internal/infrastructure/kubernetes/proxy/testdata/configmap/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/configmap/with-annotations.yaml new file mode 100644 index 00000000000..4435e134a51 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/configmap/with-annotations.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +data: + xds-certificate.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"/certs/tls.crt"},"private_key":{"filename":"/certs/tls.key"}}}]}' + xds-trusted-ca.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"/certs/ca.crt"},"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}' diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml index cc54211cc28..3edbc78328a 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 1 @@ -55,10 +55,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: requests: @@ -84,7 +84,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 volumes: - name: certs @@ -98,7 +98,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml index 4837cd08371..a8532b8dbbb 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 1 @@ -56,10 +56,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: requests: @@ -85,7 +85,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 volumes: - name: certs @@ -99,7 +99,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml index 66fbefdd763..2a1fe754986 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 2 @@ -34,6 +34,7 @@ spec: prometheus.io/scrape: "true" spec: automountServiceAccountToken: false + hostNetwork: true containers: - args: - --service-cluster default @@ -96,8 +97,10 @@ spec: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -106,7 +109,10 @@ spec: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -128,14 +134,6 @@ spec: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 - --log-level warn - --cpuset-threads command: @@ -156,10 +154,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: limits: @@ -190,7 +188,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 securityContext: runAsUser: 1000 @@ -206,7 +204,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml new file mode 100644 index 00000000000..b8aa8192d25 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml @@ -0,0 +1,227 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + replicas: 3 + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + foo.bar: custom-label + template: + metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + foo.bar: custom-label + annotations: + prometheus.io/scrape: "true" + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:v1.2.3 + imagePullPolicy: IfNotPresent + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + readinessProbe: + httpGet: + path: /ready + port: 19001 + scheme: HTTP + timeoutSeconds: 1 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + privileged: true + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - mountPath: /lib/filter_foo.so + name: custom-libs + initContainers: + - name: install-filter-foo + image: alpine:3.11.3 + command: + - /bin/sh + - -c + args: + - echo "Installing filter-foo"; + wget -q https://example.com/download/filter_foo_v1.0.0.tgz -O - | tar -xz --directory=/lib filter_foo.so; + echo "Done"; + volumeMounts: + - mountPath: /lib + name: custom-libs + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 300 + securityContext: + runAsUser: 1000 + volumes: + - name: certs + secret: + secretName: envoy + defaultMode: 420 + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds + - name: custom-libs + emptyDir: {} + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml index 2fa18c4b54b..e6b1a8bd9cf 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 2 @@ -94,8 +94,10 @@ spec: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -104,7 +106,10 @@ spec: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -126,14 +131,6 @@ spec: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 - --log-level warn - --cpuset-threads command: @@ -154,10 +151,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: limits: @@ -188,7 +185,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 securityContext: runAsUser: 1000 @@ -204,7 +201,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml index 791bb365379..b735c371a13 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 1 @@ -92,8 +92,10 @@ spec: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -102,7 +104,10 @@ spec: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -124,14 +129,6 @@ spec: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 - --log-level warn - --cpuset-threads command: @@ -152,10 +149,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: requests: @@ -181,7 +178,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 volumes: - name: certs @@ -195,7 +192,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/enable-prometheus.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/enable-prometheus.yaml index 8100d6f3dbe..7732de9f065 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/enable-prometheus.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/enable-prometheus.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 1 @@ -118,8 +118,10 @@ spec: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -128,7 +130,10 @@ spec: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -150,14 +155,6 @@ spec: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 - --log-level warn - --cpuset-threads command: @@ -178,10 +175,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP - containerPort: 19001 name: metrics @@ -210,7 +207,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 volumes: - name: certs @@ -224,7 +221,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml index 9ea8502fc4f..172511c44ac 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 2 @@ -94,8 +94,10 @@ spec: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -104,7 +106,10 @@ spec: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -126,14 +131,6 @@ spec: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 - --log-level warn - --cpuset-threads command: @@ -158,10 +155,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: limits: @@ -192,7 +189,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 securityContext: runAsUser: 1000 @@ -208,7 +205,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml new file mode 100644 index 00000000000..df2bb0e6a46 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml @@ -0,0 +1,211 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1 + label2: value2 + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1-override + label2: value2 + template: + metadata: + annotations: + anno1: value1-override + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1-override + label2: value2 + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy-dev:latest + imagePullPolicy: IfNotPresent + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + resources: + requests: + cpu: 100m + memory: 512Mi + readinessProbe: + httpGet: + path: /ready + port: 19001 + scheme: HTTP + timeoutSeconds: 1 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 300 + volumes: + - name: certs + secret: + secretName: envoy + defaultMode: 420 + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml index 56fdae98684..bdebfe89faf 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml @@ -7,7 +7,7 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: replicas: 2 @@ -94,8 +94,10 @@ spec: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -104,7 +106,10 @@ spec: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -126,14 +131,6 @@ spec: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 - layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 - --log-level warn - --cpuset-threads command: @@ -158,10 +155,10 @@ spec: name: envoy ports: - containerPort: 8080 - name: EnvoyHTTPPort + name: EnvoyH-d76a15e2 protocol: TCP - containerPort: 8443 - name: EnvoyHTTPSPort + name: EnvoyH-6658f727 protocol: TCP resources: limits: @@ -192,7 +189,7 @@ spec: dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - serviceAccountName: envoy-default-64656661 + serviceAccountName: envoy-default-37a8eec1 terminationGracePeriodSeconds: 300 securityContext: runAsUser: 1000 @@ -208,7 +205,7 @@ spec: path: xds-trusted-ca.json - key: xds-certificate.json path: xds-certificate.json - name: envoy-default-64656661 + name: envoy-default-37a8eec1 optional: false name: sds revisionHistoryLimit: 10 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml new file mode 100644 index 00000000000..6b0367843de --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml @@ -0,0 +1,205 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + template: + metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy-dev:latest + imagePullPolicy: IfNotPresent + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + resources: + requests: + cpu: 100m + memory: 512Mi + readinessProbe: + httpGet: + path: /ready + port: 19001 + scheme: HTTP + timeoutSeconds: 1 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 300 + volumes: + - name: certs + secret: + secretName: envoy + defaultMode: 420 + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml new file mode 100644 index 00000000000..3e7f505702d --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml @@ -0,0 +1,106 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + template: + metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - --config-yaml test bootstrap config + - --log-level warn + - --cpuset-threads + - --concurrency 4 + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy-dev:latest + imagePullPolicy: IfNotPresent + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + resources: + requests: + cpu: 100m + memory: 512Mi + readinessProbe: + httpGet: + path: /ready + port: 19001 + scheme: HTTP + timeoutSeconds: 1 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 300 + volumes: + - name: certs + secret: + secretName: envoy + defaultMode: 420 + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/hpa/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/hpa/custom.yaml new file mode 100644 index 00000000000..6827dec9966 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/hpa/custom.yaml @@ -0,0 +1,28 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + maxReplicas: 10 + metrics: + - resource: + name: cpu + target: + averageUtilization: 60 + type: Utilization + type: Resource + - resource: + name: memory + target: + averageUtilization: 70 + type: Utilization + type: Resource + minReplicas: 5 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: envoy-default-37a8eec1 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/hpa/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/hpa/default.yaml new file mode 100644 index 00000000000..4c5446c2d35 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/hpa/default.yaml @@ -0,0 +1,21 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + metrics: + - resource: + name: cpu + target: + averageUtilization: 80 + type: Utilization + type: Resource + maxReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: envoy-default-37a8eec1 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/hpa/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/hpa/with-annotations.yaml new file mode 100644 index 00000000000..9fa28d6ca6a --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/hpa/with-annotations.yaml @@ -0,0 +1,24 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + metrics: + - resource: + name: cpu + target: + averageUtilization: 80 + type: Utilization + type: Resource + maxReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: envoy-default-37a8eec1 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount.yaml b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount.yaml deleted file mode 100644 index 617dc090f7f..00000000000 --- a/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/name: envoy - app.kubernetes.io/component: proxy - app.kubernetes.io/managed-by: envoy-gateway - gateway.envoyproxy.io/owning-gateway-name: default - gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 - namespace: envoy-gateway-system diff --git a/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/default.yaml new file mode 100644 index 00000000000..4e2731766af --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/default.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system diff --git a/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/with-annotations.yaml new file mode 100644 index 00000000000..f63c97451ca --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/with-annotations.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml index 334b6a0e46d..4139ac4f6b1 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml @@ -9,15 +9,15 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: ports: - - name: EnvoyHTTPPort + - name: envoy-EnvoyHTTPPort-d76a15e2 port: 0 protocol: TCP targetPort: 8080 - - name: EnvoyHTTPSPort + - name: envoy-EnvoyHTTPSPort-6658f727 port: 0 protocol: TCP targetPort: 8443 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/default.yaml index f780f0198e2..6efc4ee4aaf 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/services/default.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/default.yaml @@ -7,16 +7,16 @@ metadata: app.kubernetes.io/managed-by: envoy-gateway gateway.envoyproxy.io/owning-gateway-name: default gateway.envoyproxy.io/owning-gateway-namespace: default - name: envoy-default-64656661 + name: envoy-default-37a8eec1 namespace: envoy-gateway-system spec: externalTrafficPolicy: Local ports: - - name: EnvoyHTTPPort + - name: envoy-EnvoyHTTPPort-d76a15e2 port: 0 protocol: TCP targetPort: 8080 - - name: EnvoyHTTPSPort + - name: envoy-EnvoyHTTPSPort-6658f727 port: 0 protocol: TCP targetPort: 8443 diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/override-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/override-annotations.yaml new file mode 100644 index 00000000000..8953b5d9590 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/override-annotations.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + anno1: value1-override + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1 + label2: value2 + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + externalTrafficPolicy: Local + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1 + label2: value2 + sessionAffinity: None + type: LoadBalancer diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/with-annotations.yaml new file mode 100644 index 00000000000..75635bc5fcd --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/with-annotations.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + externalTrafficPolicy: Local + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + sessionAffinity: None + type: LoadBalancer diff --git a/internal/infrastructure/kubernetes/proxy_configmap_test.go b/internal/infrastructure/kubernetes/proxy_configmap_test.go index b1c4de73919..2d99048a6d5 100644 --- a/internal/infrastructure/kubernetes/proxy_configmap_test.go +++ b/internal/infrastructure/kubernetes/proxy_configmap_test.go @@ -43,7 +43,7 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) { expect: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: cfg.Namespace, - Name: "envoy-test-74657374", + Name: "envoy-test-9f86d081", Labels: map[string]string{ "app.kubernetes.io/name": "envoy", "app.kubernetes.io/component": "proxy", @@ -77,7 +77,7 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) { expect: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: cfg.Namespace, - Name: "envoy-test-74657374", + Name: "envoy-test-9f86d081", Labels: map[string]string{ "app.kubernetes.io/name": "envoy", "app.kubernetes.io/component": "proxy", diff --git a/internal/infrastructure/kubernetes/proxy_deployment_test.go b/internal/infrastructure/kubernetes/proxy_deployment_test.go index b4f89cc9418..5dda2a8be7a 100644 --- a/internal/infrastructure/kubernetes/proxy_deployment_test.go +++ b/internal/infrastructure/kubernetes/proxy_deployment_test.go @@ -12,11 +12,11 @@ import ( "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -78,14 +78,14 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) { gatewayapi.OwningGatewayNameLabel: infra.Proxy.Name, }, }, - Config: &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Provider: &egcfgv1a1.EnvoyProxyProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyProxyKubernetesProvider{ - EnvoyDeployment: &egcfgv1a1.KubernetesDeploymentSpec{ - Container: &egcfgv1a1.KubernetesContainerSpec{ - Image: pointer.String("envoyproxy/envoy-dev:v1.2.3"), + Config: &egv1a1.EnvoyProxy{ + Spec: egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("envoyproxy/envoy-dev:v1.2.3"), }, }, }, diff --git a/internal/infrastructure/kubernetes/proxy_infra_test.go b/internal/infrastructure/kubernetes/proxy_infra_test.go index d3180439d69..9d3d2fe98ea 100644 --- a/internal/infrastructure/kubernetes/proxy_infra_test.go +++ b/internal/infrastructure/kubernetes/proxy_infra_test.go @@ -17,9 +17,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -47,16 +47,16 @@ func newTestInfraWithClient(t *testing.T, cli client.Client) *Infra { cfg, err := config.New() require.NoError(t, err) - cfg.EnvoyGateway = &egcfgv1a1.EnvoyGateway{ + cfg.EnvoyGateway = &egv1a1.EnvoyGateway{ TypeMeta: metav1.TypeMeta{}, - EnvoyGatewaySpec: egcfgv1a1.EnvoyGatewaySpec{ - RateLimit: &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + EnvoyGatewaySpec: egv1a1.EnvoyGatewaySpec{ + RateLimit: &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "", - TLS: &egcfgv1a1.RedisTLSSettings{ - CertificateRef: &gwapiv1b1.SecretObjectReference{ + TLS: &egv1a1.RedisTLSSettings{ + CertificateRef: &gwapiv1.SecretObjectReference{ Name: "ratelimit-cert", }, }, diff --git a/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go b/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go index 2b13e36276a..dbba0492d43 100644 --- a/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go +++ b/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go @@ -54,7 +54,7 @@ func TestCreateOrUpdateProxyServiceAccount(t *testing.T) { }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", - Name: "envoy-test-74657374", + Name: "envoy-test-9f86d081", Labels: map[string]string{ "app.kubernetes.io/name": "envoy", "app.kubernetes.io/component": "proxy", @@ -103,7 +103,7 @@ func TestCreateOrUpdateProxyServiceAccount(t *testing.T) { }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", - Name: "envoy-test-74657374", + Name: "envoy-test-9f86d081", Labels: map[string]string{ "app.kubernetes.io/name": "envoy", "app.kubernetes.io/component": "proxy", @@ -152,7 +152,7 @@ func TestCreateOrUpdateProxyServiceAccount(t *testing.T) { }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", - Name: "envoy-very-long-name-that-will-be-hashed-and-cut-off-b-76657279", + Name: "envoy-very-long-name-that-will-be-hashed-and-cut-off-b-5bacc75e", Labels: map[string]string{ "app.kubernetes.io/name": "envoy", "app.kubernetes.io/component": "proxy", diff --git a/internal/infrastructure/kubernetes/ratelimit/resource.go b/internal/infrastructure/kubernetes/ratelimit/resource.go index e7a39f8383c..95631e41ab8 100644 --- a/internal/infrastructure/kubernetes/ratelimit/resource.go +++ b/internal/infrastructure/kubernetes/ratelimit/resource.go @@ -12,10 +12,11 @@ import ( "strconv" corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" "github.com/envoyproxy/gateway/internal/kubernetes" ) @@ -25,8 +26,8 @@ const ( RedisSocketTypeEnvVar = "REDIS_SOCKET_TYPE" // RedisURLEnvVar is the redis url. RedisURLEnvVar = "REDIS_URL" - // RedisTLS is the redis tls. - RedisTLS = "REDIS_TLS" + // RedisTLSEnvVar is the redis tls. + RedisTLSEnvVar = "REDIS_TLS" // RedisTLSClientCertEnvVar is the redis tls client cert. RedisTLSClientCertEnvVar = "REDIS_TLS_CLIENT_CERT" // RedisTLSClientCertFilename is the redis tls client cert file. @@ -69,21 +70,27 @@ const ( LogLevelEnvVar = "LOG_LEVEL" // UseStatsdEnvVar is the use statsd. UseStatsdEnvVar = "USE_STATSD" + // ForceStartWithoutInitialConfigEnvVar enables start the ratelimit server without initial config. + ForceStartWithoutInitialConfigEnvVar = "FORCE_START_WITHOUT_INITIAL_CONFIG" + // ConfigTypeEnvVar is the configuration loading method for ratelimit. + ConfigTypeEnvVar = "CONFIG_TYPE" + // ConfigGrpcXdsServerURLEnvVar is the url of ratelimit config xds server. + ConfigGrpcXdsServerURLEnvVar = "CONFIG_GRPC_XDS_SERVER_URL" + // ConfigGrpcXdsNodeIDEnvVar is the id of ratelimit node. + ConfigGrpcXdsNodeIDEnvVar = "CONFIG_GRPC_XDS_NODE_ID" + // InfraName is the name for rate-limit resources. InfraName = "envoy-ratelimit" // InfraGRPCPort is the grpc port that the rate limit service listens on. InfraGRPCPort = 8081 - // ConfigType is the configuration loading method for ratelimit. - ConfigType = "CONFIG_TYPE" - // ConfigGrpcXdsServerURL is the url of ratelimit config xds server. - ConfigGrpcXdsServerURL = "CONFIG_GRPC_XDS_SERVER_URL" - // ConfigGrpcXdsNodeID is the id of ratelimit node. - ConfigGrpcXdsNodeID = "CONFIG_GRPC_XDS_NODE_ID" - // XdsGrpcSotwConfigServerPort is the listening port of the ratelimit xDS config server. XdsGrpcSotwConfigServerPort = 18001 // XdsGrpcSotwConfigServerHost is the hostname of the ratelimit xDS config server. XdsGrpcSotwConfigServerHost = "envoy-gateway" + // ReadinessPath is readiness path for readiness probe. + ReadinessPath = "/healthcheck" + // ReadinessPort is readiness port for readiness probe. + ReadinessPort = 8080 ) // GetServiceURL returns the URL for the rate limit service. @@ -101,10 +108,10 @@ func rateLimitLabels() map[string]string { } // expectedRateLimitContainers returns expected rateLimit containers. -func expectedRateLimitContainers(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployment *egcfgv1a1.KubernetesDeploymentSpec) []corev1.Container { +func expectedRateLimitContainers(rateLimit *egv1a1.RateLimit, rateLimitDeployment *egv1a1.KubernetesDeploymentSpec) []corev1.Container { ports := []corev1.ContainerPort{ { - Name: "http", + Name: "grpc", ContainerPort: InfraGRPCPort, Protocol: corev1.ProtocolTCP, }, @@ -125,6 +132,19 @@ func expectedRateLimitContainers(rateLimit *egcfgv1a1.RateLimit, rateLimitDeploy VolumeMounts: expectedContainerVolumeMounts(rateLimit, rateLimitDeployment), TerminationMessagePolicy: corev1.TerminationMessageReadFile, TerminationMessagePath: "/dev/termination-log", + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: ReadinessPath, + Port: intstr.IntOrString{Type: intstr.Int, IntVal: ReadinessPort}, + Scheme: corev1.URISchemeHTTP, + }, + }, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, }, } @@ -132,7 +152,7 @@ func expectedRateLimitContainers(rateLimit *egcfgv1a1.RateLimit, rateLimitDeploy } // expectedContainerVolumeMounts returns expected rateLimit container volume mounts. -func expectedContainerVolumeMounts(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployment *egcfgv1a1.KubernetesDeploymentSpec) []corev1.VolumeMount { +func expectedContainerVolumeMounts(rateLimit *egv1a1.RateLimit, rateLimitDeployment *egv1a1.KubernetesDeploymentSpec) []corev1.VolumeMount { var volumeMounts []corev1.VolumeMount // mount the cert @@ -154,7 +174,7 @@ func expectedContainerVolumeMounts(rateLimit *egcfgv1a1.RateLimit, rateLimitDepl } // expectedDeploymentVolumes returns expected rateLimit deployment volumes. -func expectedDeploymentVolumes(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployment *egcfgv1a1.KubernetesDeploymentSpec) []corev1.Volume { +func expectedDeploymentVolumes(rateLimit *egv1a1.RateLimit, rateLimitDeployment *egv1a1.KubernetesDeploymentSpec) []corev1.Volume { var volumes []corev1.Volume if rateLimit.Backend.Redis.TLS != nil && rateLimit.Backend.Redis.TLS.CertificateRef != nil { @@ -163,7 +183,7 @@ func expectedDeploymentVolumes(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployme VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: string(rateLimit.Backend.Redis.TLS.CertificateRef.Name), - DefaultMode: pointer.Int32(420), + DefaultMode: ptr.To[int32](420), }, }, }) @@ -174,7 +194,7 @@ func expectedDeploymentVolumes(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployme VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: "envoy-rate-limit", - DefaultMode: pointer.Int32(420), + DefaultMode: ptr.To[int32](420), }, }, }) @@ -183,7 +203,7 @@ func expectedDeploymentVolumes(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployme } // expectedRateLimitContainerEnv returns expected rateLimit container envs. -func expectedRateLimitContainerEnv(rateLimit *egcfgv1a1.RateLimit, rateLimitDeployment *egcfgv1a1.KubernetesDeploymentSpec) []corev1.EnvVar { +func expectedRateLimitContainerEnv(rateLimit *egv1a1.RateLimit, rateLimitDeployment *egv1a1.KubernetesDeploymentSpec) []corev1.EnvVar { env := []corev1.EnvVar{ { Name: RedisSocketTypeEnvVar, @@ -218,15 +238,15 @@ func expectedRateLimitContainerEnv(rateLimit *egcfgv1a1.RateLimit, rateLimitDepl Value: "false", }, { - Name: ConfigType, + Name: ConfigTypeEnvVar, Value: "GRPC_XDS_SOTW", }, { - Name: ConfigGrpcXdsServerURL, + Name: ConfigGrpcXdsServerURLEnvVar, Value: net.JoinHostPort(XdsGrpcSotwConfigServerHost, strconv.Itoa(XdsGrpcSotwConfigServerPort)), }, { - Name: ConfigGrpcXdsNodeID, + Name: ConfigGrpcXdsNodeIDEnvVar, Value: InfraName, }, { @@ -261,11 +281,15 @@ func expectedRateLimitContainerEnv(rateLimit *egcfgv1a1.RateLimit, rateLimitDepl Name: ConfigGRPCXDSServerTLSCACertEnvVar, Value: GRPCTLSCACertFilename, }, + { + Name: ForceStartWithoutInitialConfigEnvVar, + Value: "true", + }, } if rateLimit.Backend.Redis.TLS != nil { env = append(env, corev1.EnvVar{ - Name: RedisTLS, + Name: RedisTLSEnvVar, Value: "true", }) @@ -287,7 +311,7 @@ func expectedRateLimitContainerEnv(rateLimit *egcfgv1a1.RateLimit, rateLimitDepl } // Validate the ratelimit tls secret validating. -func Validate(ctx context.Context, client client.Client, gateway *egcfgv1a1.EnvoyGateway, namespace string) error { +func Validate(ctx context.Context, client client.Client, gateway *egv1a1.EnvoyGateway, namespace string) error { if gateway.RateLimit.Backend.Redis.TLS != nil && gateway.RateLimit.Backend.Redis.TLS.CertificateRef != nil { certificateRef := gateway.RateLimit.Backend.Redis.TLS.CertificateRef _, _, err := kubernetes.ValidateSecretObjectReference(ctx, client, certificateRef, namespace) diff --git a/internal/infrastructure/kubernetes/ratelimit/resource_provider.go b/internal/infrastructure/kubernetes/ratelimit/resource_provider.go index ba7505fc065..244b4f6b5bc 100644 --- a/internal/infrastructure/kubernetes/ratelimit/resource_provider.go +++ b/internal/infrastructure/kubernetes/ratelimit/resource_provider.go @@ -7,13 +7,14 @@ package ratelimit import ( appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" ) @@ -29,15 +30,15 @@ type ResourceRender struct { // Namespace is the Namespace used for managed infra. Namespace string - rateLimit *egcfgv1a1.RateLimit - rateLimitDeployment *egcfgv1a1.KubernetesDeploymentSpec + rateLimit *egv1a1.RateLimit + rateLimitDeployment *egv1a1.KubernetesDeploymentSpec // ownerReferenceUID store the uid of its owner reference. ownerReferenceUID map[string]types.UID } // NewResourceRender returns a new ResourceRender. -func NewResourceRender(ns string, gateway *egcfgv1a1.EnvoyGateway, ownerReferenceUID map[string]types.UID) *ResourceRender { +func NewResourceRender(ns string, gateway *egv1a1.EnvoyGateway, ownerReferenceUID map[string]types.UID) *ResourceRender { return &ResourceRender{ Namespace: ns, rateLimit: gateway.RateLimit, @@ -69,8 +70,10 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { } labels := rateLimitLabels() - - serviceSpec := resource.ExpectedServiceSpec(egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceTypeClusterIP)) + kubernetesServiceSpec := &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeClusterIP), + } + serviceSpec := resource.ExpectedServiceSpec(kubernetesServiceSpec) serviceSpec.Ports = ports serviceSpec.Selector = resource.GetSelector(labels).MatchLabels @@ -169,19 +172,20 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { Spec: corev1.PodSpec{ Containers: containers, ServiceAccountName: InfraName, - AutomountServiceAccountToken: pointer.Bool(false), - TerminationGracePeriodSeconds: pointer.Int64(int64(300)), + AutomountServiceAccountToken: ptr.To(false), + TerminationGracePeriodSeconds: ptr.To[int64](300), DNSPolicy: corev1.DNSClusterFirst, RestartPolicy: corev1.RestartPolicyAlways, SchedulerName: "default-scheduler", SecurityContext: r.rateLimitDeployment.Pod.SecurityContext, + HostNetwork: r.rateLimitDeployment.Pod.HostNetwork, Volumes: expectedDeploymentVolumes(r.rateLimit, r.rateLimitDeployment), Affinity: r.rateLimitDeployment.Pod.Affinity, Tolerations: r.rateLimitDeployment.Pod.Tolerations, }, }, - RevisionHistoryLimit: pointer.Int32(10), - ProgressDeadlineSeconds: pointer.Int32(600), + RevisionHistoryLimit: ptr.To[int32](10), + ProgressDeadlineSeconds: ptr.To[int32](600), }, } @@ -200,3 +204,7 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { return deployment, nil } + +func (r *ResourceRender) HorizontalPodAutoscaler() (*autoscalingv2.HorizontalPodAutoscaler, error) { + return nil, nil +} diff --git a/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go b/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go index 81580b1d205..d3cb1f8bc2c 100644 --- a/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go @@ -16,11 +16,11 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/yaml" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" ) @@ -63,10 +63,10 @@ func TestServiceAccount(t *testing.T) { cfg, err := config.New() require.NoError(t, err) - cfg.EnvoyGateway.RateLimit = &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + cfg.EnvoyGateway.RateLimit = &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, @@ -96,10 +96,10 @@ func TestService(t *testing.T) { cfg, err := config.New() require.NoError(t, err) - cfg.EnvoyGateway.RateLimit = &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + cfg.EnvoyGateway.RateLimit = &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, @@ -127,18 +127,18 @@ func loadService() (*corev1.Service, error) { func TestDeployment(t *testing.T) { cfg, err := config.New() require.NoError(t, err) - rateLimit := &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rateLimit := &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, } cases := []struct { caseName string - rateLimit *egcfgv1a1.RateLimit - deploy *egcfgv1a1.KubernetesDeploymentSpec + rateLimit *egv1a1.RateLimit + deploy *egv1a1.KubernetesDeploymentSpec }{ { caseName: "default", @@ -148,19 +148,20 @@ func TestDeployment(t *testing.T) { { caseName: "custom", rateLimit: rateLimit, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, + HostNetwork: true, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ - Image: pointer.String("custom-image"), + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -172,7 +173,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -180,18 +181,18 @@ func TestDeployment(t *testing.T) { { caseName: "extension-env", rateLimit: rateLimit, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: "env_a", @@ -202,7 +203,7 @@ func TestDeployment(t *testing.T) { Value: "env_b_value", }, }, - Image: pointer.String("custom-image"), + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -214,7 +215,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -222,20 +223,20 @@ func TestDeployment(t *testing.T) { { caseName: "default-env", rateLimit: rateLimit, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: nil, - Image: pointer.String("custom-image"), + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -247,7 +248,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, @@ -255,25 +256,25 @@ func TestDeployment(t *testing.T) { { caseName: "override-env", rateLimit: rateLimit, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: UseStatsdEnvVar, Value: "true", }, }, - Image: pointer.String("custom-image"), + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -285,38 +286,38 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, }, { caseName: "redis-tls-settings", - rateLimit: &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rateLimit: &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", - TLS: &egcfgv1a1.RedisTLSSettings{ - CertificateRef: &gwapiv1b1.SecretObjectReference{ + TLS: &egv1a1.RedisTLSSettings{ + CertificateRef: &gwapiv1.SecretObjectReference{ Name: "ratelimit-cert", }, }, }, }, }, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: RedisAuthEnvVar, @@ -327,7 +328,7 @@ func TestDeployment(t *testing.T) { Value: "true", }, }, - Image: pointer.String("custom-image"), + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -339,35 +340,35 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, }, { caseName: "tolerations", - rateLimit: &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rateLimit: &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", - TLS: &egcfgv1a1.RedisTLSSettings{ - CertificateRef: &gwapiv1b1.SecretObjectReference{ + TLS: &egv1a1.RedisTLSSettings{ + CertificateRef: &gwapiv1.SecretObjectReference{ Name: "ratelimit-cert", }, }, }, }, }, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, Tolerations: []corev1.Toleration{ { @@ -378,7 +379,7 @@ func TestDeployment(t *testing.T) { }, }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: RedisAuthEnvVar, @@ -389,7 +390,7 @@ func TestDeployment(t *testing.T) { Value: "true", }, }, - Image: pointer.String("custom-image"), + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -401,35 +402,35 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, }, }, }, { caseName: "volumes", - rateLimit: &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rateLimit: &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", - TLS: &egcfgv1a1.RedisTLSSettings{ - CertificateRef: &gwapiv1b1.SecretObjectReference{ + TLS: &egv1a1.RedisTLSSettings{ + CertificateRef: &gwapiv1.SecretObjectReference{ Name: "ratelimit-cert-origin", }, }, }, }, }, - deploy: &egcfgv1a1.KubernetesDeploymentSpec{ - Replicas: pointer.Int32(2), - Strategy: egcfgv1a1.DefaultKubernetesDeploymentStrategy(), - Pod: &egcfgv1a1.KubernetesPodSpec{ + deploy: &egv1a1.KubernetesDeploymentSpec{ + Replicas: ptr.To[int32](2), + Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), + Pod: &egv1a1.KubernetesPodSpec{ Annotations: map[string]string{ "prometheus.io/scrape": "true", }, SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: pointer.Int64(1000), + RunAsUser: ptr.To[int64](1000), }, Tolerations: []corev1.Toleration{ { @@ -445,13 +446,13 @@ func TestDeployment(t *testing.T) { VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: "custom-cert", - DefaultMode: pointer.Int32(420), + DefaultMode: ptr.To[int32](420), }, }, }, }, }, - Container: &egcfgv1a1.KubernetesContainerSpec{ + Container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: RedisAuthEnvVar, @@ -462,7 +463,7 @@ func TestDeployment(t *testing.T) { Value: "true", }, }, - Image: pointer.String("custom-image"), + Image: ptr.To("custom-image"), Resources: &corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("400m"), @@ -474,7 +475,7 @@ func TestDeployment(t *testing.T) { }, }, SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.Bool(true), + Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{}, }, @@ -485,9 +486,9 @@ func TestDeployment(t *testing.T) { t.Run(tc.caseName, func(t *testing.T) { cfg.EnvoyGateway.RateLimit = tc.rateLimit - cfg.EnvoyGateway.Provider = &egcfgv1a1.EnvoyGatewayProvider{ - Type: egcfgv1a1.ProviderTypeKubernetes, - Kubernetes: &egcfgv1a1.EnvoyGatewayKubernetesProvider{ + cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{ RateLimitDeployment: tc.deploy, }} r := NewResourceRender(cfg.Namespace, cfg.EnvoyGateway, ownerReferenceUID) diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/affinity.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/affinity.yaml index ba83d0237dc..fa2e11f2a2e 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/affinity.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/affinity.yaml @@ -73,12 +73,14 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" image: custom-image imagePullPolicy: IfNotPresent name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -95,6 +97,15 @@ spec: - mountPath: /certs name: certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/custom.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/custom.yaml index d3c0f29dc48..c922b53f519 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/custom.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/custom.yaml @@ -31,6 +31,7 @@ spec: prometheus.io/scrape: "true" spec: automountServiceAccountToken: false + hostNetwork: true containers: - command: - /bin/ratelimit @@ -73,12 +74,14 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" image: custom-image imagePullPolicy: IfNotPresent name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -95,6 +98,15 @@ spec: - mountPath: /certs name: certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default-env.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default-env.yaml index d3c0f29dc48..b34ab0fe254 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default-env.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default-env.yaml @@ -73,12 +73,14 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" image: custom-image imagePullPolicy: IfNotPresent name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -95,6 +97,15 @@ spec: - mountPath: /certs name: certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml index bfa28eb3186..a9451dae13c 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml @@ -71,12 +71,14 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" image: envoyproxy/ratelimit:master imagePullPolicy: IfNotPresent name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP resources: requests: @@ -88,6 +90,15 @@ spec: - mountPath: /certs name: certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/extension-env.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/extension-env.yaml index 4b6457ebdda..59c0f8089ca 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/extension-env.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/extension-env.yaml @@ -73,6 +73,8 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" - name: env_a value: env_a_value - name: env_b @@ -82,7 +84,7 @@ spec: name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -99,6 +101,15 @@ spec: - mountPath: /certs name: certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/override-env.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/override-env.yaml index 6371fea87a9..a87dd79bd9c 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/override-env.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/override-env.yaml @@ -73,12 +73,14 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" image: custom-image imagePullPolicy: IfNotPresent name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -95,6 +97,15 @@ spec: - mountPath: /certs name: certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/redis-tls-settings.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/redis-tls-settings.yaml index dacf0988f59..136101fd9bd 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/redis-tls-settings.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/redis-tls-settings.yaml @@ -73,6 +73,8 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" - name: REDIS_TLS value: "true" - name: REDIS_TLS_CLIENT_CERT @@ -86,7 +88,7 @@ spec: name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -106,6 +108,15 @@ spec: - mountPath: /redis-certs name: redis-certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/tolerations.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/tolerations.yaml index 23b2791b8e0..eac44ebd0e4 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/tolerations.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/tolerations.yaml @@ -73,6 +73,8 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" - name: REDIS_TLS value: "true" - name: REDIS_TLS_CLIENT_CERT @@ -86,7 +88,7 @@ spec: name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -106,6 +108,15 @@ spec: - mountPath: /redis-certs name: redis-certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/volumes.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/volumes.yaml index 0fba367bdb8..1ba74b57c86 100644 --- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/volumes.yaml +++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/volumes.yaml @@ -73,6 +73,8 @@ spec: value: "/certs/tls.key" - name: CONFIG_GRPC_XDS_SERVER_TLS_CACERT value: "/certs/ca.crt" + - name: FORCE_START_WITHOUT_INITIAL_CONFIG + value: "true" - name: REDIS_TLS value: "true" - name: REDIS_TLS_CLIENT_CERT @@ -86,7 +88,7 @@ spec: name: envoy-ratelimit ports: - containerPort: 8081 - name: http + name: grpc protocol: TCP securityContext: privileged: true @@ -106,6 +108,15 @@ spec: - mountPath: /redis-certs name: redis-certs readOnly: true + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 8080 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler diff --git a/internal/infrastructure/kubernetes/ratelimit_deployment_test.go b/internal/infrastructure/kubernetes/ratelimit_deployment_test.go index c07e0df981b..bfb444880f6 100644 --- a/internal/infrastructure/kubernetes/ratelimit_deployment_test.go +++ b/internal/infrastructure/kubernetes/ratelimit_deployment_test.go @@ -16,7 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" @@ -26,10 +26,10 @@ func TestCreateOrUpdateRateLimitDeployment(t *testing.T) { cfg, err := config.New() require.NoError(t, err) - cfg.EnvoyGateway.RateLimit = &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + cfg.EnvoyGateway.RateLimit = &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, @@ -59,7 +59,7 @@ func TestCreateOrUpdateRateLimitDeployment(t *testing.T) { { name: "update ratelimit deployment image", current: deployment, - want: deploymentWithImage(deployment, egcfgv1a1.DefaultRateLimitImage), + want: deploymentWithImage(deployment, egv1a1.DefaultRateLimitImage), }, } @@ -93,10 +93,10 @@ func TestCreateOrUpdateRateLimitDeployment(t *testing.T) { } func TestDeleteRateLimitDeployment(t *testing.T) { - rl := &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rl := &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, diff --git a/internal/infrastructure/kubernetes/ratelimit_service_test.go b/internal/infrastructure/kubernetes/ratelimit_service_test.go index 32d447d067f..d117d9d57bf 100644 --- a/internal/infrastructure/kubernetes/ratelimit_service_test.go +++ b/internal/infrastructure/kubernetes/ratelimit_service_test.go @@ -11,15 +11,15 @@ import ( "github.com/stretchr/testify/require" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" ) func TestDeleteRateLimitService(t *testing.T) { - rl := &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rl := &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, diff --git a/internal/infrastructure/kubernetes/ratelimit_serviceaccount_test.go b/internal/infrastructure/kubernetes/ratelimit_serviceaccount_test.go index 560a14f32ca..1ddf5cd4d8a 100644 --- a/internal/infrastructure/kubernetes/ratelimit_serviceaccount_test.go +++ b/internal/infrastructure/kubernetes/ratelimit_serviceaccount_test.go @@ -19,17 +19,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" ) func TestCreateOrUpdateRateLimitServiceAccount(t *testing.T) { - rl := &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rl := &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, @@ -127,10 +127,10 @@ func TestCreateOrUpdateRateLimitServiceAccount(t *testing.T) { } func TestDeleteRateLimitServiceAccount(t *testing.T) { - rl := &egcfgv1a1.RateLimit{ - Backend: egcfgv1a1.RateLimitDatabaseBackend{ - Type: egcfgv1a1.RedisBackendType, - Redis: &egcfgv1a1.RateLimitRedisSettings{ + rl := &egv1a1.RateLimit{ + Backend: egv1a1.RateLimitDatabaseBackend{ + Type: egv1a1.RedisBackendType, + Redis: &egv1a1.RateLimitRedisSettings{ URL: "redis.redis.svc:6379", }, }, diff --git a/internal/infrastructure/kubernetes/resource/resource.go b/internal/infrastructure/kubernetes/resource/resource.go index 96fbf2a5b4b..b001334898e 100644 --- a/internal/infrastructure/kubernetes/resource/resource.go +++ b/internal/infrastructure/kubernetes/resource/resource.go @@ -11,7 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) // GetSelector returns a label selector used to select resources @@ -23,11 +23,20 @@ func GetSelector(labels map[string]string) *metav1.LabelSelector { } // ExpectedServiceSpec returns service spec. -func ExpectedServiceSpec(serviceType *egcfgv1a1.ServiceType) corev1.ServiceSpec { +func ExpectedServiceSpec(service *egv1a1.KubernetesServiceSpec) corev1.ServiceSpec { serviceSpec := corev1.ServiceSpec{} - serviceSpec.Type = corev1.ServiceType(*serviceType) + serviceSpec.Type = corev1.ServiceType(*service.Type) serviceSpec.SessionAffinity = corev1.ServiceAffinityNone - if *serviceType == egcfgv1a1.ServiceTypeLoadBalancer { + if *service.Type == egv1a1.ServiceTypeLoadBalancer { + if service.LoadBalancerClass != nil { + serviceSpec.LoadBalancerClass = service.LoadBalancerClass + } + if service.AllocateLoadBalancerNodePorts != nil { + serviceSpec.AllocateLoadBalancerNodePorts = service.AllocateLoadBalancerNodePorts + } + if service.LoadBalancerIP != nil { + serviceSpec.LoadBalancerIP = *service.LoadBalancerIP + } // Preserve the client source IP and avoid a second hop for LoadBalancer. serviceSpec.ExternalTrafficPolicy = corev1.ServiceExternalTrafficPolicyTypeLocal } @@ -42,7 +51,7 @@ func CompareSvc(currentSvc, originalSvc *corev1.Service) bool { } // ExpectedProxyContainerEnv returns expected container envs. -func ExpectedProxyContainerEnv(container *egcfgv1a1.KubernetesContainerSpec, env []corev1.EnvVar) []corev1.EnvVar { +func ExpectedProxyContainerEnv(container *egv1a1.KubernetesContainerSpec, env []corev1.EnvVar) []corev1.EnvVar { amendFunc := func(envVar corev1.EnvVar) { for index, e := range env { if e.Name == envVar.Name { @@ -61,7 +70,7 @@ func ExpectedProxyContainerEnv(container *egcfgv1a1.KubernetesContainerSpec, env } // ExpectedDeploymentVolumes returns expected deployment volumes. -func ExpectedDeploymentVolumes(pod *egcfgv1a1.KubernetesPodSpec, volumes []corev1.Volume) []corev1.Volume { +func ExpectedDeploymentVolumes(pod *egv1a1.KubernetesPodSpec, volumes []corev1.Volume) []corev1.Volume { amendFunc := func(volume corev1.Volume) { for index, e := range volumes { if e.Name == volume.Name { @@ -81,7 +90,7 @@ func ExpectedDeploymentVolumes(pod *egcfgv1a1.KubernetesPodSpec, volumes []corev } // ExpectedContainerVolumeMounts returns expected container volume mounts. -func ExpectedContainerVolumeMounts(container *egcfgv1a1.KubernetesContainerSpec, volumeMounts []corev1.VolumeMount) []corev1.VolumeMount { +func ExpectedContainerVolumeMounts(container *egv1a1.KubernetesContainerSpec, volumeMounts []corev1.VolumeMount) []corev1.VolumeMount { amendFunc := func(volumeMount corev1.VolumeMount) { for index, e := range volumeMounts { if e.Name == volumeMount.Name { diff --git a/internal/infrastructure/kubernetes/resource/resource_test.go b/internal/infrastructure/kubernetes/resource/resource_test.go index 46730960851..1cb7c214548 100644 --- a/internal/infrastructure/kubernetes/resource/resource_test.go +++ b/internal/infrastructure/kubernetes/resource/resource_test.go @@ -13,14 +13,16 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) func TestExpectedServiceSpec(t *testing.T) { type args struct { - serviceType *egcfgv1a1.ServiceType + service *egv1a1.KubernetesServiceSpec } + loadbalancerClass := "foobar" tests := []struct { name string args args @@ -28,16 +30,59 @@ func TestExpectedServiceSpec(t *testing.T) { }{ { name: "LoadBalancer", - args: args{serviceType: egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceTypeLoadBalancer)}, + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + }}, want: corev1.ServiceSpec{ Type: corev1.ServiceTypeLoadBalancer, SessionAffinity: corev1.ServiceAffinityNone, ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, }, }, + { + name: "LoadBalancerWithClass", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerClass: &loadbalancerClass, + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + LoadBalancerClass: &loadbalancerClass, + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, + { + name: "LoadBalancerWithAllocateLoadBalancerNodePorts", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + AllocateLoadBalancerNodePorts: ptr.To(true), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + AllocateLoadBalancerNodePorts: ptr.To(true), + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, + { + name: "LoadBalancerWithLoadBalancerIP", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("10.11.12.13"), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + LoadBalancerIP: "10.11.12.13", + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, { name: "ClusterIP", - args: args{serviceType: egcfgv1a1.GetKubernetesServiceType(egcfgv1a1.ServiceTypeClusterIP)}, + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeClusterIP), + }}, want: corev1.ServiceSpec{ Type: corev1.ServiceTypeClusterIP, SessionAffinity: corev1.ServiceAffinityNone, @@ -46,7 +91,7 @@ func TestExpectedServiceSpec(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, tt.want, ExpectedServiceSpec(tt.args.serviceType), "expectedServiceSpec(%v)", tt.args.serviceType) + assert.Equalf(t, tt.want, ExpectedServiceSpec(tt.args.service), "expectedServiceSpec(%v)", tt.args.service) }) } } @@ -240,7 +285,7 @@ func TestCompareSvc(t *testing.T) { func TestExpectedProxyContainerEnv(t *testing.T) { type args struct { - container *egcfgv1a1.KubernetesContainerSpec + container *egv1a1.KubernetesContainerSpec env []corev1.EnvVar } tests := []struct { @@ -251,7 +296,7 @@ func TestExpectedProxyContainerEnv(t *testing.T) { { name: "TestExpectedProxyContainerEnv", args: args{ - container: &egcfgv1a1.KubernetesContainerSpec{ + container: &egv1a1.KubernetesContainerSpec{ Env: []corev1.EnvVar{ { Name: "env_a", @@ -311,7 +356,7 @@ func TestExpectedProxyContainerEnv(t *testing.T) { func TestExpectedDeploymentVolumes(t *testing.T) { type args struct { - pod *egcfgv1a1.KubernetesPodSpec + pod *egv1a1.KubernetesPodSpec volumes []corev1.Volume } tests := []struct { @@ -322,7 +367,7 @@ func TestExpectedDeploymentVolumes(t *testing.T) { { name: "TestExpectedDeploymentVolumes", args: args{ - pod: &egcfgv1a1.KubernetesPodSpec{ + pod: &egv1a1.KubernetesPodSpec{ Volumes: []corev1.Volume{ { Name: "certs", @@ -382,7 +427,7 @@ func TestExpectedDeploymentVolumes(t *testing.T) { func TestExpectedContainerVolumeMounts(t *testing.T) { type args struct { - container *egcfgv1a1.KubernetesContainerSpec + container *egv1a1.KubernetesContainerSpec volumeMounts []corev1.VolumeMount } tests := []struct { @@ -393,7 +438,7 @@ func TestExpectedContainerVolumeMounts(t *testing.T) { { name: "TestExpectedContainerVolumeMounts", args: args{ - container: &egcfgv1a1.KubernetesContainerSpec{ + container: &egv1a1.KubernetesContainerSpec{ VolumeMounts: []corev1.VolumeMount{ { Name: "certs", diff --git a/internal/infrastructure/manager.go b/internal/infrastructure/manager.go index 08df3cc7604..2a5cae1062a 100644 --- a/internal/infrastructure/manager.go +++ b/internal/infrastructure/manager.go @@ -12,7 +12,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" clicfg "sigs.k8s.io/controller-runtime/pkg/client/config" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes" diff --git a/internal/infrastructure/runner/runner.go b/internal/infrastructure/runner/runner.go index 2f7bd146df9..452e346f174 100644 --- a/internal/infrastructure/runner/runner.go +++ b/internal/infrastructure/runner/runner.go @@ -8,7 +8,7 @@ package runner import ( "context" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure" "github.com/envoyproxy/gateway/internal/ir" @@ -34,8 +34,7 @@ func New(cfg *Config) *Runner { } // Start starts the infrastructure runner -func (r *Runner) Start(ctx context.Context) error { - var err error +func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) r.mgr, err = infrastructure.NewManager(&r.Config.Server) if err != nil { @@ -50,24 +49,26 @@ func (r *Runner) Start(ctx context.Context) error { } r.Logger.Info("started") - return nil + return } func (r *Runner) subscribeToProxyInfraIR(ctx context.Context) { // Subscribe to resources - message.HandleSubscription(r.InfraIR.Subscribe(ctx), - func(update message.Update[string, *ir.Infra]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentInfrastructureRunner), Message: "infra-ir"}, r.InfraIR.Subscribe(ctx), + func(update message.Update[string, *ir.Infra], errChan chan error) { r.Logger.Info("received an update") val := update.Value if update.Delete { if err := r.mgr.DeleteProxyInfra(ctx, val); err != nil { r.Logger.Error(err, "failed to delete infra") + errChan <- err } } else { // Manage the proxy infra. if err := r.mgr.CreateOrUpdateProxyInfra(ctx, val); err != nil { r.Logger.Error(err, "failed to create new infra") + errChan <- err } } }, diff --git a/internal/ir/infra.go b/internal/ir/infra.go index 301973b598a..382c44ac487 100644 --- a/internal/ir/infra.go +++ b/internal/ir/infra.go @@ -11,7 +11,7 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" ) const ( @@ -44,6 +44,9 @@ type ProxyInfra struct { // InfraMetadata defines metadata for the managed proxy infrastructure. // +k8s:deepcopy-gen=true type InfraMetadata struct { + // Annotations define a map of string keys and values that can be used to + // organize and categorize proxy infrastructure objects. + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` // Labels define a map of string keys and values that can be used to organize // and categorize proxy infrastructure objects. Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` diff --git a/internal/ir/protocol.go b/internal/ir/protocol.go new file mode 100644 index 00000000000..805443921b2 --- /dev/null +++ b/internal/ir/protocol.go @@ -0,0 +1,27 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package ir + +type AppProtocol string + +const ( + // GRPC declares that the port carries gRPC traffic. + GRPC AppProtocol = "GRPC" + // GRPCWeb declares that the port carries gRPC traffic. + GRPCWeb AppProtocol = "GRPC-Web" + // HTTP declares that the port carries HTTP/1.1 traffic. + // Note that HTTP/1.0 or earlier may not be supported by the proxy. + HTTP AppProtocol = "HTTP" + // HTTP2 declares that the port carries HTTP/2 traffic. + HTTP2 AppProtocol = "HTTP2" + // HTTPS declares that the port carries HTTPS traffic. + HTTPS AppProtocol = "HTTPS" + // TCP declares the port uses TCP. + // This is the default protocol for a service port. + TCP AppProtocol = "TCP" + // UDP declares that the port uses UDP. + UDP AppProtocol = "UDP" +) diff --git a/internal/ir/xds.go b/internal/ir/xds.go index f9c4ffda043..b485fdc637a 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -6,17 +6,21 @@ package ir import ( + "cmp" "errors" "net" + "reflect" "github.com/tetratelabs/multierror" + "golang.org/x/exp/slices" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/validation" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" - "github.com/envoyproxy/gateway/api/v1alpha1/validation" + egv1a1validation "github.com/envoyproxy/gateway/api/v1alpha1/validation" ) var ( @@ -28,9 +32,10 @@ var ( ErrTLSServerCertEmpty = errors.New("field ServerCertificate must be specified") ErrTLSPrivateKey = errors.New("field PrivateKey must be specified") ErrHTTPRouteNameEmpty = errors.New("field Name must be specified") - ErrHTTPRouteMatchEmpty = errors.New("either PathMatch, HeaderMatches or QueryParamMatches fields must be specified") - ErrRouteDestinationHostInvalid = errors.New("field Address must be a valid IP address") - ErrRouteDestinationPortInvalid = errors.New("field Port specified is invalid") + ErrHTTPRouteHostnameEmpty = errors.New("field Hostname must be specified") + ErrDestinationNameEmpty = errors.New("field Name must be specified") + ErrDestEndpointHostInvalid = errors.New("field Address must be a valid IP or FQDN address") + ErrDestEndpointPortInvalid = errors.New("field Port specified is invalid") ErrStringMatchConditionInvalid = errors.New("only one of the Exact, Prefix, SafeRegex or Distinct fields must be set") ErrStringMatchNameIsEmpty = errors.New("field Name must be specified") ErrDirectResponseStatusInvalid = errors.New("only HTTP status codes 100 - 599 are supported for DirectResponse") @@ -41,7 +46,7 @@ var ( ErrAddHeaderEmptyName = errors.New("header modifier filter cannot configure a header without a name to be added") ErrAddHeaderDuplicate = errors.New("header modifier filter attempts to add the same header more than once (case insensitive)") ErrRemoveHeaderDuplicate = errors.New("header modifier filter attempts to remove the same header more than once (case insensitive)") - ErrRequestAuthenRequiresJwt = errors.New("jwt field is required when request authentication is set") + ErrLoadBalancerInvalid = errors.New("loadBalancer setting is invalid, only one setting can be set") ) // Xds holds the intermediate representation of a Gateway and is @@ -52,6 +57,8 @@ type Xds struct { AccessLog *AccessLog `json:"accessLog,omitempty" yaml:"accessLog,omitempty"` // Tracing configuration for the gateway. Tracing *Tracing `json:"tracing,omitempty" yaml:"tracing,omitempty"` + // Metrics configuration for the gateway. + Metrics *Metrics `json:"metrics,omitempty" yaml:"metrics,omitempty"` // HTTP listeners exposed by the gateway. HTTP []*HTTPListener `json:"http,omitempty" yaml:"http,omitempty"` // TCP Listeners exposed by the gateway. @@ -62,6 +69,34 @@ type Xds struct { EnvoyPatchPolicies []*EnvoyPatchPolicy `json:"envoyPatchPolicies,omitempty" yaml:"envoyPatchPolicies,omitempty"` } +// Equal implements the Comparable interface used by watchable.DeepEqual to skip unnecessary updates. +func (x *Xds) Equal(y *Xds) bool { + // Deep copy to avoid modifying the original ordering. + x = x.DeepCopy() + x.sort() + y = y.DeepCopy() + y.sort() + return reflect.DeepEqual(x, y) +} + +// sort ensures the listeners are in a consistent order. +func (x *Xds) sort() { + slices.SortFunc(x.HTTP, func(l1, l2 *HTTPListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) + for _, l := range x.HTTP { + slices.SortFunc(l.Routes, func(r1, r2 *HTTPRoute) int { + return cmp.Compare(r1.Name, r2.Name) + }) + } + slices.SortFunc(x.TCP, func(l1, l2 *TCPListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) + slices.SortFunc(x.UDP, func(l1, l2 *UDPListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) +} + // Validate the fields within the Xds structure. func (x Xds) Validate() error { var errs error @@ -116,6 +151,16 @@ func (x Xds) Printable() *Xds { for _, listener := range out.HTTP { // Omit field listener.TLS = nil + + for _, route := range listener.Routes { + // Omit field + if route.OIDC != nil { + route.OIDC.ClientSecret = []byte{} + } + if route.BasicAuth != nil { + route.BasicAuth.Users = []byte{} + } + } } return out } @@ -138,8 +183,13 @@ type HTTPListener struct { TLS []*TLSListenerConfig `json:"tls,omitempty" yaml:"tls,omitempty"` // Routes associated with HTTP traffic to the service. Routes []*HTTPRoute `json:"routes,omitempty" yaml:"routes,omitempty"` - // IsHTTP2 is set if the upstream client as well as the downstream server are configured to serve HTTP2 traffic. + // IsHTTP2 is set if the listener is configured to serve HTTP2 traffic, + // grpc-web and grpc-stats are also enabled if this is set. IsHTTP2 bool `json:"isHTTP2" yaml:"isHTTP2"` + // TCPKeepalive configuration for the listener + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` + // EnableProxyProtocol enables the listener to interpret proxy protocol header + EnableProxyProtocol bool `json:"enableProxyProtocol,omitempty" yaml:"enableProxyProtocol,omitempty"` } // Validate the fields within the HTTPListener structure @@ -206,6 +256,8 @@ type BackendWeights struct { type HTTPRoute struct { // Name of the HTTPRoute Name string `json:"name" yaml:"name"` + // Hostname that the route matches against + Hostname string `json:"hostname" yaml:"hostname,omitempty"` // PathMatch defines the match conditions on the path. PathMatch *StringMatch `json:"pathMatch,omitempty" yaml:"pathMatch,omitempty"` // HeaderMatches define the match conditions on the request headers for this route. @@ -226,17 +278,29 @@ type HTTPRoute struct { DirectResponse *DirectResponse `json:"directResponse,omitempty" yaml:"directResponse,omitempty"` // Redirections to be returned for this route. Takes precedence over Destinations. Redirect *Redirect `json:"redirect,omitempty" yaml:"redirect,omitempty"` - // Destinations that requests to this HTTPRoute will be mirrored to + // Destination that requests to this HTTPRoute will be mirrored to Mirrors []*RouteDestination `json:"mirrors,omitempty" yaml:"mirrors,omitempty"` - // Destinations associated with this matched route. - Destinations []*RouteDestination `json:"destinations,omitempty" yaml:"destinations,omitempty"` + // Destination associated with this matched route. + Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` // Rewrite to be changed for this route. URLRewrite *URLRewrite `json:"urlRewrite,omitempty" yaml:"urlRewrite,omitempty"` // RateLimit defines the more specific match conditions as well as limits for ratelimiting // the requests on this route. RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` - // RequestAuthentication defines the schema for authenticating HTTP requests. - RequestAuthentication *RequestAuthentication `json:"requestAuthentication,omitempty" yaml:"requestAuthentication,omitempty"` + // Timeout is the time until which entire response is received from the upstream. + Timeout *metav1.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` + // load balancer policy to use when routing to the backend endpoints. + LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` + // CORS policy for the route. + CORS *CORS `json:"cors,omitempty" yaml:"cors,omitempty"` + // JWT defines the schema for authenticating HTTP requests using JSON Web Tokens (JWT). + JWT *JWT `json:"jwt,omitempty" yaml:"jwt,omitempty"` + // OIDC defines the schema for authenticating HTTP requests using OpenID Connect (OIDC). + OIDC *OIDC `json:"oidc,omitempty" yaml:"oidc,omitempty"` + // Proxy Protocol Settings + ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty"` + // BasicAuth defines the schema for the HTTP Basic Authentication. + BasicAuth *BasicAuth `json:"basicAuth,omitempty" yaml:"basicAuth,omitempty"` // ExtensionRefs holds unstructured resources that were introduced by an extension and used on the HTTPRoute as extensionRef filters ExtensionRefs []*UnstructuredRef `json:"extensionRefs,omitempty" yaml:"extensionRefs,omitempty"` } @@ -250,24 +314,71 @@ type UnstructuredRef struct { Object *unstructured.Unstructured `json:"object,omitempty" yaml:"object,omitempty"` } -// RequestAuthentication defines the schema for authenticating HTTP requests. -// Only one of "jwt" can be specified. -// -// TODO: Add support for additional request authentication providers, i.e. OIDC. +// CORS holds the Cross-Origin Resource Sharing (CORS) policy for the route. // // +k8s:deepcopy-gen=true -type RequestAuthentication struct { - // JWT defines the schema for authenticating HTTP requests using JSON Web Tokens (JWT). - JWT *JwtRequestAuthentication `json:"jwt,omitempty" yaml:"jwt,omitempty"` +type CORS struct { + // AllowOrigins defines the origins that are allowed to make requests. + AllowOrigins []*StringMatch `json:"allowOrigins,omitempty" yaml:"allowOrigins,omitempty"` + // AllowMethods defines the methods that are allowed to make requests. + AllowMethods []string `json:"allowMethods,omitempty" yaml:"allowMethods,omitempty"` + // AllowHeaders defines the headers that are allowed to be sent with requests. + AllowHeaders []string `json:"allowHeaders,omitempty" yaml:"allowHeaders,omitempty"` + // ExposeHeaders defines the headers that can be exposed in the responses. + ExposeHeaders []string `json:"exposeHeaders,omitempty" yaml:"exposeHeaders,omitempty"` + // MaxAge defines how long the results of a preflight request can be cached. + MaxAge *metav1.Duration `json:"maxAge,omitempty" yaml:"maxAge,omitempty"` + // AllowCredentials indicates whether a request can include user credentials. + AllowCredentials bool `json:"allowCredentials,omitempty" yaml:"allowCredentials,omitempty"` } -// JwtRequestAuthentication defines the schema for authenticating HTTP requests using +// JWT defines the schema for authenticating HTTP requests using // JSON Web Tokens (JWT). // // +k8s:deepcopy-gen=true -type JwtRequestAuthentication struct { +type JWT struct { // Providers defines a list of JSON Web Token (JWT) authentication providers. - Providers []egv1a1.JwtAuthenticationFilterProvider `json:"providers,omitempty" yaml:"providers,omitempty"` + Providers []egv1a1.JWTProvider `json:"providers,omitempty" yaml:"providers,omitempty"` +} + +// OIDC defines the schema for authenticating HTTP requests using +// OpenID Connect (OIDC). +// +// +k8s:deepcopy-gen=true +type OIDC struct { + // The OIDC Provider configuration. + Provider OIDCProvider `json:"provider" yaml:"provider"` + + // The OIDC client ID to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + ClientID string `json:"clientID" yaml:"clientID"` + + // The OIDC client secret to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // + // This is an Opaque secret. The client secret should be stored in the key "client-secret". + + ClientSecret []byte `json:"clientSecret,omitempty" yaml:"clientSecret,omitempty"` + + // The OIDC scopes to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + Scopes []string `json:"scopes,omitempty" yaml:"scopes,omitempty"` +} + +// BasicAuth defines the schema for the HTTP Basic Authentication. +// +// +k8s:deepcopy-gen=true +type BasicAuth struct { + // The username-password pairs in htpasswd format. + Users []byte `json:"users,omitempty" yaml:"users,omitempty"` +} + +type OIDCProvider struct { + // The OIDC Provider's [authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint). + AuthorizationEndpoint string `json:"authorizationEndpoint,omitempty"` + + // The OIDC Provider's [token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint). + TokenEndpoint string `json:"tokenEndpoint,omitempty"` } // Validate the fields within the HTTPRoute structure @@ -276,8 +387,8 @@ func (h HTTPRoute) Validate() error { if h.Name == "" { errs = multierror.Append(errs, ErrHTTPRouteNameEmpty) } - if h.PathMatch == nil && (len(h.HeaderMatches) == 0) && (len(h.QueryParamMatches) == 0) { - errs = multierror.Append(errs, ErrHTTPRouteMatchEmpty) + if h.Hostname == "" { + errs = multierror.Append(errs, ErrHTTPRouteHostnameEmpty) } if h.PathMatch != nil { if err := h.PathMatch.Validate(); err != nil { @@ -294,8 +405,8 @@ func (h HTTPRoute) Validate() error { errs = multierror.Append(errs, err) } } - for _, dest := range h.Destinations { - if err := dest.Validate(); err != nil { + if h.Destination != nil { + if err := h.Destination.Validate(); err != nil { errs = multierror.Append(errs, err) } } @@ -314,9 +425,11 @@ func (h HTTPRoute) Validate() error { errs = multierror.Append(errs, err) } } - for _, mirror := range h.Mirrors { - if err := mirror.Validate(); err != nil { - errs = multierror.Append(errs, err) + if h.Mirrors != nil { + for _, mirror := range h.Mirrors { + if err := mirror.Validate(); err != nil { + errs = multierror.Append(errs, err) + } } } if len(h.AddRequestHeaders) > 0 { @@ -369,23 +482,24 @@ func (h HTTPRoute) Validate() error { } } } - if h.RequestAuthentication != nil { - switch { - case h.RequestAuthentication.JWT == nil: - errs = multierror.Append(errs, ErrRequestAuthenRequiresJwt) - default: - if err := h.RequestAuthentication.JWT.Validate(); err != nil { - errs = multierror.Append(errs, err) - } + if h.LoadBalancer != nil { + if err := h.LoadBalancer.Validate(); err != nil { + errs = multierror.Append(errs, err) + } + } + if h.JWT != nil { + if err := h.JWT.validate(); err != nil { + errs = multierror.Append(errs, err) } } + return errs } -func (j *JwtRequestAuthentication) Validate() error { +func (j *JWT) validate() error { var errs error - if err := validation.ValidateJwtProviders(j.Providers); err != nil { + if err := egv1a1validation.ValidateJWTProvider(j.Providers); err != nil { errs = multierror.Append(errs, err) } @@ -395,42 +509,94 @@ func (j *JwtRequestAuthentication) Validate() error { // RouteDestination holds the destination details associated with the route // +kubebuilder:object:generate=true type RouteDestination struct { - // Host refers to the FQDN or IP address of the backend service. - Host string `json:"host" yaml:"host"` - // Port on the service to forward the request to. - Port uint32 `json:"port" yaml:"port"` + // Name of the destination. This field allows the xds layer + // to check if this route destination already exists and can be + // reused + Name string `json:"name" yaml:"name"` + Settings []*DestinationSetting `json:"settings,omitempty" yaml:"settings,omitempty"` +} + +// Validate the fields within the RouteDestination structure +func (r RouteDestination) Validate() error { + var errs error + if len(r.Name) == 0 { + errs = multierror.Append(errs, ErrDestinationNameEmpty) + } + for _, s := range r.Settings { + if err := s.Validate(); err != nil { + errs = multierror.Append(errs, err) + } + } + + return errs +} + +// DestinationSetting holds the settings associated with the destination +// +kubebuilder:object:generate=true +type DestinationSetting struct { // Weight associated with this destination. // Note: Weight is not used in TCP/UDP route. Weight *uint32 `json:"weight,omitempty" yaml:"weight,omitempty"` + // Protocol associated with this destination/port. + Protocol AppProtocol `json:"protocol" yaml:"protocol"` + Endpoints []*DestinationEndpoint `json:"endpoints,omitempty" yaml:"endpoints,omitempty"` + // AddressTypeState specifies the state of DestinationEndpoint address type. + AddressType *DestinationAddressType `json:"addressType,omitempty" yaml:"addressType,omitempty"` } // Validate the fields within the RouteDestination structure -func (r RouteDestination) Validate() error { +func (d DestinationSetting) Validate() error { var errs error - // Only support IP hosts for now - if ip := net.ParseIP(r.Host); ip == nil { - errs = multierror.Append(errs, ErrRouteDestinationHostInvalid) - } - if r.Port == 0 { - errs = multierror.Append(errs, ErrRouteDestinationPortInvalid) + for _, ep := range d.Endpoints { + if err := ep.Validate(); err != nil { + errs = multierror.Append(errs, err) + } } return errs } -// NewRouteDest creates a new RouteDestination. -func NewRouteDest(host string, port uint32) *RouteDestination { - return &RouteDestination{ - Host: host, - Port: port, +// DestinationAddressType describes the address type state for a group of DestinationEndpoint +type DestinationAddressType string + +const ( + IP DestinationAddressType = "IP" + FQDN DestinationAddressType = "FQDN" + MIXED DestinationAddressType = "Mixed" +) + +// DestinationEndpoint holds the endpoint details associated with the destination +// +kubebuilder:object:generate=true +type DestinationEndpoint struct { + // Host refers to the FQDN or IP address of the backend service. + Host string `json:"host" yaml:"host"` + // Port on the service to forward the request to. + Port uint32 `json:"port" yaml:"port"` +} + +// Validate the fields within the DestinationEndpoint structure +func (d DestinationEndpoint) Validate() error { + var errs error + + err := validation.IsDNS1123Subdomain(d.Host) + ip := net.ParseIP(d.Host) + + if err != nil && ip == nil { + errs = multierror.Append(errs, ErrDestEndpointHostInvalid) + } + + if d.Port == 0 { + errs = multierror.Append(errs, ErrDestEndpointPortInvalid) } + + return errs } -func NewRouteDestWithWeight(host string, port uint32, weight uint32) *RouteDestination { - return &RouteDestination{ - Host: host, - Port: port, - Weight: &weight, +// NewDestEndpoint creates a new DestinationEndpoint. +func NewDestEndpoint(host string, port uint32) *DestinationEndpoint { + return &DestinationEndpoint{ + Host: host, + Port: port, } } @@ -619,7 +785,9 @@ type TCPListener struct { // TLS holds information for configuring TLS on a listener TLS *TLS `json:"tls,omitempty" yaml:"tls,omitempty"` // Destinations associated with TCP traffic to the service. - Destinations []*RouteDestination `json:"destinations,omitempty" yaml:"destinations,omitempty"` + Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` + // TCPKeepalive configuration for the listener + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` } // TLS holds information for configuring TLS on a listener @@ -658,8 +826,8 @@ func (h TCPListener) Validate() error { } } - for _, route := range h.Destinations { - if err := route.Validate(); err != nil { + if h.Destination != nil { + if err := h.Destination.Validate(); err != nil { errs = multierror.Append(errs, err) } } @@ -694,8 +862,8 @@ type UDPListener struct { Address string `json:"address" yaml:"address"` // Port on which the service can be expected to be accessed by clients. Port uint32 `json:"port" yaml:"port"` - // Destinations associated with UDP traffic to the service. - Destinations []*RouteDestination `json:"destinations,omitempty" yaml:"destinations,omitempty"` + // Destination associated with UDP traffic to the service. + Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` } // Validate the fields within the UDPListener structure @@ -710,11 +878,12 @@ func (h UDPListener) Validate() error { if h.Port == 0 { errs = multierror.Append(errs, ErrListenerPortInvalid) } - for _, route := range h.Destinations { - if err := route.Validate(); err != nil { + if h.Destination != nil { + if err := h.Destination.Validate(); err != nil { errs = multierror.Append(errs, err) } } + return errs } @@ -804,7 +973,7 @@ type OpenTelemetryAccessLog struct { type EnvoyPatchPolicy struct { EnvoyPatchPolicyStatus // JSONPatches are the JSON Patches that - // are to be applied to generaed Xds linked to the gateway. + // are to be applied to generated Xds linked to the gateway. JSONPatches []*JSONPatchConfig `json:"jsonPatches,omitempty" yaml:"jsonPatches,omitempty"` } @@ -847,5 +1016,113 @@ type JSONPatchOperation struct { type Tracing struct { ServiceName string `json:"serviceName"` - egcfgv1a1.ProxyTracing + egv1a1.ProxyTracing +} + +// Metrics defines the configuration for metrics generated by Envoy +// +k8s:deepcopy-gen=true +type Metrics struct { + EnableVirtualHostStats bool `json:"enableVirtualHostStats" yaml:"enableVirtualHostStats"` +} + +// TCPKeepalive define the TCP Keepalive configuration. +// +k8s:deepcopy-gen=true +type TCPKeepalive struct { + // The total number of unacknowledged probes to send before deciding + // the connection is dead. + // Defaults to 9. + Probes *uint32 `json:"probes,omitempty" yaml:"probes,omitempty"` + // The duration, in seconds, a connection needs to be idle before keep-alive + // probes start being sent. + // Defaults to `7200s`. + IdleTime *uint32 `json:"idleTime,omitempty" yaml:"idleTime,omitempty"` + // The duration, in seconds, between keep-alive probes. + // Defaults to `75s`. + Interval *uint32 `json:"interval,omitempty" yaml:"interval,omitempty"` +} + +// LoadBalancer defines the load balancer settings. +// +k8s:deepcopy-gen=true +type LoadBalancer struct { + // RoundRobin load balacning policy + RoundRobin *RoundRobin `json:"roundRobin,omitempty" yaml:"roundRobin,omitempty"` + // LeastRequest load balancer policy + LeastRequest *LeastRequest `json:"leastRequest,omitempty" yaml:"leastRequest,omitempty"` + // Random load balancer policy + Random *Random `json:"random,omitempty" yaml:"random,omitempty"` + // ConsistentHash load balancer policy + ConsistentHash *ConsistentHash `json:"consistentHash,omitempty" yaml:"consistentHash,omitempty"` +} + +// Validate the fields within the LoadBalancer structure +func (l *LoadBalancer) Validate() error { + var errs error + matchCount := 0 + if l.RoundRobin != nil { + matchCount++ + } + if l.LeastRequest != nil { + matchCount++ + } + if l.Random != nil { + matchCount++ + } + if l.ConsistentHash != nil { + matchCount++ + } + if matchCount != 1 { + errs = multierror.Append(errs, ErrLoadBalancerInvalid) + } + + return errs +} + +// RoundRobin load balancer settings +// +k8s:deepcopy-gen=true +type RoundRobin struct { + // SlowStart defines the slow start configuration. + // If set, slow start mode is enabled for newly added hosts in the cluster. + SlowStart *SlowStart `json:"slowStart,omitempty" yaml:"slowStart,omitempty"` +} + +// LeastRequest load balancer settings +// +k8s:deepcopy-gen=true +type LeastRequest struct { + // SlowStart defines the slow start configuration. + // If set, slow start mode is enabled for newly added hosts in the cluster. + SlowStart *SlowStart `json:"slowStart,omitempty" yaml:"slowStart,omitempty"` +} + +// Random load balancer settings +// +k8s:deepcopy-gen=true +type Random struct{} + +// ConsistentHash load balancer settings +// +k8s:deepcopy-gen=true +type ConsistentHash struct { + // Hash based on the Source IP Address + SourceIP *bool `json:"sourceIP,omitempty" yaml:"sourceIP,omitempty"` +} + +type ProxyProtocolVersion string + +const ( + // ProxyProtocolVersionV1 is the PROXY protocol version 1 (human readable format). + ProxyProtocolVersionV1 ProxyProtocolVersion = "V1" + // ProxyProtocolVersionV2 is the PROXY protocol version 2 (binary format). + ProxyProtocolVersionV2 ProxyProtocolVersion = "V2" +) + +// ProxyProtocol upstream settings +// +k8s:deepcopy-gen=true +type ProxyProtocol struct { + // Version of proxy protocol to use + Version ProxyProtocolVersion `json:"version,omitempty" yaml:"version,omitempty"` +} + +// SlowStart defines the slow start configuration. +// +k8s:deepcopy-gen=true +type SlowStart struct { + // Window defines the duration of the warm up period for newly added host. + Window *metav1.Duration `json:"window" yaml:"window"` } diff --git a/internal/ir/xds_test.go b/internal/ir/xds_test.go index 374b3e7f4de..cca7ef581ae 100644 --- a/internal/ir/xds_test.go +++ b/internal/ir/xds_test.go @@ -8,8 +8,10 @@ package ir import ( "testing" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "k8s.io/utils/ptr" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) @@ -42,13 +44,6 @@ var ( Hostnames: []string{"example.com"}, Routes: []*HTTPRoute{&happyHTTPRoute}, } - invalidRouteMatchHTTPListener = HTTPListener{ - Name: "invalid-route-match", - Address: "0.0.0.0", - Port: 80, - Hostnames: []string{"example.com"}, - Routes: []*HTTPRoute{&emptyMatchHTTPRoute}, - } invalidBackendHTTPListener = HTTPListener{ Name: "invalid-backend-match", Address: "0.0.0.0", @@ -66,11 +61,11 @@ var ( // TCPListener happyTCPListenerTLSPassthrough = TCPListener{ - Name: "happy", - Address: "0.0.0.0", - Port: 80, - TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, - Destinations: []*RouteDestination{&happyRouteDestination}, + Name: "happy", + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, + Destination: &happyRouteDestination, } happyTCPListenerTLSTerminate = TCPListener{ @@ -82,87 +77,86 @@ var ( ServerCertificate: []byte("server-cert"), PrivateKey: []byte("priv-key"), }}}, - Destinations: []*RouteDestination{&happyRouteDestination}, + Destination: &happyRouteDestination, } emptySNITCPListenerTLSPassthrough = TCPListener{ - Name: "empty-sni", - Address: "0.0.0.0", - Port: 80, - Destinations: []*RouteDestination{&happyRouteDestination}, + Name: "empty-sni", + Address: "0.0.0.0", + Port: 80, + Destination: &happyRouteDestination, } invalidNameTCPListenerTLSPassthrough = TCPListener{ - Address: "0.0.0.0", - Port: 80, - TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, - Destinations: []*RouteDestination{&happyRouteDestination}, + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, + Destination: &happyRouteDestination, } invalidAddrTCPListenerTLSPassthrough = TCPListener{ - Name: "invalid-addr", - Address: "1.0.0", - Port: 80, - TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, - Destinations: []*RouteDestination{&happyRouteDestination}, + Name: "invalid-addr", + Address: "1.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, + Destination: &happyRouteDestination, } invalidSNITCPListenerTLSPassthrough = TCPListener{ - Address: "0.0.0.0", - Port: 80, - TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{}}}, - Destinations: []*RouteDestination{&happyRouteDestination}, + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{}}}, + Destination: &happyRouteDestination, } // UDPListener happyUDPListener = UDPListener{ - Name: "happy", - Address: "0.0.0.0", - Port: 80, - Destinations: []*RouteDestination{&happyRouteDestination}, + Name: "happy", + Address: "0.0.0.0", + Port: 80, + Destination: &happyRouteDestination, } invalidNameUDPListener = UDPListener{ - Address: "0.0.0.0", - Port: 80, - Destinations: []*RouteDestination{&happyRouteDestination}, + Address: "0.0.0.0", + Port: 80, + Destination: &happyRouteDestination, } invalidAddrUDPListener = UDPListener{ - Name: "invalid-addr", - Address: "1.0.0", - Port: 80, - Destinations: []*RouteDestination{&happyRouteDestination}, + Name: "invalid-addr", + Address: "1.0.0", + Port: 80, + Destination: &happyRouteDestination, } invalidPortUDPListenerT = UDPListener{ - Name: "invalid-port", - Address: "0.0.0.0", - Port: 0, - Destinations: []*RouteDestination{&happyRouteDestination}, + Name: "invalid-port", + Address: "0.0.0.0", + Port: 0, + Destination: &happyRouteDestination, } // HTTPRoute happyHTTPRoute = HTTPRoute{ - Name: "happy", + Name: "happy", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("example"), + Exact: ptr.To("example"), }, - Destinations: []*RouteDestination{&happyRouteDestination}, - } - emptyMatchHTTPRoute = HTTPRoute{ - Name: "empty-match", - Destinations: []*RouteDestination{&happyRouteDestination}, + Destination: &happyRouteDestination, } invalidBackendHTTPRoute = HTTPRoute{ - Name: "invalid-backend", + Name: "invalid-backend", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("invalid-backend"), + Exact: ptr.To("invalid-backend"), }, BackendWeights: BackendWeights{ Invalid: 1, }, } weightedInvalidBackendsHTTPRoute = HTTPRoute{ - Name: "weighted-invalid-backends", + Name: "weighted-invalid-backends", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("invalid-backends"), + Exact: ptr.To("invalid-backends"), }, - Destinations: []*RouteDestination{&happyRouteDestination}, + Destination: &happyRouteDestination, BackendWeights: BackendWeights{ Invalid: 1, Valid: 1, @@ -170,103 +164,111 @@ var ( } redirectHTTPRoute = HTTPRoute{ - Name: "redirect", + Name: "redirect", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("redirect"), + Exact: ptr.To("redirect"), }, Redirect: &Redirect{ - Scheme: ptrTo("https"), - Hostname: ptrTo("redirect.example.com"), + Scheme: ptr.To("https"), + Hostname: ptr.To("redirect.example.com"), Path: &HTTPPathModifier{ - FullReplace: ptrTo("/redirect"), + FullReplace: ptr.To("/redirect"), }, - Port: ptrTo(uint32(8443)), - StatusCode: ptrTo(int32(301)), + Port: ptr.To(uint32(8443)), + StatusCode: ptr.To[int32](301), }, } // A direct response error is used when an invalid filter type is supplied invalidFilterHTTPRoute = HTTPRoute{ - Name: "filter-error", + Name: "filter-error", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("filter-error"), + Exact: ptr.To("filter-error"), }, DirectResponse: &DirectResponse{ - Body: ptrTo("invalid filter type"), + Body: ptr.To("invalid filter type"), StatusCode: uint32(500), }, } redirectFilterInvalidStatus = HTTPRoute{ - Name: "redirect-bad-status-scheme-nopat", + Name: "redirect-bad-status-scheme-nopat", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("redirect"), + Exact: ptr.To("redirect"), }, Redirect: &Redirect{ - Scheme: ptrTo("err"), - Hostname: ptrTo("redirect.example.com"), + Scheme: ptr.To("err"), + Hostname: ptr.To("redirect.example.com"), Path: &HTTPPathModifier{}, - Port: ptrTo(uint32(8443)), - StatusCode: ptrTo(int32(305)), + Port: ptr.To(uint32(8443)), + StatusCode: ptr.To[int32](305), }, } redirectFilterBadPath = HTTPRoute{ - Name: "redirect", + Name: "redirect", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("redirect"), + Exact: ptr.To("redirect"), }, Redirect: &Redirect{ - Scheme: ptrTo("https"), - Hostname: ptrTo("redirect.example.com"), + Scheme: ptr.To("https"), + Hostname: ptr.To("redirect.example.com"), Path: &HTTPPathModifier{ - FullReplace: ptrTo("/redirect"), - PrefixMatchReplace: ptrTo("/redirect"), + FullReplace: ptr.To("/redirect"), + PrefixMatchReplace: ptr.To("/redirect"), }, - Port: ptrTo(uint32(8443)), - StatusCode: ptrTo(int32(301)), + Port: ptr.To(uint32(8443)), + StatusCode: ptr.To[int32](301), }, } directResponseBadStatus = HTTPRoute{ - Name: "redirect", + Name: "redirect", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("redirect"), + Exact: ptr.To("redirect"), }, DirectResponse: &DirectResponse{ - Body: ptrTo("invalid filter type"), + Body: ptr.To("invalid filter type"), StatusCode: uint32(799), }, } urlRewriteHTTPRoute = HTTPRoute{ - Name: "rewrite", + Name: "rewrite", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("rewrite"), + Exact: ptr.To("rewrite"), }, URLRewrite: &URLRewrite{ - Hostname: ptrTo("rewrite.example.com"), + Hostname: ptr.To("rewrite.example.com"), Path: &HTTPPathModifier{ - FullReplace: ptrTo("/rewrite"), + FullReplace: ptr.To("/rewrite"), }, }, } urlRewriteFilterBadPath = HTTPRoute{ - Name: "rewrite", + Name: "rewrite", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("rewrite"), + Exact: ptr.To("rewrite"), }, URLRewrite: &URLRewrite{ - Hostname: ptrTo("rewrite.example.com"), + Hostname: ptr.To("rewrite.example.com"), Path: &HTTPPathModifier{ - FullReplace: ptrTo("/rewrite"), - PrefixMatchReplace: ptrTo("/rewrite"), + FullReplace: ptr.To("/rewrite"), + PrefixMatchReplace: ptr.To("/rewrite"), }, }, } addRequestHeaderHTTPRoute = HTTPRoute{ - Name: "addheader", + Name: "addheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("addheader"), + Exact: ptr.To("addheader"), }, AddRequestHeaders: []AddHeader{ { @@ -288,9 +290,10 @@ var ( } removeRequestHeaderHTTPRoute = HTTPRoute{ - Name: "remheader", + Name: "remheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("remheader"), + Exact: ptr.To("remheader"), }, RemoveRequestHeaders: []string{ "x-request-header", @@ -300,9 +303,10 @@ var ( } addAndRemoveRequestHeadersDupeHTTPRoute = HTTPRoute{ - Name: "duplicateheader", + Name: "duplicateheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("duplicateheader"), + Exact: ptr.To("duplicateheader"), }, AddRequestHeaders: []AddHeader{ { @@ -324,9 +328,10 @@ var ( } addRequestHeaderEmptyHTTPRoute = HTTPRoute{ - Name: "addemptyheader", + Name: "addemptyheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("addemptyheader"), + Exact: ptr.To("addemptyheader"), }, AddRequestHeaders: []AddHeader{ { @@ -338,9 +343,10 @@ var ( } addResponseHeaderHTTPRoute = HTTPRoute{ - Name: "addheader", + Name: "addheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("addheader"), + Exact: ptr.To("addheader"), }, AddResponseHeaders: []AddHeader{ { @@ -362,9 +368,10 @@ var ( } removeResponseHeaderHTTPRoute = HTTPRoute{ - Name: "remheader", + Name: "remheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("remheader"), + Exact: ptr.To("remheader"), }, RemoveResponseHeaders: []string{ "x-request-header", @@ -374,9 +381,10 @@ var ( } addAndRemoveResponseHeadersDupeHTTPRoute = HTTPRoute{ - Name: "duplicateheader", + Name: "duplicateheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("duplicateheader"), + Exact: ptr.To("duplicateheader"), }, AddResponseHeaders: []AddHeader{ { @@ -398,9 +406,10 @@ var ( } addResponseHeaderEmptyHTTPRoute = HTTPRoute{ - Name: "addemptyheader", + Name: "addemptyheader", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("addemptyheader"), + Exact: ptr.To("addemptyheader"), }, AddResponseHeaders: []AddHeader{ { @@ -412,62 +421,47 @@ var ( } jwtAuthenHTTPRoute = HTTPRoute{ - Name: "jwtauthen", + Name: "jwtauthen", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("jwtauthen"), - }, - RequestAuthentication: &RequestAuthentication{ - JWT: &JwtRequestAuthentication{ - Providers: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test1", - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test1.local", - }, + Exact: ptr.To("jwtauthen"), + }, + JWT: &JWT{ + Providers: []egv1a1.JWTProvider{ + { + Name: "test1", + RemoteJWKS: egv1a1.RemoteJWKS{ + URI: "https://test1.local", }, }, }, }, } requestMirrorFilter = HTTPRoute{ - Name: "mirrorfilter", + Name: "mirrorfilter", + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("mirrorfilter"), - }, - Mirrors: []*RouteDestination{ - &happyRouteDestination, - }, - } - - requestMirrorFilterMultiple = HTTPRoute{ - Name: "mirrorfilterMultiple", - PathMatch: &StringMatch{ - Exact: ptrTo("mirrorfiltermultiple"), - }, - Mirrors: []*RouteDestination{ - &happyRouteDestination, - &otherHappyRouteDestination, + Exact: ptr.To("mirrorfilter"), }, + Mirrors: []*RouteDestination{&happyRouteDestination}, } // RouteDestination happyRouteDestination = RouteDestination{ - Host: "10.11.12.13", - Port: 8080, - } - - // RouteDestination - otherHappyRouteDestination = RouteDestination{ - Host: "11.12.13.14", - Port: 8080, + Name: "happy-dest", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, } ) -// Creates a pointer to any type -func ptrTo[T any](x T) *T { - return &x -} - func TestValidateXds(t *testing.T) { tests := []struct { name string @@ -498,9 +492,9 @@ func TestValidateXds(t *testing.T) { { name: "invalid listener", input: Xds{ - HTTP: []*HTTPListener{&happyHTTPListener, &invalidAddrHTTPListener, &invalidRouteMatchHTTPListener}, + HTTP: []*HTTPListener{&happyHTTPListener, &invalidAddrHTTPListener}, }, - want: []error{ErrListenerAddressInvalid, ErrHTTPRouteMatchEmpty}, + want: []error{ErrListenerAddressInvalid}, }, { name: "invalid backend", @@ -567,11 +561,6 @@ func TestValidateHTTPListener(t *testing.T) { }, want: []error{ErrListenerPortInvalid, ErrHTTPListenerHostnamesEmpty}, }, - { - name: "invalid route match", - input: invalidRouteMatchHTTPListener, - want: []error{ErrHTTPRouteMatchEmpty}, - }, } for _, test := range tests { test := test @@ -676,6 +665,80 @@ func TestValidateTLSListenerConfig(t *testing.T) { } } +func TestEqualXds(t *testing.T) { + tests := []struct { + desc string + a *Xds + b *Xds + equal bool + }{ + { + desc: "out of order tcp listeners are equal", + a: &Xds{ + TCP: []*TCPListener{ + {Name: "listener-1"}, + {Name: "listener-2"}, + }, + }, + b: &Xds{ + TCP: []*TCPListener{ + {Name: "listener-2"}, + {Name: "listener-1"}, + }, + }, + equal: true, + }, + { + desc: "out of order http routes are equal", + a: &Xds{ + HTTP: []*HTTPListener{ + { + Name: "listener-1", + Routes: []*HTTPRoute{ + {Name: "route-1"}, + {Name: "route-2"}, + }, + }, + }, + }, + b: &Xds{ + HTTP: []*HTTPListener{ + { + Name: "listener-1", + Routes: []*HTTPRoute{ + {Name: "route-2"}, + {Name: "route-1"}, + }, + }, + }, + }, + equal: true, + }, + { + desc: "out of order udp listeners are equal", + a: &Xds{ + UDP: []*UDPListener{ + {Name: "listener-1"}, + {Name: "listener-2"}, + }, + }, + b: &Xds{ + UDP: []*UDPListener{ + {Name: "listener-2"}, + {Name: "listener-1"}, + }, + }, + equal: true, + }, + } + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + require.Equal(t, tc.equal, cmp.Equal(tc.a, tc.b)) + }) + } +} + func TestValidateUDPListener(t *testing.T) { tests := []struct { name string @@ -732,17 +795,24 @@ func TestValidateHTTPRoute(t *testing.T) { { name: "invalid name", input: HTTPRoute{ + Hostname: "*", PathMatch: &StringMatch{ - Exact: ptrTo("example"), + Exact: ptr.To("example"), }, - Destinations: []*RouteDestination{&happyRouteDestination}, + Destination: &happyRouteDestination, }, want: []error{ErrHTTPRouteNameEmpty}, }, { - name: "empty match", - input: emptyMatchHTTPRoute, - want: []error{ErrHTTPRouteMatchEmpty}, + name: "invalid hostname", + input: HTTPRoute{ + Name: "invalid hostname", + PathMatch: &StringMatch{ + Exact: ptr.To("example"), + }, + Destination: &happyRouteDestination, + }, + want: []error{ErrHTTPRouteHostnameEmpty}, }, { name: "invalid backend", @@ -757,8 +827,9 @@ func TestValidateHTTPRoute(t *testing.T) { { name: "empty name and invalid match", input: HTTPRoute{ - HeaderMatches: []*StringMatch{ptrTo(StringMatch{})}, - Destinations: []*RouteDestination{&happyRouteDestination}, + Hostname: "*", + HeaderMatches: []*StringMatch{ptr.To(StringMatch{})}, + Destination: &happyRouteDestination, }, want: []error{ErrHTTPRouteNameEmpty, ErrStringMatchConditionInvalid}, }, @@ -846,11 +917,6 @@ func TestValidateHTTPRoute(t *testing.T) { input: requestMirrorFilter, want: nil, }, - { - name: "mirror-filter-multiple", - input: requestMirrorFilterMultiple, - want: nil, - }, } for _, test := range tests { test := test @@ -879,26 +945,103 @@ func TestValidateRouteDestination(t *testing.T) { want: nil, }, { - name: "invalid ip", + name: "valid hostname", input: RouteDestination{ - Host: "example.com", - Port: 8080, + Name: "valid hostname", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "example.com", + Port: 8080, + }, + }, + }, + }, }, - want: ErrRouteDestinationHostInvalid, + want: nil, + }, + { + name: "valid ip", + input: RouteDestination{ + Name: "valid ip", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "1.2.3.4", + Port: 8080, + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "invalid address", + input: RouteDestination{ + Name: "invalid address", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "example.com::foo.bar", + Port: 8080, + }, + }, + }, + }, + }, + want: ErrDestEndpointHostInvalid, }, { name: "missing ip", input: RouteDestination{ - Port: 8080, + Name: "missing ip", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Port: 8080, + }, + }, + }, + }, }, - want: ErrRouteDestinationHostInvalid, + want: ErrDestEndpointHostInvalid, }, { name: "missing port", input: RouteDestination{ - Host: "10.11.12.13", + Name: "missing port", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "10.11.12.13", + }, + }, + }, + }, + }, + want: ErrDestEndpointPortInvalid, + }, + { + name: "missing name", + input: RouteDestination{ + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, }, - want: ErrRouteDestinationPortInvalid, + want: ErrDestinationNameEmpty, }, } for _, test := range tests { @@ -922,7 +1065,7 @@ func TestValidateStringMatch(t *testing.T) { { name: "happy", input: StringMatch{ - Exact: ptrTo("example"), + Exact: ptr.To("example"), }, want: nil, }, @@ -934,9 +1077,9 @@ func TestValidateStringMatch(t *testing.T) { { name: "multiple fields set", input: StringMatch{ - Exact: ptrTo("example"), + Exact: ptr.To("example"), Name: "example", - Prefix: ptrTo("example"), + Prefix: ptr.To("example"), }, want: ErrStringMatchConditionInvalid, }, @@ -953,23 +1096,23 @@ func TestValidateStringMatch(t *testing.T) { } } -func TestValidateJwtRequestAuthentication(t *testing.T) { +func TestValidateJWT(t *testing.T) { tests := []struct { name string - input JwtRequestAuthentication + input JWT want error }{ { name: "nil rules", - input: JwtRequestAuthentication{ + input: JWT{ Providers: nil, }, want: nil, }, { name: "provider with remote jwks uri", - input: JwtRequestAuthentication{ - Providers: []egv1a1.JwtAuthenticationFilterProvider{ + input: JWT{ + Providers: []egv1a1.JWTProvider{ { Name: "test", Issuer: "https://test.local", @@ -983,6 +1126,50 @@ func TestValidateJwtRequestAuthentication(t *testing.T) { want: nil, }, } + for i := range tests { + test := tests[i] + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.validate()) + } else { + require.EqualError(t, test.input.validate(), test.want.Error()) + } + }) + } +} + +func TestValidateLoadBalancer(t *testing.T) { + tests := []struct { + name string + input LoadBalancer + want error + }{ + { + name: "random", + input: LoadBalancer{ + Random: &Random{}, + }, + want: nil, + }, + { + name: "consistent hash", + input: LoadBalancer{ + ConsistentHash: &ConsistentHash{ + SourceIP: ptr.To(true), + }, + }, + want: nil, + }, + + { + name: "least request and random set", + input: LoadBalancer{ + Random: &Random{}, + LeastRequest: &LeastRequest{}, + }, + want: ErrLoadBalancerInvalid, + }, + } for i := range tests { test := tests[i] t.Run(test.name, func(t *testing.T) { diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 556b40b6194..8ad68937d2b 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated // Copyright Envoy Gateway Authors // SPDX-License-Identifier: Apache-2.0 @@ -11,8 +10,8 @@ package ir import ( - "github.com/envoyproxy/gateway/api/config/v1alpha1" - apiv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" + "k8s.io/apimachinery/pkg/apis/meta/v1" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -78,6 +77,143 @@ func (in *AddHeader) DeepCopy() *AddHeader { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuth) DeepCopyInto(out *BasicAuth) { + *out = *in + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth. +func (in *BasicAuth) DeepCopy() *BasicAuth { + if in == nil { + return nil + } + out := new(BasicAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CORS) DeepCopyInto(out *CORS) { + *out = *in + if in.AllowOrigins != nil { + in, out := &in.AllowOrigins, &out.AllowOrigins + *out = make([]*StringMatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + } + } + if in.AllowMethods != nil { + in, out := &in.AllowMethods, &out.AllowMethods + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AllowHeaders != nil { + in, out := &in.AllowHeaders, &out.AllowHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExposeHeaders != nil { + in, out := &in.ExposeHeaders, &out.ExposeHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MaxAge != nil { + in, out := &in.MaxAge, &out.MaxAge + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CORS. +func (in *CORS) DeepCopy() *CORS { + if in == nil { + return nil + } + out := new(CORS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConsistentHash) DeepCopyInto(out *ConsistentHash) { + *out = *in + if in.SourceIP != nil { + in, out := &in.SourceIP, &out.SourceIP + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsistentHash. +func (in *ConsistentHash) DeepCopy() *ConsistentHash { + if in == nil { + return nil + } + out := new(ConsistentHash) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DestinationEndpoint) DeepCopyInto(out *DestinationEndpoint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationEndpoint. +func (in *DestinationEndpoint) DeepCopy() *DestinationEndpoint { + if in == nil { + return nil + } + out := new(DestinationEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DestinationSetting) DeepCopyInto(out *DestinationSetting) { + *out = *in + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(uint32) + **out = **in + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]*DestinationEndpoint, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(DestinationEndpoint) + **out = **in + } + } + } + if in.AddressType != nil { + in, out := &in.AddressType, &out.AddressType + *out = new(DestinationAddressType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationSetting. +func (in *DestinationSetting) DeepCopy() *DestinationSetting { + if in == nil { + return nil + } + out := new(DestinationSetting) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DirectResponse) DeepCopyInto(out *DirectResponse) { *out = *in @@ -130,7 +266,7 @@ func (in *EnvoyPatchPolicyStatus) DeepCopyInto(out *EnvoyPatchPolicyStatus) { *out = *in if in.Status != nil { in, out := &in.Status, &out.Status - *out = new(apiv1alpha1.EnvoyPatchPolicyStatus) + *out = new(v1alpha1.EnvoyPatchPolicyStatus) (*in).DeepCopyInto(*out) } } @@ -201,6 +337,11 @@ func (in *HTTPListener) DeepCopyInto(out *HTTPListener) { } } } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPListener. @@ -310,16 +451,10 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { } } } - if in.Destinations != nil { - in, out := &in.Destinations, &out.Destinations - *out = make([]*RouteDestination, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(RouteDestination) - (*in).DeepCopyInto(*out) - } - } + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) } if in.URLRewrite != nil { in, out := &in.URLRewrite, &out.URLRewrite @@ -331,9 +466,39 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { *out = new(RateLimit) (*in).DeepCopyInto(*out) } - if in.RequestAuthentication != nil { - in, out := &in.RequestAuthentication, &out.RequestAuthentication - *out = new(RequestAuthentication) + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.LoadBalancer != nil { + in, out := &in.LoadBalancer, &out.LoadBalancer + *out = new(LoadBalancer) + (*in).DeepCopyInto(*out) + } + if in.CORS != nil { + in, out := &in.CORS, &out.CORS + *out = new(CORS) + (*in).DeepCopyInto(*out) + } + if in.JWT != nil { + in, out := &in.JWT, &out.JWT + *out = new(JWT) + (*in).DeepCopyInto(*out) + } + if in.OIDC != nil { + in, out := &in.OIDC, &out.OIDC + *out = new(OIDC) + (*in).DeepCopyInto(*out) + } + if in.ProxyProtocol != nil { + in, out := &in.ProxyProtocol, &out.ProxyProtocol + *out = new(ProxyProtocol) + **out = **in + } + if in.BasicAuth != nil { + in, out := &in.BasicAuth, &out.BasicAuth + *out = new(BasicAuth) (*in).DeepCopyInto(*out) } if in.ExtensionRefs != nil { @@ -382,6 +547,13 @@ func (in *Infra) DeepCopy() *Infra { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InfraMetadata) DeepCopyInto(out *InfraMetadata) { *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -456,23 +628,43 @@ func (in *JSONPatchOperation) DeepCopy() *JSONPatchOperation { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JwtRequestAuthentication) DeepCopyInto(out *JwtRequestAuthentication) { +func (in *JWT) DeepCopyInto(out *JWT) { *out = *in if in.Providers != nil { in, out := &in.Providers, &out.Providers - *out = make([]apiv1alpha1.JwtAuthenticationFilterProvider, len(*in)) + *out = make([]v1alpha1.JWTProvider, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtRequestAuthentication. -func (in *JwtRequestAuthentication) DeepCopy() *JwtRequestAuthentication { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWT. +func (in *JWT) DeepCopy() *JWT { + if in == nil { + return nil + } + out := new(JWT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LeastRequest) DeepCopyInto(out *LeastRequest) { + *out = *in + if in.SlowStart != nil { + in, out := &in.SlowStart, &out.SlowStart + *out = new(SlowStart) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeastRequest. +func (in *LeastRequest) DeepCopy() *LeastRequest { if in == nil { return nil } - out := new(JwtRequestAuthentication) + out := new(LeastRequest) in.DeepCopyInto(out) return out } @@ -492,6 +684,82 @@ func (in *ListenerPort) DeepCopy() *ListenerPort { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { + *out = *in + if in.RoundRobin != nil { + in, out := &in.RoundRobin, &out.RoundRobin + *out = new(RoundRobin) + (*in).DeepCopyInto(*out) + } + if in.LeastRequest != nil { + in, out := &in.LeastRequest, &out.LeastRequest + *out = new(LeastRequest) + (*in).DeepCopyInto(*out) + } + if in.Random != nil { + in, out := &in.Random, &out.Random + *out = new(Random) + **out = **in + } + if in.ConsistentHash != nil { + in, out := &in.ConsistentHash, &out.ConsistentHash + *out = new(ConsistentHash) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. +func (in *LoadBalancer) DeepCopy() *LoadBalancer { + if in == nil { + return nil + } + out := new(LoadBalancer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Metrics) DeepCopyInto(out *Metrics) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metrics. +func (in *Metrics) DeepCopy() *Metrics { + if in == nil { + return nil + } + out := new(Metrics) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDC) DeepCopyInto(out *OIDC) { + *out = *in + out.Provider = in.Provider + if in.ClientSecret != nil { + in, out := &in.ClientSecret, &out.ClientSecret + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.Scopes != nil { + in, out := &in.Scopes, &out.Scopes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDC. +func (in *OIDC) DeepCopy() *OIDC { + if in == nil { + return nil + } + out := new(OIDC) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenTelemetryAccessLog) DeepCopyInto(out *OpenTelemetryAccessLog) { *out = *in @@ -583,6 +851,36 @@ func (in *ProxyListener) DeepCopy() *ProxyListener { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyProtocol) DeepCopyInto(out *ProxyProtocol) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyProtocol. +func (in *ProxyProtocol) DeepCopy() *ProxyProtocol { + if in == nil { + return nil + } + out := new(ProxyProtocol) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Random) DeepCopyInto(out *Random) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Random. +func (in *Random) DeepCopy() *Random { + if in == nil { + return nil + } + out := new(Random) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RateLimit) DeepCopyInto(out *RateLimit) { *out = *in @@ -695,21 +993,21 @@ func (in *Redirect) DeepCopy() *Redirect { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RequestAuthentication) DeepCopyInto(out *RequestAuthentication) { +func (in *RoundRobin) DeepCopyInto(out *RoundRobin) { *out = *in - if in.JWT != nil { - in, out := &in.JWT, &out.JWT - *out = new(JwtRequestAuthentication) + if in.SlowStart != nil { + in, out := &in.SlowStart, &out.SlowStart + *out = new(SlowStart) (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestAuthentication. -func (in *RequestAuthentication) DeepCopy() *RequestAuthentication { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoundRobin. +func (in *RoundRobin) DeepCopy() *RoundRobin { if in == nil { return nil } - out := new(RequestAuthentication) + out := new(RoundRobin) in.DeepCopyInto(out) return out } @@ -717,10 +1015,16 @@ func (in *RequestAuthentication) DeepCopy() *RequestAuthentication { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouteDestination) DeepCopyInto(out *RouteDestination) { *out = *in - if in.Weight != nil { - in, out := &in.Weight, &out.Weight - *out = new(uint32) - **out = **in + if in.Settings != nil { + in, out := &in.Settings, &out.Settings + *out = make([]*DestinationSetting, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(DestinationSetting) + (*in).DeepCopyInto(*out) + } + } } } @@ -734,6 +1038,26 @@ func (in *RouteDestination) DeepCopy() *RouteDestination { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlowStart) DeepCopyInto(out *SlowStart) { + *out = *in + if in.Window != nil { + in, out := &in.Window, &out.Window + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowStart. +func (in *SlowStart) DeepCopy() *SlowStart { + if in == nil { + return nil + } + out := new(SlowStart) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StringMatch) DeepCopyInto(out *StringMatch) { *out = *in @@ -769,6 +1093,36 @@ func (in *StringMatch) DeepCopy() *StringMatch { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPKeepalive) DeepCopyInto(out *TCPKeepalive) { + *out = *in + if in.Probes != nil { + in, out := &in.Probes, &out.Probes + *out = new(uint32) + **out = **in + } + if in.IdleTime != nil { + in, out := &in.IdleTime, &out.IdleTime + *out = new(uint32) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(uint32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPKeepalive. +func (in *TCPKeepalive) DeepCopy() *TCPKeepalive { + if in == nil { + return nil + } + out := new(TCPKeepalive) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPListener) DeepCopyInto(out *TCPListener) { *out = *in @@ -777,16 +1131,15 @@ func (in *TCPListener) DeepCopyInto(out *TCPListener) { *out = new(TLS) (*in).DeepCopyInto(*out) } - if in.Destinations != nil { - in, out := &in.Destinations, &out.Destinations - *out = make([]*RouteDestination, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(RouteDestination) - (*in).DeepCopyInto(*out) - } - } + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) + } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) } } @@ -915,16 +1268,10 @@ func (in *Tracing) DeepCopy() *Tracing { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UDPListener) DeepCopyInto(out *UDPListener) { *out = *in - if in.Destinations != nil { - in, out := &in.Destinations, &out.Destinations - *out = make([]*RouteDestination, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(RouteDestination) - (*in).DeepCopyInto(*out) - } - } + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) } } @@ -995,6 +1342,11 @@ func (in *Xds) DeepCopyInto(out *Xds) { *out = new(Tracing) (*in).DeepCopyInto(*out) } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(Metrics) + **out = **in + } if in.HTTP != nil { in, out := &in.HTTP, &out.HTTP *out = make([]*HTTPListener, len(*in)) diff --git a/internal/kubernetes/secret.go b/internal/kubernetes/secret.go index 9a269c501e5..700c9b8a4d6 100644 --- a/internal/kubernetes/secret.go +++ b/internal/kubernetes/secret.go @@ -13,13 +13,13 @@ import ( corev1 "k8s.io/api/core/v1" k8smachinery "k8s.io/apimachinery/pkg/types" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/envoyproxy/gateway/internal/gatewayapi" ) // ValidateSecretObjectReference validate secret object reference for extension tls and ratelimit tls settings. -func ValidateSecretObjectReference(ctx context.Context, client k8sclient.Client, secretObjRef *gwapiv1b1.SecretObjectReference, namespace string) (*corev1.Secret, string, error) { +func ValidateSecretObjectReference(ctx context.Context, client k8sclient.Client, secretObjRef *gwapiv1.SecretObjectReference, namespace string) (*corev1.Secret, string, error) { if (secretObjRef.Group == nil || *secretObjRef.Group == corev1.GroupName) && (secretObjRef.Kind == nil || *secretObjRef.Kind == gatewayapi.KindSecret) { secret := &corev1.Secret{} diff --git a/internal/logging/log.go b/internal/logging/log.go index bb9a438678f..0f92491bca6 100644 --- a/internal/logging/log.go +++ b/internal/logging/log.go @@ -13,7 +13,7 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" ) type Logger struct { @@ -55,7 +55,7 @@ func (l Logger) WithName(name string) Logger { return Logger{ Logger: zapr.NewLogger(logger).WithName(name), logging: l.logging, - sugaredLogger: l.sugaredLogger, + sugaredLogger: logger.Sugar(), } } diff --git a/internal/logging/log_test.go b/internal/logging/log_test.go index 1cdd9c468e3..d71bee8b6b4 100644 --- a/internal/logging/log_test.go +++ b/internal/logging/log_test.go @@ -14,7 +14,7 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" ) func TestZapLogLevel(t *testing.T) { @@ -41,3 +41,32 @@ func TestLogger(t *testing.T) { assert.True(t, defaultLogger.logging != nil) assert.True(t, defaultLogger.sugaredLogger != nil) } + +func TestLoggerWithName(t *testing.T) { + originalStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + defer func() { + // Restore the original stdout and close the pipe + os.Stdout = originalStdout + err := w.Close() + assert.NoError(t, err) + }() + + config := v1alpha1.DefaultEnvoyGatewayLogging() + config.Level[v1alpha1.LogComponentInfrastructureRunner] = v1alpha1.LogLevelDebug + + logger := NewLogger(config).WithName(string(v1alpha1.LogComponentInfrastructureRunner)) + logger.Info("info message") + logger.Sugar().Debugf("debug message") + + // Read from the pipe (captured stdout) + outputBytes := make([]byte, 200) + _, err := r.Read(outputBytes) + assert.NoError(t, err) + capturedOutput := string(outputBytes) + assert.Contains(t, capturedOutput, string(v1alpha1.LogComponentInfrastructureRunner)) + assert.Contains(t, capturedOutput, "info message") + assert.Contains(t, capturedOutput, "debug message") +} diff --git a/internal/message/metrics.go b/internal/message/metrics.go new file mode 100644 index 00000000000..a78c9f87cce --- /dev/null +++ b/internal/message/metrics.go @@ -0,0 +1,22 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package message + +import "github.com/envoyproxy/gateway/internal/metrics" + +var ( + watchableDepth = metrics.NewGauge("watchable_depth", "Current depth of watchable queue.") + + watchableSubscribedDurationSeconds = metrics.NewHistogram("watchable_subscribed_duration_seconds", "How long in seconds a subscribed watchable is handled.", []float64{0.001, 0.01, 0.1, 1, 5, 10}) + + watchableSubscribedTotal = metrics.NewCounter("watchable_subscribed_total", "Total number of subscribed watchable.") + + watchableSubscribedErrorsTotal = metrics.NewCounter("watchable_subscribed_errors_total", "Total number of subscribed watchable errors.") + + runnerLabel = metrics.NewLabel("runner") + + messageLabel = metrics.NewLabel("message") +) diff --git a/internal/message/types.go b/internal/message/types.go index ef97667fade..1825a8f033e 100644 --- a/internal/message/types.go +++ b/internal/message/types.go @@ -8,8 +8,8 @@ package message import ( "github.com/telepresenceio/watchable" "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -20,15 +20,15 @@ import ( // ProviderResources message type ProviderResources struct { // GatewayAPIResources is a map from a GatewayClass name to - // a group of gateway API resources. + // a group of gateway API and other related resources. GatewayAPIResources watchable.Map[string, *gatewayapi.Resources] - GatewayStatuses watchable.Map[types.NamespacedName, *gwapiv1b1.GatewayStatus] - HTTPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRouteStatus] - GRPCRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.GRPCRouteStatus] - TLSRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.TLSRouteStatus] - TCPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.TCPRouteStatus] - UDPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.UDPRouteStatus] + // GatewayAPIStatuses is a group of gateway api + // resource statuses maps. + GatewayAPIStatuses + + // PolicyStatuses is a group of policy statuses maps. + PolicyStatuses } func (p *ProviderResources) GetResources() *gatewayapi.Resources { @@ -53,17 +53,41 @@ func (p *ProviderResources) GetResourcesKey() string { func (p *ProviderResources) Close() { p.GatewayAPIResources.Close() - p.GatewayStatuses.Close() - p.HTTPRouteStatuses.Close() - p.GRPCRouteStatuses.Close() - p.TLSRouteStatuses.Close() - p.TCPRouteStatuses.Close() - p.UDPRouteStatuses.Close() + p.GatewayAPIStatuses.Close() + p.PolicyStatuses.Close() +} + +// GatewayAPIStatuses contains gateway API resources statuses +type GatewayAPIStatuses struct { + GatewayStatuses watchable.Map[types.NamespacedName, *gwapiv1.GatewayStatus] + HTTPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1.HTTPRouteStatus] + GRPCRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.GRPCRouteStatus] + TLSRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.TLSRouteStatus] + TCPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.TCPRouteStatus] + UDPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1a2.UDPRouteStatus] +} + +func (s *GatewayAPIStatuses) Close() { + s.GatewayStatuses.Close() + s.HTTPRouteStatuses.Close() + s.GRPCRouteStatuses.Close() + s.TLSRouteStatuses.Close() + s.TCPRouteStatuses.Close() + s.UDPRouteStatuses.Close() +} + +// PolicyStatuses contains policy related resources statuses +type PolicyStatuses struct { + ClientTrafficPolicyStatuses watchable.Map[types.NamespacedName, *egv1a1.ClientTrafficPolicyStatus] + BackendTrafficPolicyStatuses watchable.Map[types.NamespacedName, *egv1a1.BackendTrafficPolicyStatus] + EnvoyPatchPolicyStatuses watchable.Map[types.NamespacedName, *egv1a1.EnvoyPatchPolicyStatus] + SecurityPolicyStatuses watchable.Map[types.NamespacedName, *egv1a1.SecurityPolicyStatus] } -// EnvoyPatchPolicyStatuses message -type EnvoyPatchPolicyStatuses struct { - watchable.Map[types.NamespacedName, *egv1a1.EnvoyPatchPolicyStatus] +func (p *PolicyStatuses) Close() { + p.ClientTrafficPolicyStatuses.Close() + p.SecurityPolicyStatuses.Close() + p.EnvoyPatchPolicyStatuses.Close() } // XdsIR message diff --git a/internal/message/watchutil.go b/internal/message/watchutil.go index 29cecc37a9f..b938f276b7d 100644 --- a/internal/message/watchutil.go +++ b/internal/message/watchutil.go @@ -6,11 +6,36 @@ package message import ( + "time" + "github.com/telepresenceio/watchable" + + "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/logging" + "github.com/envoyproxy/gateway/internal/metrics" ) type Update[K comparable, V any] watchable.Update[K, V] +var logger = logging.DefaultLogger(v1alpha1.LogLevelInfo).WithName("watchable") + +type Metadata struct { + Runner string + Message string +} + +func (m Metadata) LabelValues() []metrics.LabelValue { + labels := make([]metrics.LabelValue, 0, 2) + if m.Runner != "" { + labels = append(labels, runnerLabel.Value(m.Runner)) + } + if m.Message != "" { + labels = append(labels, messageLabel.Value(m.Message)) + } + + return labels +} + // HandleSubscription takes a channel returned by // watchable.Map.Subscribe() (or .SubscribeSubset()), and calls the // given function for each initial value in the map, and for any @@ -20,20 +45,37 @@ type Update[K comparable, V any] watchable.Update[K, V] // it handles the case where the watchable.Map already contains // entries before .Subscribe is called. func HandleSubscription[K comparable, V any]( + meta Metadata, subscription <-chan watchable.Snapshot[K, V], - handle func(Update[K, V]), + handle func(updateFunc Update[K, V], errChans chan error), ) { + //TODO: find a suitable value + errChans := make(chan error, 10) + go func() { + for err := range errChans { + logger.WithValues("runner", meta.Runner).Error(err, "observed an error") + watchableSubscribedErrorsTotal.With(meta.LabelValues()...).Increment() + } + }() + if snapshot, ok := <-subscription; ok { for k, v := range snapshot.State { + startHandleTime := time.Now() handle(Update[K, V]{ Key: k, Value: v, - }) + }, errChans) + watchableSubscribedTotal.With(meta.LabelValues()...).Increment() + watchableSubscribedDurationSeconds.With(meta.LabelValues()...).Record(time.Since(startHandleTime).Seconds()) } } for snapshot := range subscription { + watchableDepth.With(meta.LabelValues()...).Record(float64(len(subscription))) for _, update := range snapshot.Updates { - handle(Update[K, V](update)) + startHandleTime := time.Now() + handle(Update[K, V](update), errChans) + watchableSubscribedTotal.With(meta.LabelValues()...).Increment() + watchableSubscribedDurationSeconds.With(meta.LabelValues()...).Record(time.Since(startHandleTime).Seconds()) } } } diff --git a/internal/message/watchutil_test.go b/internal/message/watchutil_test.go index 873c8b2d53c..8e76a2554be 100644 --- a/internal/message/watchutil_test.go +++ b/internal/message/watchutil_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/telepresenceio/watchable" + "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/message" ) @@ -22,8 +23,9 @@ func TestHandleSubscriptionAlreadyClosed(t *testing.T) { var calls int message.HandleSubscription[string, any]( + message.Metadata{Runner: "demo", Message: "demo"}, ch, - func(message.Update[string, any]) { calls++ }, + func(update message.Update[string, any], errChans chan error) { calls++ }, ) assert.Equal(t, 0, calls) } @@ -46,8 +48,9 @@ func TestHandleSubscriptionAlreadyInitialized(t *testing.T) { var storeCalls int var deleteCalls int message.HandleSubscription[string, any]( + message.Metadata{Runner: "demo", Message: "demo"}, m.Subscribe(context.Background()), - func(update message.Update[string, any]) { + func(update message.Update[string, any], errChans chan error) { end() if update.Delete { deleteCalls++ @@ -59,3 +62,77 @@ func TestHandleSubscriptionAlreadyInitialized(t *testing.T) { assert.Equal(t, 2, storeCalls) assert.Equal(t, 1, deleteCalls) } + +func TestXdsIRUpdates(t *testing.T) { + tests := []struct { + desc string + xx []*ir.Xds + updates int + }{ + { + desc: "HTTP listener order change skips update", + xx: []*ir.Xds{ + { + HTTP: []*ir.HTTPListener{ + {Name: "listener-1"}, + {Name: "listener-2"}, + }, + }, + { + HTTP: []*ir.HTTPListener{ + {Name: "listener-2"}, + {Name: "listener-1"}, + }, + }, + }, + updates: 1, + }, + { + desc: "Additional HTTP listener triggers update", + xx: []*ir.Xds{ + { + HTTP: []*ir.HTTPListener{ + {Name: "listener-1"}, + }, + }, + { + HTTP: []*ir.HTTPListener{ + {Name: "listener-1"}, + {Name: "listener-2"}, + }, + }, + }, + updates: 2, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + ctx := context.Background() + m := new(message.XdsIR) + + snapshotC := m.Subscribe(ctx) + endCtx, end := context.WithCancel(ctx) + m.Store("start", &ir.Xds{}) + + go func() { + <-endCtx.Done() + for _, x := range tc.xx { + m.Store("test", x) + } + m.Store("end", &ir.Xds{}) + }() + + updates := 0 + message.HandleSubscription(message.Metadata{Runner: "demo", Message: "demo"}, snapshotC, func(u message.Update[string, *ir.Xds], errChans chan error) { + end() + if u.Key == "test" { + updates += 1 + } + if u.Key == "end" { + m.Close() + } + }) + assert.Equal(t, tc.updates, updates) + }) + } +} diff --git a/internal/metrics/metadata.go b/internal/metrics/metadata.go new file mode 100644 index 00000000000..f2ab8498407 --- /dev/null +++ b/internal/metrics/metadata.go @@ -0,0 +1,102 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import ( + "errors" + "sync" + + "go.opentelemetry.io/otel" + api "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/envoyproxy/gateway/api/v1alpha1" + log "github.com/envoyproxy/gateway/internal/logging" +) + +var ( + meter = func() api.Meter { + return otel.GetMeterProvider().Meter("envoy-gateway") + } + + metricsLogger = log.DefaultLogger(v1alpha1.LogLevelInfo).WithName("metrics") +) + +func init() { + otel.SetLogger(metricsLogger.Logger) +} + +// MetricType is the type of a metric. +type MetricType string + +// Metric type supports: +// * Counter: A Counter is a simple metric that only goes up (increments). +// +// * Gauge: A Gauge is a metric that represent +// a single numerical value that can arbitrarily go up and down. +// +// * Histogram: A Histogram samples observations and counts them in configurable buckets. +// It also provides a sum of all observed values. +// It's used to visualize the statistical distribution of these observations. + +const ( + CounterType MetricType = "Counter" + GaugeType MetricType = "Gauge" + HistogramType MetricType = "Histogram" +) + +// Metadata records a metric's metadata. +type Metadata struct { + Name string + Type MetricType + Description string + Bounds []float64 +} + +// metrics stores stores metrics +type store struct { + started bool + mu sync.Mutex + stores map[string]Metadata +} + +// stores is a global that stores all registered metrics +var stores = store{ + stores: map[string]Metadata{}, +} + +// register records a newly defined metric. Only valid before an exporter is set. +func (d *store) register(store Metadata) { + d.mu.Lock() + defer d.mu.Unlock() + if d.started { + metricsLogger.Error(errors.New("cannot initialize metric after metric has started"), "metric", store.Name) + } + d.stores[store.Name] = store +} + +// preAddOptions runs pre-run steps before adding to meter provider. +func (d *store) preAddOptions() []metric.Option { + d.mu.Lock() + defer d.mu.Unlock() + d.started = true + opts := []metric.Option{} + for name, store := range d.stores { + if store.Bounds == nil { + continue + } + // for each histogram metric (i.e. those with bounds), set up a view explicitly defining those buckets. + v := metric.WithView(metric.NewView( + metric.Instrument{Name: name}, + metric.Stream{ + Aggregation: metric.AggregationExplicitBucketHistogram{ + Boundaries: store.Bounds, + }}, + )) + opts = append(opts, v) + } + return opts +} diff --git a/internal/metrics/options.go b/internal/metrics/options.go new file mode 100644 index 00000000000..f274582f459 --- /dev/null +++ b/internal/metrics/options.go @@ -0,0 +1,31 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +// Options encode changes to the options passed to a Metric at creation time. +type MetricOption func(*MetricOptions) + +type MetricOptions struct { + Unit Unit + Name string + Description string +} + +// WithUnit provides configuration options for a new Metric, providing unit of measure +// information for a new Metric. +func WithUnit(unit Unit) MetricOption { + return func(opts *MetricOptions) { + opts.Unit = unit + } +} + +func metricOptions(name, description string, opts ...MetricOption) MetricOptions { + o := MetricOptions{Unit: None, Name: name, Description: description} + for _, opt := range opts { + opt(&o) + } + return o +} diff --git a/internal/metrics/otel_label.go b/internal/metrics/otel_label.go new file mode 100644 index 00000000000..45d04ff4bb0 --- /dev/null +++ b/internal/metrics/otel_label.go @@ -0,0 +1,48 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import "go.opentelemetry.io/otel/attribute" + +// A Label provides a named dimension for a Metric. +type Label struct { + key attribute.Key +} + +// NewLabel will attempt to create a new Label. +func NewLabel(key string) Label { + return Label{attribute.Key(key)} +} + +// Value creates a new LabelValue for the Label. +func (l Label) Value(value string) LabelValue { + return LabelValue{l.key.String(value)} +} + +// A LabelValue represents a Label with a specific value. It is used to record +// values for a Metric. +type LabelValue struct { + keyValue attribute.KeyValue +} + +func (l LabelValue) Key() Label { + return Label{l.keyValue.Key} +} + +func (l LabelValue) Value() string { + return l.keyValue.Value.AsString() +} + +func mergeLabelValues(attrs []attribute.KeyValue, labelValues []LabelValue) ([]attribute.KeyValue, attribute.Set) { + mergedAttrs := make([]attribute.KeyValue, 0, len(attrs)+len(labelValues)) + mergedAttrs = append(mergedAttrs, attrs...) + for _, v := range labelValues { + kv := v + mergedAttrs = append(mergedAttrs, kv.keyValue) + } + + return mergedAttrs, attribute.NewSet(mergedAttrs...) +} diff --git a/internal/metrics/otel_metric_counter.go b/internal/metrics/otel_metric_counter.go new file mode 100644 index 00000000000..93dcaa13650 --- /dev/null +++ b/internal/metrics/otel_metric_counter.go @@ -0,0 +1,48 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" + api "go.opentelemetry.io/otel/metric" +) + +type Counter struct { + name string + attrs []attribute.KeyValue + c api.Float64Counter + preRecordOptions []api.AddOption +} + +func (f *Counter) Add(value float64) { + if f.preRecordOptions != nil { + f.c.Add(context.Background(), value, f.preRecordOptions...) + } else { + f.c.Add(context.Background(), value) + } +} + +func (f *Counter) Increment() { + f.Add(1) +} + +func (f *Counter) Decrement() { + f.Add(-1) +} + +func (f *Counter) With(labelValues ...LabelValue) *Counter { + attrs, set := mergeLabelValues(f.attrs, labelValues) + m := &Counter{ + c: f.c, + preRecordOptions: []api.AddOption{api.WithAttributeSet(set)}, + name: f.name, + attrs: attrs, + } + + return m +} diff --git a/internal/metrics/otel_metric_gauge.go b/internal/metrics/otel_metric_gauge.go new file mode 100644 index 00000000000..49e02395b67 --- /dev/null +++ b/internal/metrics/otel_metric_gauge.go @@ -0,0 +1,57 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import ( + "sync" + + "go.opentelemetry.io/otel/attribute" + api "go.opentelemetry.io/otel/metric" +) + +type Gauge struct { + name string + attrs []attribute.KeyValue + + g api.Float64ObservableGauge + mutex *sync.RWMutex + stores map[attribute.Set]*GaugeValues + current *GaugeValues +} + +type GaugeValues struct { + val float64 + opt []api.ObserveOption +} + +func (f *Gauge) Record(value float64) { + f.mutex.Lock() + if f.current == nil { + f.current = &GaugeValues{} + f.stores[attribute.NewSet()] = f.current + } + f.current.val = value + f.mutex.Unlock() +} + +func (f *Gauge) With(labelValues ...LabelValue) *Gauge { + attrs, set := mergeLabelValues(f.attrs, labelValues) + m := &Gauge{ + g: f.g, + mutex: f.mutex, + stores: f.stores, + name: f.name, + attrs: attrs, + } + if _, f := m.stores[set]; !f { + m.stores[set] = &GaugeValues{ + opt: []api.ObserveOption{api.WithAttributeSet(set)}, + } + } + m.current = m.stores[set] + + return m +} diff --git a/internal/metrics/otel_metric_histogram.go b/internal/metrics/otel_metric_histogram.go new file mode 100644 index 00000000000..b1837b7a8d2 --- /dev/null +++ b/internal/metrics/otel_metric_histogram.go @@ -0,0 +1,41 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" + api "go.opentelemetry.io/otel/metric" +) + +type Histogram struct { + name string + attrs []attribute.KeyValue + + d api.Float64Histogram + preRecordOptions []api.RecordOption +} + +func (f *Histogram) Record(value float64) { + if f.preRecordOptions != nil { + f.d.Record(context.Background(), value, f.preRecordOptions...) + } else { + f.d.Record(context.Background(), value) + } +} + +func (f *Histogram) With(labelValues ...LabelValue) *Histogram { + attrs, set := mergeLabelValues(f.attrs, labelValues) + m := &Histogram{ + name: f.name, + attrs: attrs, + d: f.d, + preRecordOptions: []api.RecordOption{api.WithAttributeSet(set)}, + } + + return m +} diff --git a/internal/metrics/otel_metric_sink.go b/internal/metrics/otel_metric_sink.go new file mode 100644 index 00000000000..442f0c0d3c0 --- /dev/null +++ b/internal/metrics/otel_metric_sink.go @@ -0,0 +1,100 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import ( + "context" + "sync" + + "go.opentelemetry.io/otel/attribute" + api "go.opentelemetry.io/otel/metric" +) + +// NewCounter creates a new Counter Metric (the values will be cumulative). +// That means that data collected by the new Metric will be summed before export. +func NewCounter(name, description string, opts ...MetricOption) *Counter { + stores.register(Metadata{ + Name: name, + Type: CounterType, + Description: description, + }) + o := metricOptions(name, description, opts...) + + return newCounter(o) +} + +// NewGauge creates a new Gauge Metric. That means that data collected by the new +// Metric will export only the last recorded value. +func NewGauge(name, description string, opts ...MetricOption) *Gauge { + stores.register(Metadata{ + Name: name, + Type: GaugeType, + Description: description, + }) + o := metricOptions(name, description, opts...) + + return newGauge(o) +} + +// NewHistogram creates a new Metric with an aggregation type of Histogram. +// This means that the data collected by the Metric will be collected and exported as a histogram, with the specified bounds. +func NewHistogram(name, description string, bounds []float64, opts ...MetricOption) *Histogram { + stores.register(Metadata{ + Name: name, + Type: HistogramType, + Description: description, + Bounds: bounds, + }) + o := metricOptions(name, description, opts...) + + return newHistogram(o) +} + +func newCounter(o MetricOptions) *Counter { + c, err := meter().Float64Counter(o.Name, + api.WithDescription(o.Description), + api.WithUnit(string(o.Unit))) + if err != nil { + metricsLogger.Error(err, "failed to create otel Counter") + } + m := &Counter{c: c, name: o.Name} + + return m +} + +func newGauge(o MetricOptions) *Gauge { + r := &Gauge{mutex: &sync.RWMutex{}, name: o.Name} + r.stores = map[attribute.Set]*GaugeValues{} + g, err := meter().Float64ObservableGauge(o.Name, + api.WithFloat64Callback(func(ctx context.Context, observer api.Float64Observer) error { + r.mutex.Lock() + defer r.mutex.Unlock() + for _, gv := range r.stores { + observer.Observe(gv.val, gv.opt...) + } + return nil + }), + api.WithDescription(o.Description), + api.WithUnit(string(o.Unit))) + if err != nil { + metricsLogger.Error(err, "failed to create otel Gauge") + } + r.g = g + + return r +} + +func newHistogram(o MetricOptions) *Histogram { + d, err := meter().Float64Histogram(o.Name, + api.WithDescription(o.Description), + api.WithUnit(string(o.Unit))) + if err != nil { + metricsLogger.Error(err, "failed to create otel Histogram") + } + m := &Histogram{d: d, name: o.Name} + + return m +} diff --git a/internal/metrics/register.go b/internal/metrics/register.go new file mode 100644 index 00000000000..9c8abdf479d --- /dev/null +++ b/internal/metrics/register.go @@ -0,0 +1,207 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +import ( + "context" + "fmt" + "net" + "net/http" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + otelprom "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/envoygateway/config" +) + +const ( + defaultEndpoint = "/metrics" +) + +// Init initializes and registers the global metrics server. +func Init(cfg *config.Server) error { + options := newOptions(cfg) + handler, err := registerForHandler(options) + if err != nil { + return err + } + + if !options.pullOptions.disable { + return start(options.address, handler) + } + + return nil +} + +func start(address string, handler http.Handler) error { + handlers := http.NewServeMux() + + metricsLogger.Info("starting metrics server", "address", address) + if handler != nil { + handlers.Handle(defaultEndpoint, handler) + } + + metricsServer := &http.Server{ + Handler: handlers, + Addr: address, + ReadTimeout: 5 * time.Second, + ReadHeaderTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 15 * time.Second, + } + + // Listen And Serve Metrics Server. + go func() { + if err := metricsServer.ListenAndServe(); err != nil { + metricsLogger.Error(err, "start metrics server failed") + } + }() + + return nil +} + +func newOptions(svr *config.Server) registerOptions { + newOpts := registerOptions{} + newOpts.address = net.JoinHostPort(v1alpha1.GatewayMetricsHost, fmt.Sprint(v1alpha1.GatewayMetricsPort)) + + if svr.EnvoyGateway.DisablePrometheus() { + newOpts.pullOptions.disable = true + } else { + newOpts.pullOptions.disable = false + newOpts.pullOptions.registry = metricsserver.Registry + newOpts.pullOptions.gatherer = metricsserver.Registry + } + + for _, config := range svr.EnvoyGateway.GetEnvoyGatewayTelemetry().Metrics.Sinks { + newOpts.pushOptions.sinks = append(newOpts.pushOptions.sinks, metricsSink{ + host: config.OpenTelemetry.Host, + port: config.OpenTelemetry.Port, + protocol: config.OpenTelemetry.Protocol, + }) + } + + return newOpts +} + +// registerForHandler sets the global metrics registry to the provided Prometheus registerer. +// if enables prometheus, it will return a prom http handler. +func registerForHandler(opts registerOptions) (http.Handler, error) { + otelOpts := []metric.Option{} + + if err := registerOTELPromExporter(&otelOpts, opts); err != nil { + return nil, err + } + if err := registerOTELHTTPexporter(&otelOpts, opts); err != nil { + return nil, err + } + if err := registerOTELgRPCexporter(&otelOpts, opts); err != nil { + return nil, err + } + otelOpts = append(otelOpts, stores.preAddOptions()...) + + mp := metric.NewMeterProvider(otelOpts...) + otel.SetMeterProvider(mp) + + if !opts.pullOptions.disable { + return promhttp.HandlerFor(opts.pullOptions.gatherer, promhttp.HandlerOpts{}), nil + } + return nil, nil +} + +// registerOTELPromExporter registers OTEL prometheus exporter (PULL mode). +func registerOTELPromExporter(otelOpts *[]metric.Option, opts registerOptions) error { + if !opts.pullOptions.disable { + promOpts := []otelprom.Option{ + otelprom.WithoutScopeInfo(), + otelprom.WithoutTargetInfo(), + otelprom.WithoutUnits(), + otelprom.WithRegisterer(opts.pullOptions.registry), + otelprom.WithoutCounterSuffixes(), + } + promreader, err := otelprom.New(promOpts...) + if err != nil { + return err + } + + *otelOpts = append(*otelOpts, metric.WithReader(promreader)) + metricsLogger.Info("initialized metrics pull endpoint", "address", opts.address, "endpoint", defaultEndpoint) + } + + return nil +} + +// registerOTELHTTPexporter registers OTEL HTTP metrics exporter (PUSH mode). +func registerOTELHTTPexporter(otelOpts *[]metric.Option, opts registerOptions) error { + for _, sink := range opts.pushOptions.sinks { + if sink.protocol == v1alpha1.HTTPProtocol { + address := net.JoinHostPort(sink.host, fmt.Sprint(sink.port)) + httpexporter, err := otlpmetrichttp.New( + context.Background(), + otlpmetrichttp.WithEndpoint(address), + otlpmetrichttp.WithInsecure(), + ) + if err != nil { + return err + } + + otelreader := metric.NewPeriodicReader(httpexporter) + *otelOpts = append(*otelOpts, metric.WithReader(otelreader)) + metricsLogger.Info("initialized otel http metrics push endpoint", "address", address) + } + } + + return nil +} + +// registerOTELgRPCexporter registers OTEL gRPC metrics exporter (PUSH mode). +func registerOTELgRPCexporter(otelOpts *[]metric.Option, opts registerOptions) error { + for _, sink := range opts.pushOptions.sinks { + if sink.protocol == v1alpha1.GRPCProtocol { + address := net.JoinHostPort(sink.host, fmt.Sprint(sink.port)) + httpexporter, err := otlpmetricgrpc.New( + context.Background(), + otlpmetricgrpc.WithEndpoint(address), + otlpmetricgrpc.WithInsecure(), + ) + if err != nil { + return err + } + + otelreader := metric.NewPeriodicReader(httpexporter) + *otelOpts = append(*otelOpts, metric.WithReader(otelreader)) + metricsLogger.Info("initialized otel grpc metrics push endpoint", "address", address) + } + } + + return nil +} + +type registerOptions struct { + address string + pullOptions struct { + registry prometheus.Registerer + gatherer prometheus.Gatherer + disable bool + } + pushOptions struct { + sinks []metricsSink + } +} + +type metricsSink struct { + protocol string + host string + port int32 +} diff --git a/internal/metrics/sample_counter_test.go b/internal/metrics/sample_counter_test.go new file mode 100644 index 00000000000..ffd3a18aac3 --- /dev/null +++ b/internal/metrics/sample_counter_test.go @@ -0,0 +1,23 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics_test + +import "github.com/envoyproxy/gateway/internal/metrics" + +var ( + irUpdates = metrics.NewCounter( + "ir_updates_total", + "Number of IR updates, by ir type", + ) +) + +func NewCounter() { + // increment on every xds ir update + irUpdates.With(irType.Value("xds")).Increment() + + // xds ir updates double + irUpdates.With(irType.Value("xds")).Add(2) +} diff --git a/internal/metrics/sample_gauge_test.go b/internal/metrics/sample_gauge_test.go new file mode 100644 index 00000000000..6b287ed9ca1 --- /dev/null +++ b/internal/metrics/sample_gauge_test.go @@ -0,0 +1,27 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics_test + +import "github.com/envoyproxy/gateway/internal/metrics" + +var ( + irType = metrics.NewLabel("ir-type") + currentIRsNum = metrics.NewGauge( + "current_irs_queue_num", + "current number of ir in queue, by ir type", + ) +) + +func NewGauge() { + // only the last recorded value (2) will be exported for this gauge + currentIRsNum.With(irType.Value("xds")).Record(1) + currentIRsNum.With(irType.Value("xds")).Record(3) + currentIRsNum.With(irType.Value("xds")).Record(2) + + currentIRsNum.With(irType.Value("infra")).Record(1) + currentIRsNum.With(irType.Value("infra")).Record(3) + currentIRsNum.With(irType.Value("infra")).Record(2) +} diff --git a/internal/metrics/sample_histogram_test.go b/internal/metrics/sample_histogram_test.go new file mode 100644 index 00000000000..b34658fcbe5 --- /dev/null +++ b/internal/metrics/sample_histogram_test.go @@ -0,0 +1,23 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics_test + +import "github.com/envoyproxy/gateway/internal/metrics" + +var ( + method = metrics.NewLabel("method") + + sentBytes = metrics.NewHistogram( + "sent_bytes_total", + "Histogram of sent bytes by method", + []float64{10, 50, 100, 1000, 10000}, + metrics.WithUnit(metrics.Bytes), + ) +) + +func NewHistogram() { + sentBytes.With(method.Value("/request/path/1")).Record(458) +} diff --git a/internal/metrics/units.go b/internal/metrics/units.go new file mode 100644 index 00000000000..1c7b5ff13c2 --- /dev/null +++ b/internal/metrics/units.go @@ -0,0 +1,18 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package metrics + +// Unit encodes the standard name for describing the quantity +// measured by a Metric (if applicable). +type Unit string + +// Predefined units for use with the metrics package. +const ( + None Unit = "1" + Bytes Unit = "By" + Seconds Unit = "s" + Milliseconds Unit = "ms" +) diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 1e26fd0bde3..7eee07267c4 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -17,6 +17,7 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/discovery" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -24,12 +25,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" - "github.com/envoyproxy/gateway/api/config/v1alpha1/validation" - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1/validation" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/logging" @@ -40,40 +42,39 @@ import ( ) const ( - classGatewayIndex = "classGatewayIndex" - gatewayTLSRouteIndex = "gatewayTLSRouteIndex" - gatewayHTTPRouteIndex = "gatewayHTTPRouteIndex" - gatewayGRPCRouteIndex = "gatewayGRPCRouteIndex" - gatewayTCPRouteIndex = "gatewayTCPRouteIndex" - gatewayUDPRouteIndex = "gatewayUDPRouteIndex" - secretGatewayIndex = "secretGatewayIndex" - targetRefGrantRouteIndex = "targetRefGrantRouteIndex" - serviceHTTPRouteIndex = "serviceHTTPRouteIndex" - serviceGRPCRouteIndex = "serviceGRPCRouteIndex" - serviceTLSRouteIndex = "serviceTLSRouteIndex" - serviceTCPRouteIndex = "serviceTCPRouteIndex" - serviceUDPRouteIndex = "serviceUDPRouteIndex" - authenFilterHTTPRouteIndex = "authenHTTPRouteIndex" - rateLimitFilterHTTPRouteIndex = "rateLimitHTTPRouteIndex" + classGatewayIndex = "classGatewayIndex" + gatewayTLSRouteIndex = "gatewayTLSRouteIndex" + gatewayHTTPRouteIndex = "gatewayHTTPRouteIndex" + gatewayGRPCRouteIndex = "gatewayGRPCRouteIndex" + gatewayTCPRouteIndex = "gatewayTCPRouteIndex" + gatewayUDPRouteIndex = "gatewayUDPRouteIndex" + secretGatewayIndex = "secretGatewayIndex" + targetRefGrantRouteIndex = "targetRefGrantRouteIndex" + backendHTTPRouteIndex = "backendHTTPRouteIndex" + backendGRPCRouteIndex = "backendGRPCRouteIndex" + backendTLSRouteIndex = "backendTLSRouteIndex" + backendTCPRouteIndex = "backendTCPRouteIndex" + backendUDPRouteIndex = "backendUDPRouteIndex" ) type gatewayAPIReconciler struct { client client.Client log logging.Logger statusUpdater status.Updater - classController gwapiv1b1.GatewayController + classController gwapiv1.GatewayController store *kubernetesProviderStore namespace string - envoyGateway *egcfgv1a1.EnvoyGateway + namespaceLabels []string + envoyGateway *v1alpha1.EnvoyGateway + mergeGateways bool - resources *message.ProviderResources - envoyPatchPolicyStatuses *message.EnvoyPatchPolicyStatuses - extGVKs []schema.GroupVersionKind + resources *message.ProviderResources + extGVKs []schema.GroupVersionKind } // newGatewayAPIController func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status.Updater, - resources *message.ProviderResources, eStatuses *message.EnvoyPatchPolicyStatuses) error { + resources *message.ProviderResources) error { ctx := context.Background() // Gather additional resources to watch from registered extensions @@ -85,17 +86,27 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. } } + var namespaceLabels []string + byNamespaceSelector := cfg.EnvoyGateway.Provider != nil && + cfg.EnvoyGateway.Provider.Kubernetes != nil && + cfg.EnvoyGateway.Provider.Kubernetes.Watch != nil && + cfg.EnvoyGateway.Provider.Kubernetes.Watch.Type == v1alpha1.KubernetesWatchModeTypeNamespaceSelectors && + len(cfg.EnvoyGateway.Provider.Kubernetes.Watch.NamespaceSelectors) != 0 + if byNamespaceSelector { + namespaceLabels = cfg.EnvoyGateway.Provider.Kubernetes.Watch.NamespaceSelectors + } + r := &gatewayAPIReconciler{ - client: mgr.GetClient(), - log: cfg.Logger, - classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName), - namespace: cfg.Namespace, - statusUpdater: su, - resources: resources, - envoyPatchPolicyStatuses: eStatuses, - extGVKs: extGVKs, - store: newProviderStore(), - envoyGateway: cfg.EnvoyGateway, + client: mgr.GetClient(), + log: cfg.Logger, + classController: gwapiv1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName), + namespace: cfg.Namespace, + namespaceLabels: namespaceLabels, + statusUpdater: su, + resources: resources, + extGVKs: extGVKs, + store: newProviderStore(), + envoyGateway: cfg.EnvoyGateway, } c, err := controller.New("gatewayapi", mgr, controller.Options{Reconciler: r}) @@ -117,16 +128,10 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. type resourceMappings struct { // Map for storing namespaces for Route, Service and Gateway objects. allAssociatedNamespaces map[string]struct{} - // Map for storing service NamespaceNames referred by various Route objects. - allAssociatedBackendRefs map[types.NamespacedName]struct{} + // Map for storing backendRefs' NamespaceNames referred by various Route objects. + allAssociatedBackendRefs map[gwapiv1.BackendObjectReference]struct{} // Map for storing referenceGrant NamespaceNames for BackendRefs, SecretRefs. - allAssociatedRefGrants map[types.NamespacedName]*gwapiv1a2.ReferenceGrant - // authenFilters is a map of AuthenticationFilters, where the key is the - // namespaced name of the AuthenticationFilter. - authenFilters map[types.NamespacedName]*egv1a1.AuthenticationFilter - // rateLimitFilters is a map of RateLimitFilters, where the key is the - // namespaced name of the RateLimitFilter. - rateLimitFilters map[types.NamespacedName]*egv1a1.RateLimitFilter + allAssociatedRefGrants map[types.NamespacedName]*gwapiv1b1.ReferenceGrant // extensionRefFilters is a map of filters managed by an extension. // The key is the namespaced name of the filter and the value is the // unstructured form of the resource. @@ -136,18 +141,19 @@ type resourceMappings struct { func newResourceMapping() *resourceMappings { return &resourceMappings{ allAssociatedNamespaces: map[string]struct{}{}, - allAssociatedBackendRefs: map[types.NamespacedName]struct{}{}, - allAssociatedRefGrants: map[types.NamespacedName]*gwapiv1a2.ReferenceGrant{}, - authenFilters: map[types.NamespacedName]*egv1a1.AuthenticationFilter{}, - rateLimitFilters: map[types.NamespacedName]*egv1a1.RateLimitFilter{}, + allAssociatedBackendRefs: map[gwapiv1.BackendObjectReference]struct{}{}, + allAssociatedRefGrants: map[types.NamespacedName]*gwapiv1b1.ReferenceGrant{}, extensionRefFilters: map[types.NamespacedName]unstructured.Unstructured{}, } } -func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { - r.log.WithName(request.Name).Info("reconciling object", "namespace", request.Namespace, "name", request.Name) +// Reconcile handles reconciling all resources in a single call. Any resource event should enqueue the +// same reconcile.Request containing the gateway controller name. This allows multiple resource updates to +// be handled by a single call to Reconcile. The reconcile.Request DOES NOT map to a specific resource. +func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Request) (reconcile.Result, error) { + r.log.Info("reconciling gateways") - var gatewayClasses gwapiv1b1.GatewayClassList + var gatewayClasses gwapiv1.GatewayClassList if err := r.client.List(ctx, &gatewayClasses); err != nil { return reconcile.Result{}, fmt.Errorf("error listing gatewayclasses: %v", err) } @@ -197,39 +203,59 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. return reconcile.Result{}, err } - for serviceNamespaceName := range resourceMap.allAssociatedBackendRefs { - r.log.Info("processing Service", "namespace", serviceNamespaceName.Namespace, - "name", serviceNamespaceName.Name) + for backendRef := range resourceMap.allAssociatedBackendRefs { + backendRefKind := gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService) + r.log.Info("processing Backend", "kind", backendRefKind, "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) - service := new(corev1.Service) - err := r.client.Get(ctx, serviceNamespaceName, service) - if err != nil { - r.log.Error(err, "failed to get Service", "namespace", serviceNamespaceName.Namespace, - "name", serviceNamespaceName.Name) - } else { - resourceMap.allAssociatedNamespaces[service.Namespace] = struct{}{} - resourceTree.Services = append(resourceTree.Services, service) - r.log.Info("added Service to resource tree", "namespace", serviceNamespaceName.Namespace, - "name", serviceNamespaceName.Name) - - // Retrieve the EndpointSlices associated with the service - endpointSliceList := new(discoveryv1.EndpointSliceList) - opts := []client.ListOption{ - client.MatchingLabels(map[string]string{ - discoveryv1.LabelServiceName: serviceNamespaceName.Name, - }), - client.InNamespace(serviceNamespaceName.Namespace), + var endpointSliceLabelKey string + switch backendRefKind { + case gatewayapi.KindService: + service := new(corev1.Service) + err := r.client.Get(ctx, types.NamespacedName{Namespace: string(*backendRef.Namespace), Name: string(backendRef.Name)}, service) + if err != nil { + r.log.Error(err, "failed to get Service", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) + } else { + resourceMap.allAssociatedNamespaces[service.Namespace] = struct{}{} + resourceTree.Services = append(resourceTree.Services, service) + r.log.Info("added Service to resource tree", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) } - if err := r.client.List(ctx, endpointSliceList, opts...); err != nil { - r.log.Error(err, "failed to get EndpointSlices", "namespace", serviceNamespaceName.Namespace, - "service", serviceNamespaceName.Name) + endpointSliceLabelKey = discoveryv1.LabelServiceName + + case gatewayapi.KindServiceImport: + serviceImport := new(mcsapi.ServiceImport) + err := r.client.Get(ctx, types.NamespacedName{Namespace: string(*backendRef.Namespace), Name: string(backendRef.Name)}, serviceImport) + if err != nil { + r.log.Error(err, "failed to get ServiceImport", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) } else { - for _, endpointSlice := range endpointSliceList.Items { - endpointSlice := endpointSlice - r.log.Info("added EndpointSlice to resource tree", "namespace", endpointSlice.Namespace, - "name", endpointSlice.Name) - resourceTree.EndpointSlices = append(resourceTree.EndpointSlices, &endpointSlice) - } + resourceMap.allAssociatedNamespaces[serviceImport.Namespace] = struct{}{} + resourceTree.ServiceImports = append(resourceTree.ServiceImports, serviceImport) + r.log.Info("added ServiceImport to resource tree", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) + } + endpointSliceLabelKey = mcsapi.LabelServiceName + } + + // Retrieve the EndpointSlices associated with the service + endpointSliceList := new(discoveryv1.EndpointSliceList) + opts := []client.ListOption{ + client.MatchingLabels(map[string]string{ + endpointSliceLabelKey: string(backendRef.Name), + }), + client.InNamespace(string(*backendRef.Namespace)), + } + if err := r.client.List(ctx, endpointSliceList, opts...); err != nil { + r.log.Error(err, "failed to get EndpointSlices", "namespace", string(*backendRef.Namespace), + backendRefKind, string(backendRef.Name)) + } else { + for _, endpointSlice := range endpointSliceList.Items { + endpointSlice := endpointSlice + r.log.Info("added EndpointSlice to resource tree", "namespace", endpointSlice.Namespace, + "name", endpointSlice.Name) + resourceTree.EndpointSlices = append(resourceTree.EndpointSlices, &endpointSlice) } } } @@ -241,20 +267,66 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. // Add all EnvoyPatchPolicies if r.envoyGateway.ExtensionAPIs != nil && r.envoyGateway.ExtensionAPIs.EnableEnvoyPatchPolicy { - envoyPatchPolicies := egv1a1.EnvoyPatchPolicyList{} + envoyPatchPolicies := v1alpha1.EnvoyPatchPolicyList{} if err := r.client.List(ctx, &envoyPatchPolicies); err != nil { - return reconcile.Result{}, fmt.Errorf("error listing envoypatchpolicies: %v", err) + return reconcile.Result{}, fmt.Errorf("error listing EnvoyPatchPolicies: %v", err) } for _, policy := range envoyPatchPolicies.Items { policy := policy // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = egv1a1.EnvoyPatchPolicyStatus{} + policy.Status = v1alpha1.EnvoyPatchPolicyStatus{} resourceTree.EnvoyPatchPolicies = append(resourceTree.EnvoyPatchPolicies, &policy) } } + // Add all ClientTrafficPolicies + clientTrafficPolicies := v1alpha1.ClientTrafficPolicyList{} + if err := r.client.List(ctx, &clientTrafficPolicies); err != nil { + return reconcile.Result{}, fmt.Errorf("error listing ClientTrafficPolicies: %v", err) + } + + for _, policy := range clientTrafficPolicies.Items { + policy := policy + // Discard Status to reduce memory consumption in watchable + // It will be recomputed by the gateway-api layer + policy.Status = v1alpha1.ClientTrafficPolicyStatus{} + resourceTree.ClientTrafficPolicies = append(resourceTree.ClientTrafficPolicies, &policy) + + } + + // Add all BackendTrafficPolicies + backendTrafficPolicies := v1alpha1.BackendTrafficPolicyList{} + if err := r.client.List(ctx, &backendTrafficPolicies); err != nil { + return reconcile.Result{}, fmt.Errorf("error listing BackendTrafficPolicies: %v", err) + } + + for _, policy := range backendTrafficPolicies.Items { + policy := policy + // Discard Status to reduce memory consumption in watchable + // It will be recomputed by the gateway-api layer + policy.Status = v1alpha1.BackendTrafficPolicyStatus{} + resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &policy) + } + + // Add all SecurityPolicies + securityPolicies := v1alpha1.SecurityPolicyList{} + if err := r.client.List(ctx, &securityPolicies); err != nil { + return reconcile.Result{}, fmt.Errorf("error listing SecurityPolicies: %v", err) + } + + for _, policy := range securityPolicies.Items { + policy := policy + // Discard Status to reduce memory consumption in watchable + // It will be recomputed by the gateway-api layer + policy.Status = v1alpha1.SecurityPolicyStatus{} + resourceTree.SecurityPolicies = append(resourceTree.SecurityPolicies, &policy) + } + + // Add the referenced Secrets in SecurityPolicies to the resourceTree + r.processSecurityPolicySecretRefs(ctx, resourceTree, resourceMap) + // For this particular Gateway, and all associated objects, check whether the // namespace exists. Add to the resourceTree. for ns := range resourceMap.allAssociatedNamespaces { @@ -274,7 +346,7 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. if acceptedGC.Spec.ParametersRef != nil && acceptedGC.DeletionTimestamp == nil { if err := r.processParamsRef(ctx, acceptedGC, resourceTree); err != nil { msg := fmt.Sprintf("%s: %v", status.MsgGatewayClassInvalidParams, err) - if err := r.gatewayClassUpdater(ctx, acceptedGC, false, string(gwapiv1b1.GatewayClassReasonInvalidParameters), msg); err != nil { + if err := r.gatewayClassUpdater(ctx, acceptedGC, false, string(gwapiv1.GatewayClassReasonInvalidParameters), msg); err != nil { r.log.Error(err, "unable to update GatewayClass status") } r.log.Error(err, "failed to process parametersRef for gatewayclass", "name", acceptedGC.Name) @@ -282,7 +354,11 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. } } - if err := r.gatewayClassUpdater(ctx, acceptedGC, true, string(gwapiv1b1.GatewayClassReasonAccepted), status.MsgValidGatewayClass); err != nil { + if resourceTree.EnvoyProxy != nil && resourceTree.EnvoyProxy.Spec.MergeGateways != nil { + r.mergeGateways = *resourceTree.EnvoyProxy.Spec.MergeGateways + } + + if err := r.gatewayClassUpdater(ctx, acceptedGC, true, string(gwapiv1.GatewayClassReasonAccepted), status.MsgValidGatewayClass); err != nil { r.log.Error(err, "unable to update GatewayClass status") return reconcile.Result{}, err } @@ -311,17 +387,116 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. // Store will be required to trigger a cleanup of envoy infra resources. r.resources.GatewayAPIResources.Store(acceptedGC.Name, resourceTree) - r.log.WithName(request.Name).Info("reconciled gatewayAPI object successfully", "namespace", request.Namespace, "name", request.Name) + r.log.Info("reconciled gateways successfully") return reconcile.Result{}, nil } -func (r *gatewayAPIReconciler) gatewayClassUpdater(ctx context.Context, gc *gwapiv1b1.GatewayClass, accepted bool, reason, msg string) error { +// processSecurityPolicySecretRefs adds the referenced Secrets in SecurityPolicies +// to the resourceTree +func (r *gatewayAPIReconciler) processSecurityPolicySecretRefs( + ctx context.Context, resourceTree *gatewayapi.Resources, resourceMap *resourceMappings) { + for _, policy := range resourceTree.SecurityPolicies { + oidc := policy.Spec.OIDC + + if oidc != nil { + if err := r.processSecretRef( + ctx, + resourceMap, + resourceTree, + gatewayapi.KindSecurityPolicy, + policy.Namespace, + policy.Name, + oidc.ClientSecret); err != nil { + // we don't return an error here, because we want to continue + // reconciling the rest of the SecurityPolicies despite that this + // secret reference is invalid. + // This SecurityPolicy will be marked as invalid in its status + // when translating to IR because the referenced secret can't be + // found. + r.log.Error(err, + "failed to process OIDC SecretRef for SecurityPolicy", + "policy", policy, "secretRef", oidc.ClientSecret) + } + } + basicAuth := policy.Spec.BasicAuth + if basicAuth != nil { + if err := r.processSecretRef( + ctx, + resourceMap, + resourceTree, + gatewayapi.KindSecurityPolicy, + policy.Namespace, + policy.Name, + basicAuth.Users); err != nil { + r.log.Error(err, + "failed to process BasicAuth SecretRef for SecurityPolicy", + "policy", policy, "secretRef", basicAuth.Users) + } + } + } +} + +// processSecretRef adds the referenced Secret to the resourceTree if it's valid. +// - If it exists in the same namespace as the owner. +// - If it exists in a different namespace, and there is a ReferenceGrant. +func (r *gatewayAPIReconciler) processSecretRef( + ctx context.Context, + resourceMap *resourceMappings, + resourceTree *gatewayapi.Resources, + ownerKind string, + ownerNS string, + ownerName string, + secretRef gwapiv1b1.SecretObjectReference, +) error { + secret := new(corev1.Secret) + secretNS := gatewayapi.NamespaceDerefOr(secretRef.Namespace, ownerNS) + err := r.client.Get(ctx, + types.NamespacedName{Namespace: secretNS, Name: string(secretRef.Name)}, + secret, + ) + if err != nil && !kerrors.IsNotFound(err) { + return fmt.Errorf("unable to find the Secret: %s/%s", secretNS, string(secretRef.Name)) + } + + if secretNS != ownerNS { + from := ObjectKindNamespacedName{ + kind: ownerKind, + namespace: ownerNS, + name: ownerName, + } + to := ObjectKindNamespacedName{ + kind: gatewayapi.KindSecret, + namespace: secretNS, + name: secret.Name, + } + refGrant, err := r.findReferenceGrant(ctx, from, to) + switch { + case err != nil: + return fmt.Errorf("failed to find ReferenceGrant: %v", err) + case refGrant == nil: + return fmt.Errorf( + "no matching ReferenceGrants found: from %s/%s to %s/%s", + from.kind, from.namespace, to.kind, to.namespace) + default: + // RefGrant found + resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } + } + resourceMap.allAssociatedNamespaces[secretNS] = struct{}{} // TODO Zhaohuabing do we need this line? + resourceTree.Secrets = append(resourceTree.Secrets, secret) + r.log.Info("processing Secret", "namespace", secretNS, "name", string(secretRef.Name)) + return nil +} + +func (r *gatewayAPIReconciler) gatewayClassUpdater(ctx context.Context, gc *gwapiv1.GatewayClass, accepted bool, reason, msg string) error { if r.statusUpdater != nil { r.statusUpdater.Send(status.Update{ NamespacedName: types.NamespacedName{Name: gc.Name}, - Resource: &gwapiv1b1.GatewayClass{}, + Resource: &gwapiv1.GatewayClass{}, Mutator: status.MutatorFunc(func(obj client.Object) client.Object { - gc, ok := obj.(*gwapiv1b1.GatewayClass) + gc, ok := obj.(*gwapiv1.GatewayClass) if !ok { panic(fmt.Sprintf("unsupported object type %T", obj)) } @@ -350,7 +525,7 @@ func (r *gatewayAPIReconciler) getNamespace(ctx context.Context, name string) (* return ns, nil } -func (r *gatewayAPIReconciler) statusUpdateForGateway(ctx context.Context, gtw *gwapiv1b1.Gateway) { +func (r *gatewayAPIReconciler) statusUpdateForGateway(ctx context.Context, gtw *gwapiv1.Gateway) { // nil check for unit tests. if r.statusUpdater == nil { return @@ -379,9 +554,9 @@ func (r *gatewayAPIReconciler) statusUpdateForGateway(ctx context.Context, gtw * // publish status r.statusUpdater.Send(status.Update{ NamespacedName: key, - Resource: new(gwapiv1b1.Gateway), + Resource: new(gwapiv1.Gateway), Mutator: status.MutatorFunc(func(obj client.Object) client.Object { - g, ok := obj.(*gwapiv1b1.Gateway) + g, ok := obj.(*gwapiv1.Gateway) if !ok { panic(fmt.Sprintf("unsupported object type %T", obj)) } @@ -394,14 +569,33 @@ func (r *gatewayAPIReconciler) statusUpdateForGateway(ctx context.Context, gtw * }) } -func (r *gatewayAPIReconciler) findReferenceGrant(ctx context.Context, from, to ObjectKindNamespacedName) (*gwapiv1a2.ReferenceGrant, error) { - refGrantList := new(gwapiv1a2.ReferenceGrantList) +func (r *gatewayAPIReconciler) findReferenceGrant(ctx context.Context, from, to ObjectKindNamespacedName) (*gwapiv1b1.ReferenceGrant, error) { + refGrantList := new(gwapiv1b1.ReferenceGrantList) opts := &client.ListOptions{FieldSelector: fields.OneTermEqualSelector(targetRefGrantRouteIndex, to.kind)} if err := r.client.List(ctx, refGrantList, opts); err != nil { return nil, fmt.Errorf("failed to list ReferenceGrants: %v", err) } - for _, refGrant := range refGrantList.Items { + refGrants := refGrantList.Items + if len(r.namespaceLabels) != 0 { + var rgs []gwapiv1b1.ReferenceGrant + for _, refGrant := range refGrants { + ns := refGrant.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return nil, fmt.Errorf("failed to check namespace labels for ReferenceGrant %s in namespace %s: %s", refGrant.GetName(), ns, err) + } + if !ok { + // TODO: should log? + continue + } + rgs = append(rgs, refGrant) + } + refGrants = rgs + } + + for _, refGrant := range refGrants { if refGrant.Namespace == to.namespace { for _, src := range refGrant.Spec.From { if src.Kind == gwapiv1a2.Kind(from.kind) && string(src.Namespace) == from.namespace { @@ -415,19 +609,10 @@ func (r *gatewayAPIReconciler) findReferenceGrant(ctx context.Context, from, to return nil, nil } -func (r *gatewayAPIReconciler) getRateLimitFilters(ctx context.Context) ([]egv1a1.RateLimitFilter, error) { - rateLimitList := new(egv1a1.RateLimitFilterList) - if err := r.client.List(ctx, rateLimitList); err != nil { - return nil, fmt.Errorf("failed to list RateLimitFilters: %v", err) - } - - return rateLimitList.Items, nil -} - -func (r *gatewayAPIReconciler) processGateways(ctx context.Context, acceptedGC *gwapiv1b1.GatewayClass, resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { +func (r *gatewayAPIReconciler) processGateways(ctx context.Context, acceptedGC *gwapiv1.GatewayClass, resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { // Find gateways for the acceptedGC // Find the Gateways that reference this Class. - gatewayList := &gwapiv1b1.GatewayList{} + gatewayList := &gwapiv1.GatewayList{} if err := r.client.List(ctx, gatewayList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(classGatewayIndex, acceptedGC.Name), }); err != nil { @@ -435,7 +620,25 @@ func (r *gatewayAPIReconciler) processGateways(ctx context.Context, acceptedGC * return err } - for _, gtw := range gatewayList.Items { + gateways := gatewayList.Items + if len(r.namespaceLabels) != 0 { + var gtws []gwapiv1.Gateway + for _, gtw := range gateways { + ns := gtw.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return fmt.Errorf("failed to check namespace labels for gateway %s in namespace %s: %s", gtw.GetName(), ns, err) + } + + if ok { + gtws = append(gtws, gtw) + } + } + gateways = gtws + } + + for _, gtw := range gateways { gtw := gtw r.log.Info("processing Gateway", "namespace", gtw.Namespace, "name", gtw.Name) resourceMap.allAssociatedNamespaces[gtw.Namespace] = struct{}{} @@ -447,47 +650,19 @@ func (r *gatewayAPIReconciler) processGateways(ctx context.Context, acceptedGC * for _, certRef := range listener.TLS.CertificateRefs { certRef := certRef if refsSecret(&certRef) { - secret := new(corev1.Secret) - secretNamespace := gatewayapi.NamespaceDerefOr(certRef.Namespace, gtw.Namespace) - err := r.client.Get(ctx, - types.NamespacedName{Namespace: secretNamespace, Name: string(certRef.Name)}, - secret, - ) - if err != nil && !kerrors.IsNotFound(err) { - r.log.Error(err, "unable to find Secret") - return err - } - - r.log.Info("processing Secret", "namespace", secretNamespace, "name", string(certRef.Name)) - - if secretNamespace != gtw.Namespace { - from := ObjectKindNamespacedName{ - kind: gatewayapi.KindGateway, - namespace: gtw.Namespace, - name: gtw.Name, - } - to := ObjectKindNamespacedName{ - kind: gatewayapi.KindSecret, - namespace: secretNamespace, - name: string(certRef.Name), - } - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - // RefGrant found - resourceMap.allAssociatedRefGrants[utils.NamespacedName(refGrant)] = refGrant - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } + if err := r.processSecretRef( + ctx, + resourceMap, + resourceTree, + gatewayapi.KindGateway, + gtw.Namespace, + gtw.Name, + certRef); err != nil { + + r.log.Error(err, + "failed to process TLS SecretRef for gateway", + "gateway", gtw, "secretRef", certRef) } - - resourceMap.allAssociatedNamespaces[secretNamespace] = struct{}{} - resourceTree.Secrets = append(resourceTree.Secrets, secret) } } } @@ -521,15 +696,15 @@ func (r *gatewayAPIReconciler) processGateways(ctx context.Context, acceptedGC * // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - gtw.Status = gwapiv1b1.GatewayStatus{} + gtw.Status = gwapiv1.GatewayStatus{} resourceTree.Gateways = append(resourceTree.Gateways, >w) } return nil } func addReferenceGrantIndexers(ctx context.Context, mgr manager.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.ReferenceGrant{}, targetRefGrantRouteIndex, func(rawObj client.Object) []string { - refGrant := rawObj.(*gwapiv1a2.ReferenceGrant) + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, func(rawObj client.Object) []string { + refGrant := rawObj.(*gwapiv1b1.ReferenceGrant) var referredServices []string for _, target := range refGrant.Spec.To { referredServices = append(referredServices, string(target.Kind)) @@ -542,74 +717,22 @@ func addReferenceGrantIndexers(ctx context.Context, mgr manager.Manager) error { } // addHTTPRouteIndexers adds indexing on HTTPRoute. -// - For Service objects that are referenced in HTTPRoute objects via `.spec.rules.backendRefs`. +// - For Service, ServiceImports objects that are referenced in HTTPRoute objects via `.spec.rules.backendRefs`. // This helps in querying for HTTPRoutes that are affected by a particular Service CRUD. -// - For AuthenticationFilter and RateLimitFilter objects that are referenced in HTTPRoute objects via -// `.spec.rules[].filters`. This helps in querying for HTTPRoutes that are affected by a -// particular AuthenticationFilter CRUD. func addHTTPRouteIndexers(ctx context.Context, mgr manager.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.HTTPRoute{}, gatewayHTTPRouteIndex, gatewayHTTPRouteIndexFunc); err != nil { - return err - } - - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.HTTPRoute{}, serviceHTTPRouteIndex, serviceHTTPRouteIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, gatewayHTTPRouteIndex, gatewayHTTPRouteIndexFunc); err != nil { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.HTTPRoute{}, authenFilterHTTPRouteIndex, authenFilterHTTPRouteIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, backendHTTPRouteIndex, backendHTTPRouteIndexFunc); err != nil { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.HTTPRoute{}, rateLimitFilterHTTPRouteIndex, rateLimitFilterHTTPRouteIndexFunc); err != nil { - return err - } return nil } -func authenFilterHTTPRouteIndexFunc(rawObj client.Object) []string { - httproute := rawObj.(*gwapiv1b1.HTTPRoute) - var filters []string - for _, rule := range httproute.Spec.Rules { - for i := range rule.Filters { - filter := rule.Filters[i] - if gatewayapi.IsAuthnHTTPFilter(&filter) { - if err := gatewayapi.ValidateHTTPRouteFilter(&filter); err == nil { - filters = append(filters, - types.NamespacedName{ - Namespace: httproute.Namespace, - Name: string(filter.ExtensionRef.Name), - }.String(), - ) - } - } - } - } - return filters -} - -func rateLimitFilterHTTPRouteIndexFunc(rawObj client.Object) []string { - httproute := rawObj.(*gwapiv1b1.HTTPRoute) - var filters []string - for _, rule := range httproute.Spec.Rules { - for i := range rule.Filters { - filter := rule.Filters[i] - if gatewayapi.IsRateLimitHTTPFilter(&filter) { - if err := gatewayapi.ValidateHTTPRouteFilter(&filter); err == nil { - filters = append(filters, - types.NamespacedName{ - Namespace: httproute.Namespace, - Name: string(filter.ExtensionRef.Name), - }.String(), - ) - } - } - } - } - return filters -} - func gatewayHTTPRouteIndexFunc(rawObj client.Object) []string { - httproute := rawObj.(*gwapiv1b1.HTTPRoute) + httproute := rawObj.(*gwapiv1.HTTPRoute) var gateways []string for _, parent := range httproute.Spec.ParentRefs { if parent.Kind == nil || string(*parent.Kind) == gatewayapi.KindGateway { @@ -626,15 +749,15 @@ func gatewayHTTPRouteIndexFunc(rawObj client.Object) []string { return gateways } -func serviceHTTPRouteIndexFunc(rawObj client.Object) []string { - httproute := rawObj.(*gwapiv1b1.HTTPRoute) - var services []string +func backendHTTPRouteIndexFunc(rawObj client.Object) []string { + httproute := rawObj.(*gwapiv1.HTTPRoute) + var backendRefs []string for _, rule := range httproute.Spec.Rules { for _, backend := range rule.BackendRefs { if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { - // If an explicit Service namespace is not provided, use the HTTPRoute namespace to + // If an explicit Backend namespace is not provided, use the HTTPRoute namespace to // lookup the provided Gateway Name. - services = append(services, + backendRefs = append(backendRefs, types.NamespacedName{ Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, httproute.Namespace), Name: string(backend.Name), @@ -643,7 +766,7 @@ func serviceHTTPRouteIndexFunc(rawObj client.Object) []string { } } } - return services + return backendRefs } // addGRPCRouteIndexers adds indexing on GRPCRoute, for Service objects that are @@ -654,7 +777,7 @@ func addGRPCRouteIndexers(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.GRPCRoute{}, serviceGRPCRouteIndex, serviceGRPCRouteIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.GRPCRoute{}, backendGRPCRouteIndex, backendGRPCRouteIndexFunc); err != nil { return err } @@ -679,15 +802,15 @@ func gatewayGRPCRouteIndexFunc(rawObj client.Object) []string { return gateways } -func serviceGRPCRouteIndexFunc(rawObj client.Object) []string { +func backendGRPCRouteIndexFunc(rawObj client.Object) []string { grpcroute := rawObj.(*gwapiv1a2.GRPCRoute) - var services []string + var backendRefs []string for _, rule := range grpcroute.Spec.Rules { for _, backend := range rule.BackendRefs { if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { - // If an explicit Service namespace is not provided, use the GRPCRoute namespace to + // If an explicit Backend namespace is not provided, use the GRPCRoute namespace to // lookup the provided Gateway Name. - services = append(services, + backendRefs = append(backendRefs, types.NamespacedName{ Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, grpcroute.Namespace), Name: string(backend.Name), @@ -696,7 +819,7 @@ func serviceGRPCRouteIndexFunc(rawObj client.Object) []string { } } } - return services + return backendRefs } // addTLSRouteIndexers adds indexing on TLSRoute, for Service objects that are @@ -723,21 +846,21 @@ func addTLSRouteIndexers(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TLSRoute{}, serviceTLSRouteIndex, serviceTLSRouteIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TLSRoute{}, backendTLSRouteIndex, backendTLSRouteIndexFunc); err != nil { return err } return nil } -func serviceTLSRouteIndexFunc(rawObj client.Object) []string { +func backendTLSRouteIndexFunc(rawObj client.Object) []string { tlsroute := rawObj.(*gwapiv1a2.TLSRoute) - var services []string + var backendRefs []string for _, rule := range tlsroute.Spec.Rules { for _, backend := range rule.BackendRefs { if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { - // If an explicit Service namespace is not provided, use the TLSRoute namespace to + // If an explicit Backend namespace is not provided, use the TLSRoute namespace to // lookup the provided Gateway Name. - services = append(services, + backendRefs = append(backendRefs, types.NamespacedName{ Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, tlsroute.Namespace), Name: string(backend.Name), @@ -746,7 +869,7 @@ func serviceTLSRouteIndexFunc(rawObj client.Object) []string { } } } - return services + return backendRefs } // addTCPRouteIndexers adds indexing on TCPRoute, for Service objects that are @@ -773,21 +896,21 @@ func addTCPRouteIndexers(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TCPRoute{}, serviceTCPRouteIndex, serviceTCPRouteIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TCPRoute{}, backendTCPRouteIndex, backendTCPRouteIndexFunc); err != nil { return err } return nil } -func serviceTCPRouteIndexFunc(rawObj client.Object) []string { +func backendTCPRouteIndexFunc(rawObj client.Object) []string { tcpRoute := rawObj.(*gwapiv1a2.TCPRoute) - var services []string + var backendRefs []string for _, rule := range tcpRoute.Spec.Rules { for _, backend := range rule.BackendRefs { if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { - // If an explicit Service namespace is not provided, use the TCPRoute namespace to + // If an explicit Backend namespace is not provided, use the TCPRoute namespace to // lookup the provided Gateway Name. - services = append(services, + backendRefs = append(backendRefs, types.NamespacedName{ Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, tcpRoute.Namespace), Name: string(backend.Name), @@ -796,7 +919,7 @@ func serviceTCPRouteIndexFunc(rawObj client.Object) []string { } } } - return services + return backendRefs } // addUDPRouteIndexers adds indexing on UDPRoute, for Service objects that are @@ -823,21 +946,21 @@ func addUDPRouteIndexers(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.UDPRoute{}, serviceUDPRouteIndex, serviceUDPRouteIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.UDPRoute{}, backendUDPRouteIndex, backendUDPRouteIndexFunc); err != nil { return err } return nil } -func serviceUDPRouteIndexFunc(rawObj client.Object) []string { +func backendUDPRouteIndexFunc(rawObj client.Object) []string { udproute := rawObj.(*gwapiv1a2.UDPRoute) - var services []string + var backendRefs []string for _, rule := range udproute.Spec.Rules { for _, backend := range rule.BackendRefs { if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { - // If an explicit Service namespace is not provided, use the UDPRoute namespace to + // If an explicit Backend namespace is not provided, use the UDPRoute namespace to // lookup the provided Gateway Name. - services = append(services, + backendRefs = append(backendRefs, types.NamespacedName{ Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, udproute.Namespace), Name: string(backend.Name), @@ -846,19 +969,19 @@ func serviceUDPRouteIndexFunc(rawObj client.Object) []string { } } } - return services + return backendRefs } // addGatewayIndexers adds indexing on Gateway, for Secret objects that are // referenced in Gateway objects. This helps in querying for Gateways that are // affected by a particular Secret CRUD. func addGatewayIndexers(ctx context.Context, mgr manager.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.Gateway{}, secretGatewayIndex, secretGatewayIndexFunc); err != nil { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.Gateway{}, secretGatewayIndex, secretGatewayIndexFunc); err != nil { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.Gateway{}, classGatewayIndex, func(rawObj client.Object) []string { - gateway := rawObj.(*gwapiv1b1.Gateway) + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.Gateway{}, classGatewayIndex, func(rawObj client.Object) []string { + gateway := rawObj.(*gwapiv1.Gateway) return []string{string(gateway.Spec.GatewayClassName)} }); err != nil { return err @@ -867,10 +990,10 @@ func addGatewayIndexers(ctx context.Context, mgr manager.Manager) error { } func secretGatewayIndexFunc(rawObj client.Object) []string { - gateway := rawObj.(*gwapiv1b1.Gateway) + gateway := rawObj.(*gwapiv1.Gateway) var secretReferences []string for _, listener := range gateway.Spec.Listeners { - if listener.TLS == nil || *listener.TLS.Mode != gwapiv1b1.TLSModeTerminate { + if listener.TLS == nil || *listener.TLS.Mode != gwapiv1.TLSModeTerminate { continue } for _, cert := range listener.TLS.CertificateRefs { @@ -890,7 +1013,7 @@ func secretGatewayIndexFunc(rawObj client.Object) []string { } // removeFinalizer removes the gatewayclass finalizer from the provided gc, if it exists. -func (r *gatewayAPIReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error { +func (r *gatewayAPIReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1.GatewayClass) error { if slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) { base := client.MergeFrom(gc.DeepCopy()) gc.Finalizers = slice.RemoveString(gc.Finalizers, gatewayClassFinalizer) @@ -902,7 +1025,7 @@ func (r *gatewayAPIReconciler) removeFinalizer(ctx context.Context, gc *gwapiv1b } // addFinalizer adds the gatewayclass finalizer to the provided gc, if it doesn't exist. -func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.GatewayClass) error { +func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1.GatewayClass) error { if !slice.ContainsString(gc.Finalizers, gatewayClassFinalizer) { base := client.MergeFrom(gc.DeepCopy()) gc.Finalizers = append(gc.Finalizers, gatewayClassFinalizer) @@ -918,16 +1041,17 @@ func (r *gatewayAPIReconciler) addFinalizer(ctx context.Context, gc *gwapiv1b1.G func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // Gateway object status updater go func() { - message.HandleSubscription(r.resources.GatewayStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *gwapiv1b1.GatewayStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "gateway-status"}, r.resources.GatewayStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1.GatewayStatus], errChan chan error) { // skip delete updates. if update.Delete { return } // Get gateway object - gtw := new(gwapiv1b1.Gateway) + gtw := new(gwapiv1.Gateway) if err := r.client.Get(ctx, update.Key, gtw); err != nil { r.log.Error(err, "gateway not found", "namespace", gtw.Namespace, "name", gtw.Name) + errChan <- err return } // Set the updated Status and call the status update @@ -940,8 +1064,8 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // HTTPRoute object status updater go func() { - message.HandleSubscription(r.resources.HTTPRouteStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *gwapiv1b1.HTTPRouteStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "httproute-status"}, r.resources.HTTPRouteStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1.HTTPRouteStatus], errChan chan error) { // skip delete updates. if update.Delete { return @@ -950,11 +1074,13 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { val := update.Value r.statusUpdater.Send(status.Update{ NamespacedName: key, - Resource: new(gwapiv1b1.HTTPRoute), + Resource: new(gwapiv1.HTTPRoute), Mutator: status.MutatorFunc(func(obj client.Object) client.Object { - h, ok := obj.(*gwapiv1b1.HTTPRoute) + h, ok := obj.(*gwapiv1.HTTPRoute) if !ok { - panic(fmt.Sprintf("unsupported object type %T", obj)) + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) } hCopy := h.DeepCopy() hCopy.Status.Parents = val.Parents @@ -968,8 +1094,8 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // GRPCRoute object status updater go func() { - message.HandleSubscription(r.resources.GRPCRouteStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *gwapiv1a2.GRPCRouteStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "grpcroute-status"}, r.resources.GRPCRouteStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1a2.GRPCRouteStatus], errChan chan error) { // skip delete updates. if update.Delete { return @@ -982,7 +1108,9 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { Mutator: status.MutatorFunc(func(obj client.Object) client.Object { h, ok := obj.(*gwapiv1a2.GRPCRoute) if !ok { - panic(fmt.Sprintf("unsupported object type %T", obj)) + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) } hCopy := h.DeepCopy() hCopy.Status.Parents = val.Parents @@ -996,8 +1124,8 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // TLSRoute object status updater go func() { - message.HandleSubscription(r.resources.TLSRouteStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *gwapiv1a2.TLSRouteStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "tlsroute-status"}, r.resources.TLSRouteStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1a2.TLSRouteStatus], errChan chan error) { // skip delete updates. if update.Delete { return @@ -1010,7 +1138,9 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { Mutator: status.MutatorFunc(func(obj client.Object) client.Object { t, ok := obj.(*gwapiv1a2.TLSRoute) if !ok { - panic(fmt.Sprintf("unsupported object type %T", obj)) + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) } tCopy := t.DeepCopy() tCopy.Status.Parents = val.Parents @@ -1024,8 +1154,8 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // TCPRoute object status updater go func() { - message.HandleSubscription(r.resources.TCPRouteStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *gwapiv1a2.TCPRouteStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "tcproute-status"}, r.resources.TCPRouteStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1a2.TCPRouteStatus], errChan chan error) { // skip delete updates. if update.Delete { return @@ -1038,7 +1168,9 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { Mutator: status.MutatorFunc(func(obj client.Object) client.Object { t, ok := obj.(*gwapiv1a2.TCPRoute) if !ok { - panic(fmt.Sprintf("unsupported object type %T", obj)) + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) } tCopy := t.DeepCopy() tCopy.Status.Parents = val.Parents @@ -1052,8 +1184,8 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // UDPRoute object status updater go func() { - message.HandleSubscription(r.resources.UDPRouteStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *gwapiv1a2.UDPRouteStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "udproute-status"}, r.resources.UDPRouteStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1a2.UDPRouteStatus], errChan chan error) { // skip delete updates. if update.Delete { return @@ -1066,7 +1198,9 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { Mutator: status.MutatorFunc(func(obj client.Object) client.Object { t, ok := obj.(*gwapiv1a2.UDPRoute) if !ok { - panic(fmt.Sprintf("unsupported object type %T", obj)) + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) } tCopy := t.DeepCopy() tCopy.Status.Parents = val.Parents @@ -1080,8 +1214,8 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { // EnvoyPatchPolicy object status updater go func() { - message.HandleSubscription(r.envoyPatchPolicyStatuses.Subscribe(ctx), - func(update message.Update[types.NamespacedName, *egv1a1.EnvoyPatchPolicyStatus]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "envoypatchpolicy-status"}, r.resources.EnvoyPatchPolicyStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *v1alpha1.EnvoyPatchPolicyStatus], errChan chan error) { // skip delete updates. if update.Delete { return @@ -1090,11 +1224,13 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { val := update.Value r.statusUpdater.Send(status.Update{ NamespacedName: key, - Resource: new(egv1a1.EnvoyPatchPolicy), + Resource: new(v1alpha1.EnvoyPatchPolicy), Mutator: status.MutatorFunc(func(obj client.Object) client.Object { - t, ok := obj.(*egv1a1.EnvoyPatchPolicy) + t, ok := obj.(*v1alpha1.EnvoyPatchPolicy) if !ok { - panic(fmt.Sprintf("unsupported object type %T", obj)) + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) } tCopy := t.DeepCopy() tCopy.Status = *val @@ -1105,33 +1241,138 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { ) r.log.Info("envoyPatchPolicy status subscriber shutting down") }() + + // ClientTrafficPolicy object status updater + go func() { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "clienttrafficpolicy-status"}, r.resources.ClientTrafficPolicyStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *v1alpha1.ClientTrafficPolicyStatus], errChan chan error) { + // skip delete updates. + if update.Delete { + return + } + key := update.Key + val := update.Value + r.statusUpdater.Send(status.Update{ + NamespacedName: key, + Resource: new(v1alpha1.ClientTrafficPolicy), + Mutator: status.MutatorFunc(func(obj client.Object) client.Object { + t, ok := obj.(*v1alpha1.ClientTrafficPolicy) + if !ok { + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) + } + tCopy := t.DeepCopy() + tCopy.Status = *val + return tCopy + }), + }) + }, + ) + r.log.Info("clientTrafficPolicy status subscriber shutting down") + }() + + // BackendTrafficPolicy object status updater + go func() { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "backendtrafficpolicy-status"}, r.resources.BackendTrafficPolicyStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *v1alpha1.BackendTrafficPolicyStatus], errChan chan error) { + // skip delete updates. + if update.Delete { + return + } + key := update.Key + val := update.Value + r.statusUpdater.Send(status.Update{ + NamespacedName: key, + Resource: new(v1alpha1.BackendTrafficPolicy), + Mutator: status.MutatorFunc(func(obj client.Object) client.Object { + t, ok := obj.(*v1alpha1.BackendTrafficPolicy) + if !ok { + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) + } + tCopy := t.DeepCopy() + tCopy.Status = *val + return tCopy + }), + }) + }, + ) + r.log.Info("backendTrafficPolicy status subscriber shutting down") + }() + + // SecurityPolicy object status updater + go func() { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentProviderRunner), Message: "securitypolicy-status"}, r.resources.SecurityPolicyStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *v1alpha1.SecurityPolicyStatus], errChan chan error) { + // skip delete updates. + if update.Delete { + return + } + key := update.Key + val := update.Value + r.statusUpdater.Send(status.Update{ + NamespacedName: key, + Resource: new(v1alpha1.SecurityPolicy), + Mutator: status.MutatorFunc(func(obj client.Object) client.Object { + t, ok := obj.(*v1alpha1.SecurityPolicy) + if !ok { + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) + } + tCopy := t.DeepCopy() + tCopy.Status = *val + return tCopy + }), + }) + }, + ) + r.log.Info("securityPolicy status subscriber shutting down") + }() } // watchResources watches gateway api resources. func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.Manager, c controller.Controller) error { - // Only enqueue GatewayClass objects that match this Envoy Gateway's controller name. if err := c.Watch( - source.Kind(mgr.GetCache(), &gwapiv1b1.GatewayClass{}), - &handler.EnqueueRequestForObject{}, + source.Kind(mgr.GetCache(), &gwapiv1.GatewayClass{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + predicate.GenerationChangedPredicate{}, predicate.NewPredicateFuncs(r.hasMatchingController), ); err != nil { return err } // Only enqueue EnvoyProxy objects that match this Envoy Gateway's GatewayClass. - if err := c.Watch( - source.Kind(mgr.GetCache(), &egcfgv1a1.EnvoyProxy{}), - handler.EnqueueRequestsFromMapFunc(r.enqueueManagedClass), + epPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, predicate.ResourceVersionChangedPredicate{}, + predicate.NewPredicateFuncs(r.hasManagedClass), + } + if len(r.namespaceLabels) != 0 { + epPredicates = append(epPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } + if err := c.Watch( + source.Kind(mgr.GetCache(), &v1alpha1.EnvoyProxy{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + epPredicates..., ); err != nil { return err } // Watch Gateway CRUDs and reconcile affected GatewayClass. - if err := c.Watch( - source.Kind(mgr.GetCache(), &gwapiv1b1.Gateway{}), - &handler.EnqueueRequestForObject{}, + gPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, predicate.NewPredicateFuncs(r.validateGatewayForReconcile), + } + if len(r.namespaceLabels) != 0 { + gPredicates = append(gPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } + if err := c.Watch( + source.Kind(mgr.GetCache(), &gwapiv1.Gateway{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + gPredicates..., ); err != nil { return err } @@ -1140,9 +1381,14 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch HTTPRoute CRUDs and process affected Gateways. + httprPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + httprPredicates = append(httprPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( - source.Kind(mgr.GetCache(), &gwapiv1b1.HTTPRoute{}), - &handler.EnqueueRequestForObject{}, + source.Kind(mgr.GetCache(), &gwapiv1.HTTPRoute{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + httprPredicates..., ); err != nil { return err } @@ -1151,9 +1397,14 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch GRPCRoute CRUDs and process affected Gateways. + grpcrPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + grpcrPredicates = append(grpcrPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &gwapiv1a2.GRPCRoute{}), - &handler.EnqueueRequestForObject{}, + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + grpcrPredicates..., ); err != nil { return err } @@ -1162,9 +1413,14 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch TLSRoute CRUDs and process affected Gateways. + tlsrPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + tlsrPredicates = append(tlsrPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &gwapiv1a2.TLSRoute{}), - &handler.EnqueueRequestForObject{}, + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + tlsrPredicates..., ); err != nil { return err } @@ -1173,9 +1429,14 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch UDPRoute CRUDs and process affected Gateways. + udprPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + udprPredicates = append(udprPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &gwapiv1a2.UDPRoute{}), - &handler.EnqueueRequestForObject{}, + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + udprPredicates..., ); err != nil { return err } @@ -1184,9 +1445,14 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch TCPRoute CRUDs and process affected Gateways. + tcprPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + tcprPredicates = append(tcprPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &gwapiv1a2.TCPRoute{}), - &handler.EnqueueRequestForObject{}, + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + tcprPredicates..., ); err != nil { return err } @@ -1195,45 +1461,94 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch Service CRUDs and process affected *Route objects. + servicePredicates := []predicate.Predicate{predicate.NewPredicateFuncs(r.validateServiceForReconcile)} + if len(r.namespaceLabels) != 0 { + servicePredicates = append(servicePredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &corev1.Service{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.validateServiceForReconcile)); err != nil { + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + servicePredicates..., + ); err != nil { return err } + serviceImportCRDExists := r.serviceImportCRDExists(mgr) + if !serviceImportCRDExists { + r.log.Info("ServiceImport CRD not found, skipping ServiceImport watch") + } + + // Watch ServiceImport CRUDs and process affected *Route objects. + if serviceImportCRDExists { + if err := c.Watch( + source.Kind(mgr.GetCache(), &mcsapi.ServiceImport{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.validateServiceImportForReconcile)); err != nil { + // ServiceImport is not available in the cluster, skip the watch and not throw error. + r.log.Info("unable to watch ServiceImport: %s", err.Error()) + } + } + // Watch EndpointSlice CRUDs and process affected *Route objects. + esPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.validateEndpointSliceForReconcile), + } + if len(r.namespaceLabels) != 0 { + esPredicates = append(esPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &discoveryv1.EndpointSlice{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.validateEndpointSliceForReconcile)); err != nil { + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + esPredicates..., + ); err != nil { return err } // Watch Node CRUDs to update Gateway Address exposed by Service of type NodePort. // Node creation/deletion and ExternalIP updates would require update in the Gateway + nPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.handleNode), + } + if len(r.namespaceLabels) != 0 { + nPredicates = append(nPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } // resource address. if err := c.Watch( source.Kind(mgr.GetCache(), &corev1.Node{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.handleNode), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + nPredicates..., ); err != nil { return err } // Watch Secret CRUDs and process affected Gateways. + secretPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.validateSecretForReconcile), + } + if len(r.namespaceLabels) != 0 { + secretPredicates = append(secretPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &corev1.Secret{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.validateSecretForReconcile), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + secretPredicates..., ); err != nil { return err } // Watch ReferenceGrant CRUDs and process affected Gateways. + rgPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + rgPredicates = append(rgPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( - source.Kind(mgr.GetCache(), &gwapiv1a2.ReferenceGrant{}), - &handler.EnqueueRequestForObject{}, + source.Kind(mgr.GetCache(), &gwapiv1b1.ReferenceGrant{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + rgPredicates..., ); err != nil { return err } @@ -1242,48 +1557,90 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch Deployment CRUDs and process affected Gateways. + dPredicates := []predicate.Predicate{predicate.NewPredicateFuncs(r.validateDeploymentForReconcile)} + if len(r.namespaceLabels) != 0 { + dPredicates = append(dPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } if err := c.Watch( source.Kind(mgr.GetCache(), &appsv1.Deployment{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.validateDeploymentForReconcile), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + dPredicates..., ); err != nil { return err } - // Watch AuthenticationFilter CRUDs and enqueue associated HTTPRoute objects. + // Watch EnvoyPatchPolicy if enabled in config + eppPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + eppPredicates = append(eppPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } + if r.envoyGateway.ExtensionAPIs != nil && r.envoyGateway.ExtensionAPIs.EnableEnvoyPatchPolicy { + // Watch EnvoyPatchPolicy CRUDs + if err := c.Watch( + source.Kind(mgr.GetCache(), &v1alpha1.EnvoyPatchPolicy{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + eppPredicates..., + ); err != nil { + return err + } + } + + // Watch ClientTrafficPolicy + ctpPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + ctpPredicates = append(ctpPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } + if err := c.Watch( - source.Kind(mgr.GetCache(), &egv1a1.AuthenticationFilter{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.httpRoutesForAuthenticationFilter)); err != nil { + source.Kind(mgr.GetCache(), &v1alpha1.ClientTrafficPolicy{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + ctpPredicates..., + ); err != nil { return err } - // Watch RateLimitFilter CRUDs and enqueue associated HTTPRoute objects. + // Watch BackendTrafficPolicy + btpPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + btpPredicates = append(btpPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } + if err := c.Watch( - source.Kind(mgr.GetCache(), &egv1a1.RateLimitFilter{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.httpRoutesForRateLimitFilter)); err != nil { + source.Kind(mgr.GetCache(), &v1alpha1.BackendTrafficPolicy{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + btpPredicates..., + ); err != nil { return err } - // Watch EnvoyPatchPolicy if enabled in config - if r.envoyGateway.ExtensionAPIs != nil && r.envoyGateway.ExtensionAPIs.EnableEnvoyPatchPolicy { - // Watch EnvoyPatchPolicy CRUDs - if err := c.Watch( - source.Kind(mgr.GetCache(), &egv1a1.EnvoyPatchPolicy{}), - &handler.EnqueueRequestForObject{}); err != nil { - return err - } + // Watch SecurityPolicy + spPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + spPredicates = append(spPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } + + if err := c.Watch( + source.Kind(mgr.GetCache(), &v1alpha1.SecurityPolicy{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + spPredicates..., + ); err != nil { + return err } r.log.Info("Watching gatewayAPI related objects") // Watch any additional GVKs from the registered extension. + uPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if len(r.namespaceLabels) != 0 { + uPredicates = append(uPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + } for _, gvk := range r.extGVKs { u := &unstructured.Unstructured{} u.SetGroupVersionKind(gvk) if err := c.Watch(source.Kind(mgr.GetCache(), u), - &handler.EnqueueRequestForObject{}); err != nil { + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + uPredicates..., + ); err != nil { return err } r.log.Info("Watching additional resource", "resource", gvk.String()) @@ -1291,8 +1648,14 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M return nil } -func (r *gatewayAPIReconciler) enqueueManagedClass(_ context.Context, obj client.Object) []reconcile.Request { - ep, ok := obj.(*egcfgv1a1.EnvoyProxy) +func (r *gatewayAPIReconciler) enqueueClass(_ context.Context, _ client.Object) []reconcile.Request { + return []reconcile.Request{{NamespacedName: types.NamespacedName{ + Name: string(r.classController), + }}} +} + +func (r *gatewayAPIReconciler) hasManagedClass(obj client.Object) bool { + ep, ok := obj.(*v1alpha1.EnvoyProxy) if !ok { panic(fmt.Sprintf("unsupported object type %T", obj)) } @@ -1301,14 +1664,14 @@ func (r *gatewayAPIReconciler) enqueueManagedClass(_ context.Context, obj client if ep.Namespace != r.namespace { r.log.Info("envoyproxy namespace does not match Envoy Gateway's namespace", "namespace", ep.Namespace, "name", ep.Name) - return []reconcile.Request{} + return false } - gcList := new(gwapiv1b1.GatewayClassList) + gcList := new(gwapiv1.GatewayClassList) err := r.client.List(context.TODO(), gcList) if err != nil { r.log.Error(err, "failed to list gatewayclasses") - return []reconcile.Request{} + return false } for i := range gcList.Items { @@ -1317,23 +1680,21 @@ func (r *gatewayAPIReconciler) enqueueManagedClass(_ context.Context, obj client if r.hasMatchingController(&gc) && classAccepted(&gc) && classRefsEnvoyProxy(&gc, ep) { - req := reconcile.Request{ - NamespacedName: types.NamespacedName{Name: gc.Name}, - } - return []reconcile.Request{req} + return true } } - return []reconcile.Request{} + return false } // processParamsRef processes the parametersRef of the provided GatewayClass. -func (r *gatewayAPIReconciler) processParamsRef(ctx context.Context, gc *gwapiv1b1.GatewayClass, resourceTree *gatewayapi.Resources) error { +func (r *gatewayAPIReconciler) processParamsRef(ctx context.Context, gc *gwapiv1.GatewayClass, resourceTree *gatewayapi.Resources) error { if !refsEnvoyProxy(gc) { return fmt.Errorf("unsupported parametersRef for gatewayclass %s", gc.Name) } - epList := new(egcfgv1a1.EnvoyProxyList) + epList := new(v1alpha1.EnvoyProxyList) + // The EnvoyProxy must be in the same namespace as EG. if err := r.client.List(ctx, epList, &client.ListOptions{Namespace: r.namespace}); err != nil { return fmt.Errorf("failed to list envoyproxies in namespace %s: %v", r.namespace, err) @@ -1372,3 +1733,26 @@ func (r *gatewayAPIReconciler) processParamsRef(ctx context.Context, gc *gwapiv1 return nil } + +// serviceImportCRDExists checks for the existence of the ServiceImport CRD in k8s APIServer before watching it +func (r *gatewayAPIReconciler) serviceImportCRDExists(mgr manager.Manager) bool { + discoveryClient, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig()) + if err != nil { + r.log.Error(err, "failed to create discovery client") + } + apiResourceList, err := discoveryClient.ServerPreferredResources() + if err != nil { + r.log.Error(err, "failed to get API resource list") + } + serviceImportFound := false + for _, list := range apiResourceList { + for _, resource := range list.APIResources { + if list.GroupVersion == mcsapi.GroupVersion.String() && resource.Kind == gatewayapi.KindServiceImport { + serviceImportFound = true + break + } + } + } + + return serviceImportFound +} diff --git a/internal/provider/kubernetes/controller_test.go b/internal/provider/kubernetes/controller_test.go index 840e80d5dcf..1aa58a1d533 100644 --- a/internal/provider/kubernetes/controller_test.go +++ b/internal/provider/kubernetes/controller_test.go @@ -14,10 +14,9 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -27,43 +26,43 @@ import ( func TestAddGatewayClassFinalizer(t *testing.T) { testCases := []struct { name string - gc *gwapiv1b1.GatewayClass + gc *gwapiv1.GatewayClass expect []string }{ { name: "gatewayclass with no finalizers", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, }, expect: []string{gatewayClassFinalizer}, }, { name: "gatewayclass with a different finalizer", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", Finalizers: []string{"fooFinalizer"}, }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, }, expect: []string{"fooFinalizer", gatewayClassFinalizer}, }, { name: "gatewayclass with existing gatewayclass finalizer", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", Finalizers: []string{gatewayClassFinalizer}, }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, }, expect: []string{gatewayClassFinalizer}, @@ -91,43 +90,43 @@ func TestAddGatewayClassFinalizer(t *testing.T) { func TestRemoveGatewayClassFinalizer(t *testing.T) { testCases := []struct { name string - gc *gwapiv1b1.GatewayClass + gc *gwapiv1.GatewayClass expect []string }{ { name: "gatewayclass with no finalizers", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, }, expect: nil, }, { name: "gatewayclass with a different finalizer", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", Finalizers: []string{"fooFinalizer"}, }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, }, expect: []string{"fooFinalizer"}, }, { name: "gatewayclass with existing gatewayclass finalizer", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", Finalizers: []string{gatewayClassFinalizer}, }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, }, expect: nil, @@ -152,131 +151,127 @@ func TestRemoveGatewayClassFinalizer(t *testing.T) { } } -func TestEnqueueManagedClass(t *testing.T) { - gcCtrlName := gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName) +func TestHasManagedClass(t *testing.T) { + gcCtrlName := gwapiv1.GatewayController(egv1a1.GatewayControllerName) testCases := []struct { name string ep client.Object - classes []*gwapiv1b1.GatewayClass - expected []reconcile.Request + classes []*gwapiv1.GatewayClass + expected bool }{ { name: "no matching gatewayclasses", - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-envoyproxy", }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "SomeOtherController", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-envoyproxy", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, }, }, }, }, }, - expected: []reconcile.Request{}, + expected: false, }, { name: "match one gatewayclass", - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-envoyproxy", }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-envoyproxy", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, }, }, }, }, }, - expected: []reconcile.Request{ - { - NamespacedName: types.NamespacedName{Name: "test-gc"}, - }, - }, + expected: true, }, { name: "envoyproxy in different namespace as eg", - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: "not-eg-ns", Name: "test-envoyproxy", }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ControllerName: gcCtrlName}, + Spec: gwapiv1.GatewayClassSpec{ControllerName: gcCtrlName}, }, }, - expected: []reconcile.Request{}, + expected: false, }, { name: "multiple gatewayclasses one with accepted status", - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-envoyproxy", }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "test-gc1", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-envoyproxy", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, }, }, @@ -286,30 +281,26 @@ func TestEnqueueManagedClass(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "test-gc2", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-envoyproxy", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, }, }, }, }, }, - expected: []reconcile.Request{ - { - NamespacedName: types.NamespacedName{Name: "test-gc1"}, - }, - }, + expected: true, }, } @@ -317,7 +308,7 @@ func TestEnqueueManagedClass(t *testing.T) { tc := testCases[i] // Create the reconciler. - logger := logging.DefaultLogger(egcfgv1a1.LogLevelInfo) + logger := logging.DefaultLogger(egv1a1.LogLevelInfo) r := &gatewayAPIReconciler{ log: logger, classController: gcCtrlName, @@ -339,38 +330,38 @@ func TestEnqueueManagedClass(t *testing.T) { Build() // Process the test case gatewayclasses. - results := r.enqueueManagedClass(context.TODO(), tc.ep) + results := r.hasManagedClass(tc.ep) require.Equal(t, tc.expected, results) }) } } func TestProcessParamsRef(t *testing.T) { - gcCtrlName := gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName) + gcCtrlName := gwapiv1.GatewayController(egv1a1.GatewayControllerName) testCases := []struct { name string - gc *gwapiv1b1.GatewayClass - ep *egcfgv1a1.EnvoyProxy + gc *gwapiv1.GatewayClass + ep *egv1a1.EnvoyProxy expected bool }{ { name: "valid envoyproxy reference", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test", @@ -380,15 +371,15 @@ func TestProcessParamsRef(t *testing.T) { }, { name: "envoyproxy kind does not exist", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, @@ -398,21 +389,21 @@ func TestProcessParamsRef(t *testing.T) { }, { name: "referenced envoyproxy does not exist", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "non-exist", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test", @@ -422,21 +413,21 @@ func TestProcessParamsRef(t *testing.T) { }, { name: "invalid gatewayclass parameters ref", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group("UnSupportedGroup"), - Kind: gwapiv1b1.Kind("UnSupportedKind"), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group("UnSupportedGroup"), + Kind: gwapiv1.Kind("UnSupportedKind"), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test", @@ -450,7 +441,7 @@ func TestProcessParamsRef(t *testing.T) { tc := testCases[i] // Create the reconciler. - logger := logging.DefaultLogger(egcfgv1a1.LogLevelInfo) + logger := logging.DefaultLogger(egv1a1.LogLevelInfo) r := &gatewayAPIReconciler{ log: logger, diff --git a/internal/provider/kubernetes/filters.go b/internal/provider/kubernetes/filters.go index 5f82322c589..d526e06d12e 100644 --- a/internal/provider/kubernetes/filters.go +++ b/internal/provider/kubernetes/filters.go @@ -10,30 +10,36 @@ import ( "fmt" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) -func (r *gatewayAPIReconciler) getAuthenticationFilters(ctx context.Context) ([]egv1a1.AuthenticationFilter, error) { - authenList := new(egv1a1.AuthenticationFilterList) - if err := r.client.List(ctx, authenList); err != nil { - return nil, fmt.Errorf("failed to list AuthenticationFilters: %v", err) - } - - return authenList.Items, nil -} - func (r *gatewayAPIReconciler) getExtensionRefFilters(ctx context.Context) ([]unstructured.Unstructured, error) { var resourceItems []unstructured.Unstructured for _, gvk := range r.extGVKs { - uExtResources := &unstructured.UnstructuredList{} - uExtResources.SetGroupVersionKind(gvk) - if err := r.client.List(ctx, uExtResources); err != nil { + uExtResourceList := &unstructured.UnstructuredList{} + uExtResourceList.SetGroupVersionKind(gvk) + if err := r.client.List(ctx, uExtResourceList); err != nil { r.log.Info("no associated resources found for %s", gvk.String()) return nil, fmt.Errorf("failed to list %s: %v", gvk.String(), err) } - resourceItems = append(resourceItems, uExtResources.Items...) + uExtResources := uExtResourceList.Items + if len(r.namespaceLabels) != 0 { + var extRs []unstructured.Unstructured + for _, extR := range uExtResources { + ns := extR.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return nil, fmt.Errorf("failed to check namespace labels for ExtensionRefFilter %s in namespace %s: %s", extR.GetName(), ns, err) + } + if ok { + extRs = append(extRs, extR) + } + } + uExtResources = extRs + } + + resourceItems = append(resourceItems, uExtResources...) } return resourceItems, nil diff --git a/internal/provider/kubernetes/helpers.go b/internal/provider/kubernetes/helpers.go index d96f17170c3..04bf83fecba 100644 --- a/internal/provider/kubernetes/helpers.go +++ b/internal/provider/kubernetes/helpers.go @@ -13,16 +13,16 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" - "github.com/envoyproxy/gateway/internal/envoygateway/config" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" - "github.com/envoyproxy/gateway/internal/provider/utils" + "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/proxy" ) const ( - gatewayClassFinalizer = gwapiv1b1.GatewayClassFinalizerGatewaysExist + gatewayClassFinalizer = gwapiv1.GatewayClassFinalizerGatewaysExist ) type ObjectKindNamespacedName struct { @@ -35,16 +35,16 @@ type ObjectKindNamespacedName struct { // referenced Gateways managed by Envoy Gateway. The only supported parentRef // is a Gateway. func validateParentRefs(ctx context.Context, client client.Client, namespace string, - gatewayClassController gwapiv1b1.GatewayController, - routeParentReferences []gwapiv1b1.ParentReference) ([]gwapiv1b1.Gateway, error) { + gatewayClassController gwapiv1.GatewayController, + routeParentReferences []gwapiv1.ParentReference) ([]gwapiv1.Gateway, error) { - var gateways []gwapiv1b1.Gateway + var gateways []gwapiv1.Gateway for i := range routeParentReferences { ref := routeParentReferences[i] if ref.Kind != nil && *ref.Kind != "Gateway" { return nil, fmt.Errorf("invalid Kind %q", *ref.Kind) } - if ref.Group != nil && *ref.Group != gwapiv1b1.GroupName { + if ref.Group != nil && *ref.Group != gwapiv1.GroupName { return nil, fmt.Errorf("invalid Group %q", *ref.Group) } @@ -59,13 +59,13 @@ func validateParentRefs(ctx context.Context, client client.Client, namespace str Name: string(ref.Name), } - gw := new(gwapiv1b1.Gateway) + gw := new(gwapiv1.Gateway) if err := client.Get(ctx, gwKey, gw); err != nil { return nil, fmt.Errorf("failed to get gateway %s/%s: %v", gwKey.Namespace, gwKey.Name, err) } gcKey := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)} - gc := new(gwapiv1b1.GatewayClass) + gc := new(gwapiv1.GatewayClass) if err := client.Get(ctx, gcKey, gc); err != nil { return nil, fmt.Errorf("failed to get gatewayclass %s: %v", gcKey.Name, err) } @@ -79,15 +79,15 @@ func validateParentRefs(ctx context.Context, client client.Client, namespace str type controlledClasses struct { // matchedClasses holds all GatewayClass objects with matching controllerName. - matchedClasses []*gwapiv1b1.GatewayClass + matchedClasses []*gwapiv1.GatewayClass // oldestClass stores the first GatewayClass encountered with matching // controllerName. This is maintained so that the oldestClass does not change // during reboots. - oldestClass *gwapiv1b1.GatewayClass + oldestClass *gwapiv1.GatewayClass } -func (cc *controlledClasses) addMatch(gc *gwapiv1b1.GatewayClass) { +func (cc *controlledClasses) addMatch(gc *gwapiv1.GatewayClass) { cc.matchedClasses = append(cc.matchedClasses, gc) switch { @@ -101,7 +101,7 @@ func (cc *controlledClasses) addMatch(gc *gwapiv1b1.GatewayClass) { } } -func (cc *controlledClasses) removeMatch(gc *gwapiv1b1.GatewayClass) { +func (cc *controlledClasses) removeMatch(gc *gwapiv1.GatewayClass) { // First remove gc from matchedClasses. for i, matchedGC := range cc.matchedClasses { if matchedGC.Name == gc.Name { @@ -132,12 +132,12 @@ func (cc *controlledClasses) removeMatch(gc *gwapiv1b1.GatewayClass) { } } -func (cc *controlledClasses) acceptedClass() *gwapiv1b1.GatewayClass { +func (cc *controlledClasses) acceptedClass() *gwapiv1.GatewayClass { return cc.oldestClass } -func (cc *controlledClasses) notAcceptedClasses() []*gwapiv1b1.GatewayClass { - var res []*gwapiv1b1.GatewayClass +func (cc *controlledClasses) notAcceptedClasses() []*gwapiv1.GatewayClass { + var res []*gwapiv1.GatewayClass for _, gc := range cc.matchedClasses { // skip the oldest one since it will be accepted. if gc.Name != cc.oldestClass.Name { @@ -150,12 +150,12 @@ func (cc *controlledClasses) notAcceptedClasses() []*gwapiv1b1.GatewayClass { // isAccepted returns true if the provided gatewayclass contains the Accepted=true // status condition. -func isAccepted(gc *gwapiv1b1.GatewayClass) bool { +func isAccepted(gc *gwapiv1.GatewayClass) bool { if gc == nil { return false } for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { return true } } @@ -163,8 +163,8 @@ func isAccepted(gc *gwapiv1b1.GatewayClass) bool { } // gatewaysOfClass returns a list of gateways that reference gc from the provided gwList. -func gatewaysOfClass(gc *gwapiv1b1.GatewayClass, gwList *gwapiv1b1.GatewayList) []gwapiv1b1.Gateway { - var gateways []gwapiv1b1.Gateway +func gatewaysOfClass(gc *gwapiv1.GatewayClass, gwList *gwapiv1.GatewayList) []gwapiv1.Gateway { + var gateways []gwapiv1.Gateway if gwList == nil || gc == nil { return gateways } @@ -179,31 +179,32 @@ func gatewaysOfClass(gc *gwapiv1b1.GatewayClass, gwList *gwapiv1b1.GatewayList) // terminatesTLS returns true if the provided gateway contains a listener configured // for TLS termination. -func terminatesTLS(listener *gwapiv1b1.Listener) bool { +func terminatesTLS(listener *gwapiv1.Listener) bool { if listener.TLS != nil && - (listener.Protocol == gwapiv1b1.HTTPSProtocolType || - listener.Protocol == gwapiv1b1.TLSProtocolType) && + (listener.Protocol == gwapiv1.HTTPSProtocolType || + listener.Protocol == gwapiv1.TLSProtocolType) && listener.TLS.Mode != nil && - *listener.TLS.Mode == gwapiv1b1.TLSModeTerminate { + *listener.TLS.Mode == gwapiv1.TLSModeTerminate { return true } return false } // refsSecret returns true if ref refers to a Secret. -func refsSecret(ref *gwapiv1b1.SecretObjectReference) bool { +func refsSecret(ref *gwapiv1.SecretObjectReference) bool { return (ref.Group == nil || *ref.Group == corev1.GroupName) && (ref.Kind == nil || *ref.Kind == gatewayapi.KindSecret) } -func infraServiceName(gateway *gwapiv1b1.Gateway) string { - infraName := utils.GetHashedName(fmt.Sprintf("%s-%s", gateway.Namespace, gateway.Name)) - return fmt.Sprintf("%s-%s", config.EnvoyPrefix, infraName) -} - -func infraDeploymentName(gateway *gwapiv1b1.Gateway) string { - infraName := utils.GetHashedName(fmt.Sprintf("%s-%s", gateway.Namespace, gateway.Name)) - return fmt.Sprintf("%s-%s", config.EnvoyPrefix, infraName) +// infraName returns expected name for the EnvoyProxy infra resources. +// By default it returns hashed string from {GatewayNamespace}/{GatewayName}, +// but if mergeGateways is set, it will return hashed string of {GatewayClassName}. +func infraName(gateway *gwapiv1.Gateway, merged bool) string { + if merged { + return proxy.ExpectedResourceHashedName(string(gateway.Spec.GatewayClassName)) + } + infraName := fmt.Sprintf("%s/%s", gateway.Namespace, gateway.Name) + return proxy.ExpectedResourceHashedName(infraName) } // validateBackendRef validates that ref is a reference to a local Service. @@ -211,22 +212,22 @@ func infraDeploymentName(gateway *gwapiv1b1.Gateway) string { // - Validating weights. // - Validating ports. // - Referencing HTTPRoutes. -func validateBackendRef(ref *gwapiv1b1.BackendRef) error { +func validateBackendRef(ref *gwapiv1.BackendRef) error { switch { case ref == nil: return nil - case ref.Group != nil && *ref.Group != corev1.GroupName: - return fmt.Errorf("invalid group; must be nil or empty string") - case ref.Kind != nil && *ref.Kind != gatewayapi.KindService: - return fmt.Errorf("invalid kind %q; must be %q", - *ref.BackendObjectReference.Kind, gatewayapi.KindService) + case gatewayapi.GroupDerefOr(ref.Group, corev1.GroupName) != corev1.GroupName && gatewayapi.GroupDerefOr(ref.Group, corev1.GroupName) != mcsapi.GroupName: + return fmt.Errorf("invalid group; must be nil, empty string or %q", mcsapi.GroupName) + case gatewayapi.KindDerefOr(ref.Kind, gatewayapi.KindService) != gatewayapi.KindService && gatewayapi.KindDerefOr(ref.Kind, gatewayapi.KindService) != gatewayapi.KindServiceImport: + return fmt.Errorf("invalid kind %q; must be %q or %q", + *ref.BackendObjectReference.Kind, gatewayapi.KindService, gatewayapi.KindServiceImport) } return nil } // classRefsEnvoyProxy returns true if the provided GatewayClass references the provided EnvoyProxy. -func classRefsEnvoyProxy(gc *gwapiv1b1.GatewayClass, ep *egcfgv1a1.EnvoyProxy) bool { +func classRefsEnvoyProxy(gc *gwapiv1.GatewayClass, ep *egv1a1.EnvoyProxy) bool { if gc == nil || ep == nil { return false } @@ -237,26 +238,26 @@ func classRefsEnvoyProxy(gc *gwapiv1b1.GatewayClass, ep *egcfgv1a1.EnvoyProxy) b } // refsEnvoyProxy returns true if the provided GatewayClass references an EnvoyProxy. -func refsEnvoyProxy(gc *gwapiv1b1.GatewayClass) bool { +func refsEnvoyProxy(gc *gwapiv1.GatewayClass) bool { if gc == nil { return false } return gc.Spec.ParametersRef != nil && - string(gc.Spec.ParametersRef.Group) == egcfgv1a1.GroupVersion.Group && - gc.Spec.ParametersRef.Kind == egcfgv1a1.KindEnvoyProxy && + string(gc.Spec.ParametersRef.Group) == egv1a1.GroupVersion.Group && + gc.Spec.ParametersRef.Kind == egv1a1.KindEnvoyProxy && gc.Spec.ParametersRef.Namespace != nil && len(gc.Spec.ParametersRef.Name) > 0 } // classAccepted returns true if the provided GatewayClass is accepted. -func classAccepted(gc *gwapiv1b1.GatewayClass) bool { +func classAccepted(gc *gwapiv1.GatewayClass) bool { if gc == nil { return false } for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { return true } diff --git a/internal/provider/kubernetes/helpers_test.go b/internal/provider/kubernetes/helpers_test.go index e3eed4225ef..1d88c38f0e2 100644 --- a/internal/provider/kubernetes/helpers_test.go +++ b/internal/provider/kubernetes/helpers_test.go @@ -11,34 +11,34 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" ) func TestGatewaysOfClass(t *testing.T) { - gc := &gwapiv1b1.GatewayClass{ + gc := &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, } testCases := []struct { name string - gws []gwapiv1b1.Gateway + gws []gwapiv1.Gateway expect int }{ { name: "no matching gateways", - gws: []gwapiv1b1.Gateway{ + gws: []gwapiv1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName("no-match"), + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName("no-match"), }, }, { @@ -46,8 +46,8 @@ func TestGatewaysOfClass(t *testing.T) { Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName("no-match2"), + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName("no-match2"), }, }, }, @@ -55,14 +55,14 @@ func TestGatewaysOfClass(t *testing.T) { }, { name: "one of two matching gateways", - gws: []gwapiv1b1.Gateway{ + gws: []gwapiv1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), }, }, { @@ -70,8 +70,8 @@ func TestGatewaysOfClass(t *testing.T) { Name: "test2", Namespace: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName("no-match"), + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName("no-match"), }, }, }, @@ -79,14 +79,14 @@ func TestGatewaysOfClass(t *testing.T) { }, { name: "two of two matching gateways", - gws: []gwapiv1b1.Gateway{ + gws: []gwapiv1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), }, }, { @@ -94,8 +94,8 @@ func TestGatewaysOfClass(t *testing.T) { Name: "test2", Namespace: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), }, }, }, @@ -106,7 +106,7 @@ func TestGatewaysOfClass(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - gwList := &gwapiv1b1.GatewayList{Items: tc.gws} + gwList := &gwapiv1.GatewayList{Items: tc.gws} actual := gatewaysOfClass(gc, gwList) require.Equal(t, tc.expect, len(actual)) }) @@ -116,22 +116,22 @@ func TestGatewaysOfClass(t *testing.T) { func TestIsGatewayClassAccepted(t *testing.T) { testCases := []struct { name string - gc *gwapiv1b1.GatewayClass + gc *gwapiv1.GatewayClass expect bool }{ { name: "gatewayclass accepted condition", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController(egv1a1.GatewayControllerName), }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, }, }, @@ -141,17 +141,17 @@ func TestIsGatewayClassAccepted(t *testing.T) { }, { name: "gatewayclass not accepted condition", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController(egv1a1.GatewayControllerName), }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, }, }, @@ -161,14 +161,14 @@ func TestIsGatewayClassAccepted(t *testing.T) { }, { name: "no gatewayclass accepted condition type", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController(egv1a1.GatewayControllerName), }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { Type: "SomeOtherType", @@ -195,14 +195,14 @@ func TestIsGatewayClassAccepted(t *testing.T) { } func TestGatewayOldestClass(t *testing.T) { - createGatewayClass := func(name string, creationTime time.Time) *gwapiv1b1.GatewayClass { - return &gwapiv1b1.GatewayClass{ + createGatewayClass := func(name string, creationTime time.Time) *gwapiv1.GatewayClass { + return &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: name, CreationTimestamp: metav1.NewTime(creationTime), }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: egcfgv1a1.GatewayControllerName, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: egv1a1.GatewayControllerName, }, } } @@ -291,7 +291,7 @@ func TestGatewayOldestClass(t *testing.T) { func TestRefsEnvoyProxy(t *testing.T) { testCases := []struct { name string - gc *gwapiv1b1.GatewayClass + gc *gwapiv1.GatewayClass expect bool }{ { @@ -301,16 +301,16 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "valid envoyproxy parameters ref", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, @@ -320,12 +320,12 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "unspecified parameters ref", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", }, }, @@ -333,16 +333,16 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "unsupported group parameters ref", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group("Unsupported"), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group("Unsupported"), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, @@ -352,16 +352,16 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "unsupported group parameters ref", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind("Unsupported"), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind("Unsupported"), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, @@ -371,16 +371,16 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "unsupported group parameters ref", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind("Unsupported"), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind("Unsupported"), Name: "test", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, @@ -390,16 +390,16 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "empty parameters ref name", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, @@ -409,16 +409,16 @@ func TestRefsEnvoyProxy(t *testing.T) { }, { name: "unspecified parameters ref namespace", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: "test", - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test", }, }, @@ -437,12 +437,12 @@ func TestRefsEnvoyProxy(t *testing.T) { } func TestClassRefsEnvoyProxy(t *testing.T) { - gcCtrlName := gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName) + gcCtrlName := gwapiv1.GatewayController(egv1a1.GatewayControllerName) testCases := []struct { name string - gc *gwapiv1b1.GatewayClass - ep *egcfgv1a1.EnvoyProxy + gc *gwapiv1.GatewayClass + ep *egv1a1.EnvoyProxy expected bool }{ { @@ -453,21 +453,21 @@ func TestClassRefsEnvoyProxy(t *testing.T) { }, { name: "gatewayclass references envoyproxy", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-ep", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-ep", @@ -477,21 +477,21 @@ func TestClassRefsEnvoyProxy(t *testing.T) { }, { name: "gatewayclass does not reference envoyproxy", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "not-test-ep", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-ep", @@ -501,21 +501,21 @@ func TestClassRefsEnvoyProxy(t *testing.T) { }, { name: "gatewayclass references invalid kind", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind("UnsupportedKind"), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind("UnsupportedKind"), Name: "test-ep", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-ep", @@ -525,21 +525,21 @@ func TestClassRefsEnvoyProxy(t *testing.T) { }, { name: "gatewayclass references invalid group", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group("UnsupportedGroup"), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group("UnsupportedGroup"), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-ep", Namespace: gatewayapi.NamespacePtr(config.DefaultNamespace), }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-ep", @@ -549,20 +549,20 @@ func TestClassRefsEnvoyProxy(t *testing.T) { }, { name: "gatewayclass references envoyproxy without namespace", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, - ParametersRef: &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + ParametersRef: &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy), Name: "test-ep", }, }, }, - ep: &egcfgv1a1.EnvoyProxy{ + ep: &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: config.DefaultNamespace, Name: "test-ep", @@ -585,11 +585,11 @@ func TestClassRefsEnvoyProxy(t *testing.T) { } func TestClassAccepted(t *testing.T) { - gcCtrlName := gwapiv1b1.GatewayController(egcfgv1a1.GatewayControllerName) + gcCtrlName := gwapiv1.GatewayController(egv1a1.GatewayControllerName) testCases := []struct { name string - gc *gwapiv1b1.GatewayClass + gc *gwapiv1.GatewayClass expected bool }{ { @@ -599,17 +599,17 @@ func TestClassAccepted(t *testing.T) { }, { name: "gatewayclass accepted", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, }, }, @@ -619,17 +619,17 @@ func TestClassAccepted(t *testing.T) { }, { name: "gatewayclass not accepted", - gc: &gwapiv1b1.GatewayClass{ + gc: &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gc", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, }, - Status: gwapiv1b1.GatewayClassStatus{ + Status: gwapiv1.GatewayClassStatus{ Conditions: []metav1.Condition{ { - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, }, }, diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go index 96fea419934..104400f2b05 100644 --- a/internal/provider/kubernetes/kubernetes.go +++ b/internal/provider/kubernetes/kubernetes.go @@ -11,6 +11,7 @@ import ( "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -30,7 +31,7 @@ type Provider struct { } // New creates a new Provider from the provided EnvoyGateway. -func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResources, eStatuses *message.EnvoyPatchPolicyStatuses) (*Provider, error) { +func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResources) (*Provider, error) { // TODO: Decide which mgr opts should be exposed through envoygateway.provider.kubernetes API. mgrOpts := manager.Options{ Scheme: envoygateway.GetScheme(), @@ -38,14 +39,14 @@ func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResour LeaderElection: false, HealthProbeBindAddress: ":8081", LeaderElectionID: "5b9825d2.gateway.envoyproxy.io", - MetricsBindAddress: ":8080", } - if svr.EnvoyGateway.Provider != nil && - svr.EnvoyGateway.Provider.Kubernetes != nil && - (svr.EnvoyGateway.Provider.Kubernetes.Watch != nil) && - (len(svr.EnvoyGateway.Provider.Kubernetes.Watch.Namespaces) > 0) { - mgrOpts.Cache.Namespaces = svr.EnvoyGateway.Provider.Kubernetes.Watch.Namespaces + // TODO: implement config validation on the watch mode config + if svr.EnvoyGateway.NamespaceMode() { + mgrOpts.Cache.DefaultNamespaces = make(map[string]cache.Config) + for _, watchNS := range svr.EnvoyGateway.Provider.Kubernetes.Watch.Namespaces { + mgrOpts.Cache.DefaultNamespaces[watchNS] = cache.Config{} + } } mgr, err := ctrl.NewManager(cfg, mgrOpts) @@ -59,7 +60,7 @@ func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResour } // Create and register the controllers with the manager. - if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources, eStatuses); err != nil { + if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources); err != nil { return nil, fmt.Errorf("failted to create gatewayapi controller: %w", err) } diff --git a/internal/provider/kubernetes/kubernetes_test.go b/internal/provider/kubernetes/kubernetes_test.go index a37a138ebef..39f96109ff8 100644 --- a/internal/provider/kubernetes/kubernetes_test.go +++ b/internal/provider/kubernetes/kubernetes_test.go @@ -24,14 +24,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -53,8 +53,7 @@ func TestProvider(t *testing.T) { svr, err := config.New() require.NoError(t, err) resources := new(message.ProviderResources) - ePatchPolicyStatuses := new(message.EnvoyPatchPolicyStatuses) - provider, err := New(cliCfg, svr, resources, ePatchPolicyStatuses) + provider, err := New(cliCfg, svr, resources) require.NoError(t, err) ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) go func() { @@ -74,8 +73,6 @@ func TestProvider(t *testing.T) { "gateway scheduled status": testGatewayScheduledStatus, "httproute": testHTTPRoute, "tlsroute": testTLSRoute, - "ratelimit filter": testRateLimitFilter, - "authentication filter": testAuthenFilter, "stale service cleanup route deletion": testServiceCleanupForMultipleRoutes, } for name, tc := range testcases { @@ -89,8 +86,10 @@ func startEnv() (*envtest.Environment, *rest.Config, error) { log.SetLogger(zap.New(zap.WriteTo(os.Stderr), zap.UseDevMode(true))) gwAPIs := filepath.Join("..", "..", "..", "charts", "gateway-helm", "crds", "gatewayapi-crds.yaml") egAPIs := filepath.Join("..", "..", "..", "charts", "gateway-helm", "crds", "generated") + mcsAPIs := filepath.Join(".", "testdata", "crds", "multicluster-svc.yaml") + env := &envtest.Environment{ - CRDDirectoryPaths: []string{gwAPIs, egAPIs}, + CRDDirectoryPaths: []string{gwAPIs, egAPIs, mcsAPIs}, } cfg, err := env.Start() if err != nil { @@ -99,10 +98,10 @@ func startEnv() (*envtest.Environment, *rest.Config, error) { return env, cfg, nil } -func testGatewayClassController(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { +func testGatewayClassController(ctx context.Context, t *testing.T, provider *Provider, _ *message.ProviderResources) { cli := provider.manager.GetClient() - gc := test.GetGatewayClass("test-gc-controllername", egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass("test-gc-controllername", egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) defer func() { @@ -118,7 +117,7 @@ func testGatewayClassController(ctx context.Context, t *testing.T, provider *Pro func testGatewayClassAcceptedStatus(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { cli := provider.manager.GetClient() - gc := test.GetGatewayClass("test-gc-accepted-status", egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass("test-gc-accepted-status", egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) defer func() { @@ -131,7 +130,7 @@ func testGatewayClassAcceptedStatus(ctx context.Context, t *testing.T, provider } for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { return true } } @@ -168,10 +167,10 @@ func testGatewayClassWithParamRef(ctx context.Context, t *testing.T, provider *P require.NoError(t, cli.Delete(ctx, ep)) }() - gc := test.GetGatewayClass("gc-with-param-ref", egcfgv1a1.GatewayControllerName) - gc.Spec.ParametersRef = &gwapiv1b1.ParametersReference{ - Group: gwapiv1b1.Group(egcfgv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egcfgv1a1.KindEnvoyProxy), + gc := test.GetGatewayClass("gc-with-param-ref", egv1a1.GatewayControllerName) + gc.Spec.ParametersRef = &gwapiv1.ParametersReference{ + Group: gwapiv1.Group(egv1a1.GroupVersion.Group), + Kind: egv1a1.KindEnvoyProxy, Name: epName, Namespace: gatewayapi.NamespacePtr(testNs), } @@ -188,7 +187,7 @@ func testGatewayClassWithParamRef(ctx context.Context, t *testing.T, provider *P } for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { return true } } @@ -197,15 +196,25 @@ func testGatewayClassWithParamRef(ctx context.Context, t *testing.T, provider *P }, defaultWait, defaultTick) // Ensure the resource map contains the EnvoyProxy. - res, ok := resources.GatewayAPIResources.Load(gc.Name) - assert.Equal(t, ok, true) - assert.Equal(t, res.EnvoyProxy.Spec, ep.Spec) + require.Eventually(t, func() bool { + res, ok := resources.GatewayAPIResources.Load(gc.Name) + if !ok { + return false + } + + if res.EnvoyProxy != nil { + assert.Equal(t, res.EnvoyProxy.Spec, ep.Spec) + return true + } + + return false + }, defaultWait, defaultTick) } func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { cli := provider.manager.GetClient() - gc := test.GetGatewayClass("gc-scheduled-status-test", egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass("gc-scheduled-status-test", egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) // Ensure the GatewayClass reports "Ready". @@ -215,7 +224,7 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro } for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { return true } } @@ -231,18 +240,18 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-gw-of-class"}} require.NoError(t, cli.Create(ctx, ns)) - gw := &gwapiv1b1.Gateway{ + gw := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "scheduled-status-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), + Listeners: []gwapiv1.Listener{ { Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), + Protocol: gwapiv1.HTTPProtocolType, }, }, }, @@ -311,7 +320,7 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro for _, cond := range gw.Status.Conditions { fmt.Printf("Condition: %v\n", cond) - if cond.Type == string(gwapiv1b1.GatewayConditionAccepted) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.GatewayConditionAccepted) && cond.Status == metav1.ConditionTrue { return true } } @@ -354,465 +363,10 @@ func testGatewayScheduledStatus(ctx context.Context, t *testing.T, provider *Pro assert.Equal(t, gw.Spec, res.Gateways[0].Spec) } -// Test that even when resources such as the Service/Deployment get hashed names (because of a gateway with a very long name) -func testLongNameHashedResources(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { - cli := provider.manager.GetClient() - - gc := test.GetGatewayClass("envoy-gateway-class", egcfgv1a1.GatewayControllerName) - require.NoError(t, cli.Create(ctx, gc)) - - // Ensure the GatewayClass reports "Ready". - require.Eventually(t, func() bool { - if err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc); err != nil { - return false - } - - for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { - return true - } - } - - return false - }, defaultWait, defaultTick) - - defer func() { - require.NoError(t, cli.Delete(ctx, gc)) - }() - - // Create the namespace for the Gateway under test. - ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "envoy-gateway"}} - require.NoError(t, cli.Create(ctx, ns)) - - gw := &gwapiv1b1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewaywithaverylongnamethatwillresultinhashedresources", - Namespace: ns.Name, - }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ - { - Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, - }, - }, - }, - } - require.NoError(t, cli.Create(ctx, gw)) - - // Ensure the Gateway is ready and gets an address. - ready := false - hasAddress := false - require.Eventually(t, func() bool { - if err := cli.Get(ctx, types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}, gw); err != nil { - return false - } - - for _, cond := range gw.Status.Conditions { - fmt.Printf("Condition: %v\n", cond) - if cond.Type == string(gwapiv1b1.GatewayConditionProgrammed) && cond.Status == metav1.ConditionTrue { - ready = true - } - } - - if gw.Status.Addresses != nil { - hasAddress = len(gw.Status.Addresses) >= 1 - } - - return ready && hasAddress - }, defaultWait, defaultTick) - - defer func() { - require.NoError(t, cli.Delete(ctx, gw)) - }() - - // Ensure the gatewayclass has been finalized. - require.Eventually(t, func() bool { - err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc) - return err == nil && slices.Contains(gc.Finalizers, gatewayClassFinalizer) - }, defaultWait, defaultTick) - - // Ensure the number of Gateways in the Gateway resource table is as expected. - require.Eventually(t, func() bool { - res, _ := resources.GatewayAPIResources.Load("envoy-gateway-class") - return len(res.Gateways) == 1 - }, defaultWait, defaultTick) - - // Ensure the test Gateway in the Gateway resources is as expected. - key := types.NamespacedName{ - Namespace: gw.Namespace, - Name: gw.Name, - } - require.Eventually(t, func() bool { - return cli.Get(ctx, key, gw) == nil - }, defaultWait, defaultTick) - res, _ := resources.GatewayAPIResources.Load("envoy-gateway-class") - // Only check if the spec is equal - // The watchable map will not store a resource - // with an updated status if the spec has not changed - // to eliminate this endless loop: - // reconcile->store->translate->update-status->reconcile - assert.Equal(t, gw.Spec, res.Gateways[0].Spec) -} - -func testRateLimitFilter(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { - cli := provider.manager.GetClient() - - gc := test.GetGatewayClass("ratelimit-test", egcfgv1a1.GatewayControllerName) - require.NoError(t, cli.Create(ctx, gc)) - - // Ensure the GatewayClass reports ready. - require.Eventually(t, func() bool { - if err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc); err != nil { - return false - } - - for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { - return true - } - } - - return false - }, defaultWait, defaultTick) - - defer func() { - require.NoError(t, cli.Delete(ctx, gc)) - }() - - // Create the namespace for the Gateway under test. - ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ratelimit-test"}} - require.NoError(t, cli.Create(ctx, ns)) - - gw := &gwapiv1b1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ratelimit-test", - Namespace: ns.Name, - }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ - { - Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, - }, - }, - }, - } - require.NoError(t, cli.Create(ctx, gw)) - - defer func() { - require.NoError(t, cli.Delete(ctx, gw)) - }() - - svc := test.GetService(types.NamespacedName{Namespace: ns.Name, Name: "test"}, nil, map[string]int32{ - "http": 80, - "https": 443, - }) - - require.NoError(t, cli.Create(ctx, svc)) - - defer func() { - require.NoError(t, cli.Delete(ctx, svc)) - }() - - rateLimitFilter := test.GetRateLimitFilter("ratelimit-test", ns.Name) - - require.NoError(t, cli.Create(ctx, rateLimitFilter)) - - defer func() { - require.NoError(t, cli.Delete(ctx, rateLimitFilter)) - }() - - var testCases = []struct { - name string - route gwapiv1b1.HTTPRoute - }{ - { - name: "ratelimit-test-httproute", - route: gwapiv1b1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ratelimit-test", - Namespace: ns.Name, - }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ - { - Name: gwapiv1b1.ObjectName(gw.Name), - }, - }, - }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ - { - Matches: []gwapiv1b1.HTTPRouteMatch{ - { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/ratelimitfilter/"), - }, - }, - }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ - { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Name: "test", - }, - }, - }, - }, - Filters: []gwapiv1b1.HTTPRouteFilter{ - { - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egv1a1.KindRateLimitFilter), - Name: gwapiv1b1.ObjectName("ratelimit-test"), - }, - }, - }, - }, - }, - }, - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - require.NoError(t, cli.Create(ctx, &testCase.route)) - defer func() { - require.NoError(t, cli.Delete(ctx, &testCase.route)) - }() - - require.Eventually(t, func() bool { - return resources.GatewayAPIResources.Len() != 0 - }, defaultWait, defaultTick) - - // Ensure the test HTTPRoute in the HTTPRoute resources is as expected. - key := types.NamespacedName{ - Namespace: testCase.route.Namespace, - Name: testCase.route.Name, - } - require.Eventually(t, func() bool { - return cli.Get(ctx, key, &testCase.route) == nil - }, defaultWait, defaultTick) - - require.Eventually(t, func() bool { - res, ok := resources.GatewayAPIResources.Load("ratelimit-test") - return ok && - len(res.HTTPRoutes) != 0 && - assert.Equal(t, testCase.route.Spec, res.HTTPRoutes[0].Spec) - }, defaultWait, defaultTick) - - // Ensure the RateLimitFilter is in the resource map. - require.Eventually(t, func() bool { - res, ok := resources.GatewayAPIResources.Load("ratelimit-test") - return ok && - len(res.RateLimitFilters) != 0 && - assert.Equal(t, rateLimitFilter.Spec, res.RateLimitFilters[0].Spec) - }, defaultWait, defaultTick) - - // Update the rate limit filter. - rateLimitFilter.Spec.Global.Rules = append(rateLimitFilter.Spec.Global.Rules, test.GetRateLimitGlobalRule("two")) - require.NoError(t, cli.Update(ctx, rateLimitFilter)) - - // Ensure the RateLimitFilter in the resource map has been updated. - require.Eventually(t, func() bool { - res, ok := resources.GatewayAPIResources.Load("ratelimit-test") - return ok && - len(res.RateLimitFilters) != 0 && - assert.Equal(t, 2, len(res.RateLimitFilters[0].Spec.Global.Rules)) - }, defaultWait, defaultTick) - }) - } -} - -func testAuthenFilter(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { - cli := provider.manager.GetClient() - - gc := test.GetGatewayClass("authen-test", egcfgv1a1.GatewayControllerName) - require.NoError(t, cli.Create(ctx, gc)) - - // Ensure the GatewayClass reports ready. - require.Eventually(t, func() bool { - if err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc); err != nil { - return false - } - - for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { - return true - } - } - - return false - }, defaultWait, defaultTick) - - defer func() { - require.NoError(t, cli.Delete(ctx, gc)) - }() - - // Create the namespace for the Gateway under test. - ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "authen-test"}} - require.NoError(t, cli.Create(ctx, ns)) - - gw := &gwapiv1b1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "authen-test", - Namespace: ns.Name, - }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ - { - Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, - }, - }, - }, - } - require.NoError(t, cli.Create(ctx, gw)) - - defer func() { - require.NoError(t, cli.Delete(ctx, gw)) - }() - - svc := test.GetService(types.NamespacedName{Namespace: ns.Name, Name: "test"}, nil, map[string]int32{ - "http": 80, - "https": 443, - }) - - require.NoError(t, cli.Create(ctx, svc)) - - defer func() { - require.NoError(t, cli.Delete(ctx, svc)) - }() - - authenFilter := test.GetAuthenticationFilter("test-authen", ns.Name) - - require.NoError(t, cli.Create(ctx, authenFilter)) - - defer func() { - require.NoError(t, cli.Delete(ctx, authenFilter)) - }() - - var testCases = []struct { - name string - route gwapiv1b1.HTTPRoute - }{ - { - name: "authenfilter-httproute", - route: gwapiv1b1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "httproute-authenfilter-test", - Namespace: ns.Name, - }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ - { - Name: gwapiv1b1.ObjectName(gw.Name), - }, - }, - }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ - { - Matches: []gwapiv1b1.HTTPRouteMatch{ - { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/authenfilter/"), - }, - }, - }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ - { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Name: "test", - }, - }, - }, - }, - Filters: []gwapiv1b1.HTTPRouteFilter{ - { - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egv1a1.KindAuthenticationFilter), - Name: gwapiv1b1.ObjectName(authenFilter.Name), - }, - }, - }, - }, - }, - }, - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - require.NoError(t, cli.Create(ctx, &testCase.route)) - defer func() { - require.NoError(t, cli.Delete(ctx, &testCase.route)) - }() - - require.Eventually(t, func() bool { - return resources.GatewayAPIResources.Len() != 0 - }, defaultWait, defaultTick) - - // Ensure the test HTTPRoute in the HTTPRoute resources is as expected. - key := types.NamespacedName{ - Namespace: testCase.route.Namespace, - Name: testCase.route.Name, - } - require.Eventually(t, func() bool { - return cli.Get(ctx, key, &testCase.route) == nil - }, defaultWait, defaultTick) - - require.Eventually(t, func() bool { - res, ok := resources.GatewayAPIResources.Load("authen-test") - return ok && - len(res.HTTPRoutes) != 0 && - assert.Equal(t, testCase.route.Spec, res.HTTPRoutes[0].Spec) - }, defaultWait, defaultTick) - - // Ensure the AuthenticationFilter is in the resource map. - require.Eventually(t, func() bool { - res, ok := resources.GatewayAPIResources.Load("authen-test") - return ok && - len(res.AuthenticationFilters) != 0 && - assert.Equal(t, authenFilter.Spec, res.AuthenticationFilters[0].Spec) - }, defaultWait, defaultTick) - - // Update the authn filter. - authenFilter.Spec.JwtProviders = append(authenFilter.Spec.JwtProviders, test.GetAuthenticationProvider("test2")) - require.NoError(t, cli.Update(ctx, authenFilter)) - - // Ensure the AuthenticationFilter in the resource map has been updated. - require.Eventually(t, func() bool { - res, ok := resources.GatewayAPIResources.Load("authen-test") - return ok && - len(res.AuthenticationFilters) != 0 && - assert.Equal(t, 2, len(res.AuthenticationFilters[0].Spec.JwtProviders)) - }, defaultWait, defaultTick) - }) - } -} - func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { cli := provider.manager.GetClient() - gc := test.GetGatewayClass("httproute-test", egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass("httproute-test", egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) // Ensure the GatewayClass reports ready. @@ -822,7 +376,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour } for _, cond := range gc.Status.Conditions { - if cond.Type == string(gwapiv1b1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { return true } } @@ -838,18 +392,18 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "httproute-test"}} require.NoError(t, cli.Create(ctx, ns)) - gw := &gwapiv1b1.Gateway{ + gw := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), + Listeners: []gwapiv1.Listener{ { Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), + Protocol: gwapiv1.HTTPProtocolType, }, }, }, @@ -871,55 +425,48 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour require.NoError(t, cli.Delete(ctx, svc)) }() - authenFilter := test.GetAuthenticationFilter("test-authen", ns.Name) - - require.NoError(t, cli.Create(ctx, authenFilter)) - - defer func() { - require.NoError(t, cli.Delete(ctx, authenFilter)) - }() - - redirectHostname := gwapiv1b1.PreciseHostname("redirect.hostname.local") - redirectPort := gwapiv1b1.PortNumber(8443) + redirectHostname := gwapiv1.PreciseHostname("redirect.hostname.local") + redirectPort := gwapiv1.PortNumber(8443) redirectStatus := 301 - rewriteHostname := gwapiv1b1.PreciseHostname("rewrite.hostname.local") + rewriteHostname := gwapiv1.PreciseHostname("rewrite.hostname.local") var testCases = []struct { name string - route gwapiv1b1.HTTPRoute + route gwapiv1.HTTPRoute }{ { name: "destination-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, @@ -931,48 +478,39 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "redirect-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-redirect-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/redirect/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/redirect/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + Filters: []gwapiv1.HTTPRouteFilter{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Name: "test", - }, - }, - }, - }, - Filters: []gwapiv1b1.HTTPRouteFilter{ - { - Type: gwapiv1b1.HTTPRouteFilterType("RequestRedirect"), - RequestRedirect: &gwapiv1b1.HTTPRequestRedirectFilter{ - Scheme: gatewayapi.StringPtr("https"), + Type: gwapiv1.HTTPRouteFilterType("RequestRedirect"), + RequestRedirect: &gwapiv1.HTTPRequestRedirectFilter{ + Scheme: ptr.To("https"), Hostname: &redirectHostname, - Path: &gwapiv1b1.HTTPPathModifier{ - Type: gwapiv1b1.HTTPPathModifierType("ReplaceFullPath"), - ReplaceFullPath: gatewayapi.StringPtr("/newpath"), + Path: &gwapiv1.HTTPPathModifier{ + Type: gwapiv1.HTTPPathModifierType("ReplaceFullPath"), + ReplaceFullPath: ptr.To("/newpath"), }, Port: &redirectPort, StatusCode: &redirectStatus, @@ -986,47 +524,48 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "rewrite-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-rewrite-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/rewrite/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/rewrite/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterType("URLRewrite"), - URLRewrite: &gwapiv1b1.HTTPURLRewriteFilter{ + Type: gwapiv1.HTTPRouteFilterType("URLRewrite"), + URLRewrite: &gwapiv1.HTTPURLRewriteFilter{ Hostname: &rewriteHostname, - Path: &gwapiv1b1.HTTPPathModifier{ - Type: gwapiv1b1.HTTPPathModifierType("ReplaceFullPath"), - ReplaceFullPath: gatewayapi.StringPtr("/newpath"), + Path: &gwapiv1.HTTPPathModifier{ + Type: gwapiv1.HTTPPathModifierType("ReplaceFullPath"), + ReplaceFullPath: ptr.To("/newpath"), }, }, }, @@ -1038,56 +577,57 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "add-request-header-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-add-request-header-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/addheader/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/addheader/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterType("RequestHeaderModifier"), - RequestHeaderModifier: &gwapiv1b1.HTTPHeaderFilter{ - Add: []gwapiv1b1.HTTPHeader{ + Type: gwapiv1.HTTPRouteFilterType("RequestHeaderModifier"), + RequestHeaderModifier: &gwapiv1.HTTPHeaderFilter{ + Add: []gwapiv1.HTTPHeader{ { - Name: gwapiv1b1.HTTPHeaderName("header-1"), + Name: gwapiv1.HTTPHeaderName("header-1"), Value: "value-1", }, { - Name: gwapiv1b1.HTTPHeaderName("header-2"), + Name: gwapiv1.HTTPHeaderName("header-2"), Value: "value-2", }, }, - Set: []gwapiv1b1.HTTPHeader{ + Set: []gwapiv1.HTTPHeader{ { - Name: gwapiv1b1.HTTPHeaderName("header-3"), + Name: gwapiv1.HTTPHeaderName("header-3"), Value: "value-3", }, }, @@ -1101,43 +641,44 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "remove-request-header-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-remove-request-header-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/remheader/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/remheader/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterType("RequestHeaderModifier"), - RequestHeaderModifier: &gwapiv1b1.HTTPHeaderFilter{ + Type: gwapiv1.HTTPRouteFilterType("RequestHeaderModifier"), + RequestHeaderModifier: &gwapiv1.HTTPHeaderFilter{ Remove: []string{ "example-header-1", "test-header", @@ -1153,56 +694,57 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "add-response-header-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-add-response-header-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/addheader/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/addheader/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterType("ResponseHeaderModifier"), - ResponseHeaderModifier: &gwapiv1b1.HTTPHeaderFilter{ - Add: []gwapiv1b1.HTTPHeader{ + Type: gwapiv1.HTTPRouteFilterType("ResponseHeaderModifier"), + ResponseHeaderModifier: &gwapiv1.HTTPHeaderFilter{ + Add: []gwapiv1.HTTPHeader{ { - Name: gwapiv1b1.HTTPHeaderName("header-1"), + Name: gwapiv1.HTTPHeaderName("header-1"), Value: "value-1", }, { - Name: gwapiv1b1.HTTPHeaderName("header-2"), + Name: gwapiv1.HTTPHeaderName("header-2"), Value: "value-2", }, }, - Set: []gwapiv1b1.HTTPHeader{ + Set: []gwapiv1.HTTPHeader{ { - Name: gwapiv1b1.HTTPHeaderName("header-3"), + Name: gwapiv1.HTTPHeaderName("header-3"), Value: "value-3", }, }, @@ -1216,43 +758,44 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "remove-response-header-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-remove-response-header-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/remheader/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/remheader/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterType("ResponseHeaderModifier"), - ResponseHeaderModifier: &gwapiv1b1.HTTPHeaderFilter{ + Type: gwapiv1.HTTPRouteFilterType("ResponseHeaderModifier"), + ResponseHeaderModifier: &gwapiv1.HTTPHeaderFilter{ Remove: []string{ "example-header-1", "test-header", @@ -1268,45 +811,47 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour }, { name: "mirror-httproute", - route: gwapiv1b1.HTTPRoute{ + route: gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-mirror-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Name: gwapiv1b1.ObjectName(gw.Name), + Name: gwapiv1.ObjectName(gw.Name), }, }, }, - Hostnames: []gwapiv1b1.Hostname{"test.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{ + Hostnames: []gwapiv1.Hostname{"test.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/mirror/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/mirror/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterType("RequestMirror"), - RequestMirror: &gwapiv1b1.HTTPRequestMirrorFilter{ - BackendRef: gwapiv1b1.BackendObjectReference{ + Type: gwapiv1.HTTPRouteFilterType("RequestMirror"), + RequestMirror: &gwapiv1.HTTPRequestMirrorFilter{ + BackendRef: gwapiv1.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }, @@ -1361,6 +906,11 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour // Ensure the Service is in the resource map. require.Eventually(t, func() bool { + // The redirect test case does not have a service. + if testCase.name == "redirect-httproute" { + return true + } + res, ok := resources.GatewayAPIResources.Load("httproute-test") if !ok { return false @@ -1379,7 +929,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { cli := provider.manager.GetClient() - gc := test.GetGatewayClass("tlsroute-test", egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass("tlsroute-test", egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) defer func() { @@ -1390,18 +940,21 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "tlsroute-test"}} require.NoError(t, cli.Create(ctx, ns)) - gw := &gwapiv1b1.Gateway{ + gw := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "tlsroute-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), + Listeners: []gwapiv1.Listener{ { Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.TLSProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), + Protocol: gwapiv1.TLSProtocolType, + TLS: &gwapiv1.GatewayTLSConfig{ + Mode: ptr.To(gwapiv1.TLSModePassthrough), + }, }, }, }, @@ -1446,6 +999,7 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc { BackendObjectReference: gwapiv1a2.BackendObjectReference{ Name: "test", + Port: ptr.To(gwapiv1.PortNumber(90)), }, }, }, @@ -1520,7 +1074,7 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) { cli := provider.manager.GetClient() - gc := test.GetGatewayClass("service-cleanup-test", egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass("service-cleanup-test", egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) defer func() { require.NoError(t, cli.Delete(ctx, gc)) @@ -1530,23 +1084,26 @@ func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, prov ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "service-cleanup-test"}} require.NoError(t, cli.Create(ctx, ns)) - gw := &gwapiv1b1.Gateway{ + gw := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "service-cleanup-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), + Listeners: []gwapiv1.Listener{ { Name: "httptest", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), + Protocol: gwapiv1.HTTPProtocolType, }, { Name: "tlstest", - Port: gwapiv1b1.PortNumber(int32(8043)), - Protocol: gwapiv1b1.TLSProtocolType, + Port: gwapiv1.PortNumber(int32(8043)), + Protocol: gwapiv1.TLSProtocolType, + TLS: &gwapiv1.GatewayTLSConfig{ + Mode: ptr.To(gwapiv1.TLSModePassthrough), + }, }, }, }, @@ -1581,35 +1138,37 @@ func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, prov BackendRefs: []gwapiv1a2.BackendRef{{ BackendObjectReference: gwapiv1a2.BackendObjectReference{ Name: "test-common-svc", + Port: ptr.To(gwapiv1.PortNumber(90)), }}, }}, }, }, } - httpRoute := gwapiv1b1.HTTPRoute{ + httpRoute := gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "httproute-test", Namespace: ns.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{{ - Name: gwapiv1b1.ObjectName(gw.Name), + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{{ + Name: gwapiv1.ObjectName(gw.Name), }}, }, - Hostnames: []gwapiv1b1.Hostname{"test-http.hostname.local"}, - Rules: []gwapiv1b1.HTTPRouteRule{{ - Matches: []gwapiv1b1.HTTPRouteMatch{{ - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), + Hostnames: []gwapiv1.Hostname{"test-http.hostname.local"}, + Rules: []gwapiv1.HTTPRouteRule{{ + Matches: []gwapiv1.HTTPRouteMatch{{ + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/"), }, }}, - BackendRefs: []gwapiv1b1.HTTPBackendRef{{ - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRefs: []gwapiv1.HTTPBackendRef{{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Name: "test-common-svc", + Port: ptr.To(gwapiv1.PortNumber(80)), }, }, }}, @@ -1675,15 +1234,15 @@ func TestNamespacedProvider(t *testing.T) { svr, err := config.New() require.NoError(t, err) // config to watch a subset of namespaces - svr.EnvoyGateway.Provider.Kubernetes = &egcfgv1a1.EnvoyGatewayKubernetesProvider{ - Watch: &egcfgv1a1.KubernetesWatchMode{ + svr.EnvoyGateway.Provider.Kubernetes = &egv1a1.EnvoyGatewayKubernetesProvider{ + Watch: &egv1a1.KubernetesWatchMode{ + Type: egv1a1.KubernetesWatchModeTypeNamespaces, Namespaces: []string{"ns1", "ns2"}, }, } resources := new(message.ProviderResources) - ePatchPolicyStatuses := new(message.EnvoyPatchPolicyStatuses) - provider, err := New(cliCfg, svr, resources, ePatchPolicyStatuses) + provider, err := New(cliCfg, svr, resources) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) go func() { @@ -1695,7 +1254,7 @@ func TestNamespacedProvider(t *testing.T) { cli := provider.manager.GetClient() gcName := "gc-watch-ns" - gc := test.GetGatewayClass(gcName, egcfgv1a1.GatewayControllerName) + gc := test.GetGatewayClass(gcName, egv1a1.GatewayControllerName) require.NoError(t, cli.Create(ctx, gc)) // Create the namespaces. @@ -1715,7 +1274,7 @@ func TestNamespacedProvider(t *testing.T) { require.NoError(t, cli.Create(ctx, gw3)) // Ensure only 2 gateways are reconciled - gatewayList := &gwapiv1b1.GatewayList{} + gatewayList := &gwapiv1.GatewayList{} require.NoError(t, cli.List(ctx, gatewayList)) assert.Equal(t, len(gatewayList.Items), 2) @@ -1724,5 +1283,259 @@ func TestNamespacedProvider(t *testing.T) { cancel() require.NoError(t, testEnv.Stop()) }() +} + +func TestNamespaceSelectorsProvider(t *testing.T) { + // Setup the test environment. + testEnv, cliCfg, err := startEnv() + require.NoError(t, err) + + // Setup and start the kube provider. + svr, err := config.New() + require.NoError(t, err) + // config to watch a subset of namespaces + svr.EnvoyGateway.Provider.Kubernetes = &egv1a1.EnvoyGatewayKubernetesProvider{ + Watch: &egv1a1.KubernetesWatchMode{ + Type: egv1a1.KubernetesWatchModeTypeNamespaceSelectors, + NamespaceSelectors: []string{"label-1", "label-2"}, + }, + } + + resources := new(message.ProviderResources) + provider, err := New(cliCfg, svr, resources) + require.NoError(t, err) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + require.NoError(t, provider.Start(ctx)) + }() + + defer func() { + cancel() + require.NoError(t, testEnv.Stop()) + }() + + cli := provider.manager.GetClient() + watchedNS := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{ + Name: "watched-ns", + Labels: map[string]string{"label-1": "true", "label-2": "true"}, + }} + require.NoError(t, cli.Create(ctx, watchedNS)) + nonWatchedNS := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "non-watched-ns"}} + require.NoError(t, cli.Create(ctx, nonWatchedNS)) + + gcName := "gc-name" + gc := test.GetGatewayClass(gcName, egv1a1.GatewayControllerName) + require.NoError(t, cli.Create(ctx, gc)) + + require.Eventually(t, func() bool { + if err := cli.Get(ctx, types.NamespacedName{Name: gc.Name}, gc); err != nil { + return false + } + + for _, cond := range gc.Status.Conditions { + if cond.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) && cond.Status == metav1.ConditionTrue { + return true + } + } + + return false + }, defaultWait, defaultTick) + + defer func() { + require.NoError(t, cli.Delete(ctx, gc)) + }() + + // Create the gateways + watchedGateway := test.GetGateway(types.NamespacedName{Name: "watched-gateway", Namespace: watchedNS.Name}, gcName) + require.NoError(t, cli.Create(ctx, watchedGateway)) + defer func() { + require.NoError(t, cli.Delete(ctx, watchedGateway)) + }() + + nonWatchedGateway := test.GetGateway(types.NamespacedName{Name: "non-watched-gateway", Namespace: nonWatchedNS.Name}, gcName) + require.NoError(t, cli.Create(ctx, nonWatchedGateway)) + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedGateway)) + }() + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + return res != nil && len(res.Gateways) == 1 + }, defaultWait, defaultTick) + + _, ok := resources.GatewayStatuses.Load(types.NamespacedName{Name: "non-watched-gateway", Namespace: nonWatchedNS.Name}) + require.Equal(t, false, ok) + + watchedSvc := test.GetService(types.NamespacedName{Namespace: watchedNS.Name, Name: "watched-service"}, nil, map[string]int32{ + "http": 80, + "https": 443, + }) + require.NoError(t, cli.Create(ctx, watchedSvc)) + + defer func() { + require.NoError(t, cli.Delete(ctx, watchedSvc)) + }() + + nonWatchedSvc := test.GetService(types.NamespacedName{Namespace: nonWatchedNS.Name, Name: "non-watched-service"}, nil, map[string]int32{ + "http": 8001, + "https": 44300, + }) + require.NoError(t, cli.Create(ctx, nonWatchedSvc)) + + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedSvc)) + }() + + watchedHTTPRoute := test.GetHTTPRoute( + types.NamespacedName{ + Namespace: watchedNS.Name, + Name: "watched-http-route", + }, + watchedGateway.Name, + types.NamespacedName{Name: watchedSvc.Name}, 80) + + require.NoError(t, cli.Create(ctx, watchedHTTPRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, watchedHTTPRoute)) + }() + + nonWatchedHTTPRoute := test.GetHTTPRoute( + types.NamespacedName{ + Namespace: nonWatchedNS.Name, + Name: "non-watched-http-route", + }, + nonWatchedGateway.Name, + types.NamespacedName{Name: nonWatchedSvc.Name}, 8001) + require.NoError(t, cli.Create(ctx, nonWatchedHTTPRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedHTTPRoute)) + }() + + watchedGRPCRoute := test.GetGRPCRoute( + types.NamespacedName{ + Namespace: watchedNS.Name, + Name: "watched-grpc-route", + }, + watchedGateway.Name, + types.NamespacedName{Name: watchedSvc.Name}, 80) + require.NoError(t, cli.Create(ctx, watchedGRPCRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, watchedGRPCRoute)) + }() + + nonWatchedGRPCRoute := test.GetGRPCRoute( + types.NamespacedName{ + Namespace: nonWatchedNS.Name, + Name: "non-watched-grpc-route", + }, + nonWatchedGateway.Name, + types.NamespacedName{Name: nonWatchedNS.Name}, 8001) + require.NoError(t, cli.Create(ctx, nonWatchedGRPCRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedGRPCRoute)) + }() + + watchedTCPRoute := test.GetTCPRoute( + types.NamespacedName{ + Namespace: watchedNS.Name, + Name: "watched-tcp-route", + }, + watchedGateway.Name, + types.NamespacedName{Name: watchedSvc.Name}, 80) + require.NoError(t, cli.Create(ctx, watchedTCPRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, watchedTCPRoute)) + }() + + nonWatchedTCPRoute := test.GetTCPRoute( + types.NamespacedName{ + Namespace: nonWatchedNS.Name, + Name: "non-watched-tcp-route", + }, + nonWatchedGateway.Name, + types.NamespacedName{Name: nonWatchedNS.Name}, 80) + require.NoError(t, cli.Create(ctx, nonWatchedTCPRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedTCPRoute)) + }() + + watchedTLSRoute := test.GetTLSRoute( + types.NamespacedName{ + Namespace: watchedNS.Name, + Name: "watched-tls-route", + }, + watchedGateway.Name, + types.NamespacedName{Name: watchedSvc.Name}, 443) + require.NoError(t, cli.Create(ctx, watchedTLSRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, watchedTLSRoute)) + }() + + nonWatchedTLSRoute := test.GetTLSRoute( + types.NamespacedName{ + Namespace: nonWatchedNS.Name, + Name: "non-watched-tls-route", + }, + nonWatchedGateway.Name, + types.NamespacedName{Name: nonWatchedNS.Name}, 443) + require.NoError(t, cli.Create(ctx, nonWatchedTLSRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedTLSRoute)) + }() + + watchedUDPRoute := test.GetUDPRoute( + types.NamespacedName{ + Namespace: watchedNS.Name, + Name: "watched-udp-route", + }, + watchedGateway.Name, + types.NamespacedName{Name: watchedSvc.Name}, 80) + require.NoError(t, cli.Create(ctx, watchedUDPRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, watchedUDPRoute)) + }() + + nonWatchedUDPRoute := test.GetUDPRoute( + types.NamespacedName{ + Namespace: nonWatchedNS.Name, + Name: "non-watched-udp-route", + }, + nonWatchedGateway.Name, + types.NamespacedName{Name: nonWatchedNS.Name}, 80) + require.NoError(t, cli.Create(ctx, nonWatchedUDPRoute)) + defer func() { + require.NoError(t, cli.Delete(ctx, nonWatchedUDPRoute)) + }() + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + // The service number dependes on the service created and the backendRef + return res != nil && len(res.Services) == 5 + }, defaultWait, defaultTick) + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + return res != nil && len(res.HTTPRoutes) == 1 + }, defaultWait, defaultTick) + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + return res != nil && len(res.TCPRoutes) == 1 + }, defaultWait, defaultTick) + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + return res != nil && len(res.TLSRoutes) == 1 + }, defaultWait, defaultTick) + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + return res != nil && len(res.UDPRoutes) == 1 + }, defaultWait, defaultTick) + + require.Eventually(t, func() bool { + res, _ := resources.GatewayAPIResources.Load(gc.Name) + return res != nil && len(res.GRPCRoutes) == 1 + }, defaultWait, defaultTick) } diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go index 2e20345fe07..0ffcfc369a4 100644 --- a/internal/provider/kubernetes/predicates.go +++ b/internal/provider/kubernetes/predicates.go @@ -15,10 +15,10 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/provider/utils" ) @@ -27,7 +27,7 @@ import ( // with a Spec.Controller string matching this Envoy Gateway's controller string, // or false otherwise. func (r *gatewayAPIReconciler) hasMatchingController(obj client.Object) bool { - gc, ok := obj.(*gwapiv1b1.GatewayClass) + gc, ok := obj.(*gwapiv1.GatewayClass) if !ok { r.log.Info("bypassing reconciliation due to unexpected object type", "type", obj) return false @@ -42,16 +42,75 @@ func (r *gatewayAPIReconciler) hasMatchingController(obj client.Object) bool { return false } +// hasMatchingNamespaceLabels returns true if the namespace of provided object has +// the provided labels or false otherwise. +func (r *gatewayAPIReconciler) hasMatchingNamespaceLabels(obj client.Object) bool { + ok, err := r.checkObjectNamespaceLabels(obj.GetNamespace()) + if err != nil { + r.log.Error( + err, "failed to get Namespace", + "object", obj.GetObjectKind().GroupVersionKind().String(), + "name", obj.GetName()) + return false + } + return ok +} + +type NamespaceGetter interface { + GetNamespace() string +} + +// checkObjectNamespaceLabels checks if labels of namespace of the object is a subset of namespaceLabels +// TODO: check if param can be an interface, so the caller doesn't need to get the namespace before calling +// this function. +func (r *gatewayAPIReconciler) checkObjectNamespaceLabels(nsString string) (bool, error) { + // TODO: add validation here because some objects don't have namespace + ns := &corev1.Namespace{} + if err := r.client.Get( + context.Background(), + client.ObjectKey{ + Namespace: "", // Namespace object should have empty Namespace + Name: nsString, + }, + ns, + ); err != nil { + return false, err + } + return containAll(ns.Labels, r.namespaceLabels), nil +} + +func containAll(labels map[string]string, labelsToCheck []string) bool { + if len(labels) < len(labelsToCheck) { + return false + } + for _, l := range labelsToCheck { + if !contains(labels, l) { + return false + } + } + return true +} + +func contains(m map[string]string, i string) bool { + for k := range m { + if k == i { + return true + } + } + + return false +} + // validateGatewayForReconcile returns true if the provided object is a Gateway // using a GatewayClass matching the configured gatewayclass controller name. func (r *gatewayAPIReconciler) validateGatewayForReconcile(obj client.Object) bool { - gw, ok := obj.(*gwapiv1b1.Gateway) + gw, ok := obj.(*gwapiv1.Gateway) if !ok { r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) return false } - gc := &gwapiv1b1.GatewayClass{} + gc := &gwapiv1.GatewayClass{} key := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)} if err := r.client.Get(context.Background(), key, gc); err != nil { r.log.Error(err, "failed to get gatewayclass", "name", gw.Spec.GatewayClassName) @@ -59,6 +118,7 @@ func (r *gatewayAPIReconciler) validateGatewayForReconcile(obj client.Object) bo } if gc.Spec.ControllerName != r.classController { + r.log.Info("gatewayclass controller name", gc.Spec.ControllerName, "class controller name", r.classController) r.log.Info("gatewayclass name for gateway doesn't match configured name", "namespace", gw.Namespace, "name", gw.Name) return false @@ -75,7 +135,7 @@ func (r *gatewayAPIReconciler) validateSecretForReconcile(obj client.Object) boo return false } - gwList := &gwapiv1b1.GatewayList{} + gwList := &gwapiv1.GatewayList{} if err := r.client.List(context.Background(), gwList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(secretGatewayIndex, utils.NamespacedName(secret).String()), }); err != nil { @@ -107,25 +167,54 @@ func (r *gatewayAPIReconciler) validateServiceForReconcile(obj client.Object) bo r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) return false } + labels := svc.GetLabels() // Check if the Service belongs to a Gateway, if so, update the Gateway status. - gtw := r.findOwningGateway(ctx, svc.GetLabels()) + gtw := r.findOwningGateway(ctx, labels) if gtw != nil { r.statusUpdateForGateway(ctx, gtw) return false } + // Only merged gateways will have this label, update status of all Gateways under found GatewayClass. + gclass, ok := labels[gatewayapi.OwningGatewayClassLabel] + if ok { + res, _ := r.resources.GatewayAPIResources.Load(gclass) + if res != nil && len(res.Gateways) > 0 { + for _, gw := range res.Gateways { + gw := gw + r.statusUpdateForGateway(ctx, gw) + } + } + + return false + } + nsName := utils.NamespacedName(svc) - return r.isRouteReferencingService(&nsName) + return r.isRouteReferencingBackend(&nsName) +} + +// validateServiceImportForReconcile tries finding the owning Gateway of the ServiceImport +// if it exists, finds the Gateway's Deployment, and further updates the Gateway +// status Ready condition. All Services are pushed for reconciliation. +func (r *gatewayAPIReconciler) validateServiceImportForReconcile(obj client.Object) bool { + svcImport, ok := obj.(*mcsapi.ServiceImport) + if !ok { + r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) + return false + } + + nsName := utils.NamespacedName(svcImport) + return r.isRouteReferencingBackend(&nsName) } -// isRouteReferencingService returns true if the service is referenced by any of the xRoutes +// isRouteReferencingBackend returns true if the backend(service and serviceImport) is referenced by any of the xRoutes // in the system, else returns false. -func (r *gatewayAPIReconciler) isRouteReferencingService(nsName *types.NamespacedName) bool { +func (r *gatewayAPIReconciler) isRouteReferencingBackend(nsName *types.NamespacedName) bool { ctx := context.Background() - httpRouteList := &gwapiv1b1.HTTPRouteList{} + httpRouteList := &gwapiv1.HTTPRouteList{} if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(serviceHTTPRouteIndex, nsName.String()), + FieldSelector: fields.OneTermEqualSelector(backendHTTPRouteIndex, nsName.String()), }); err != nil { r.log.Error(err, "unable to find associated HTTPRoutes") return false @@ -133,7 +222,7 @@ func (r *gatewayAPIReconciler) isRouteReferencingService(nsName *types.Namespace grpcRouteList := &gwapiv1a2.GRPCRouteList{} if err := r.client.List(ctx, grpcRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(serviceGRPCRouteIndex, nsName.String()), + FieldSelector: fields.OneTermEqualSelector(backendGRPCRouteIndex, nsName.String()), }); err != nil { r.log.Error(err, "unable to find associated GRPCRoutes") return false @@ -141,7 +230,7 @@ func (r *gatewayAPIReconciler) isRouteReferencingService(nsName *types.Namespace tlsRouteList := &gwapiv1a2.TLSRouteList{} if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(serviceTLSRouteIndex, nsName.String()), + FieldSelector: fields.OneTermEqualSelector(backendTLSRouteIndex, nsName.String()), }); err != nil { r.log.Error(err, "unable to find associated TLSRoutes") return false @@ -149,7 +238,7 @@ func (r *gatewayAPIReconciler) isRouteReferencingService(nsName *types.Namespace tcpRouteList := &gwapiv1a2.TCPRouteList{} if err := r.client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(serviceTCPRouteIndex, nsName.String()), + FieldSelector: fields.OneTermEqualSelector(backendTCPRouteIndex, nsName.String()), }); err != nil { r.log.Error(err, "unable to find associated TCPRoutes") return false @@ -157,13 +246,13 @@ func (r *gatewayAPIReconciler) isRouteReferencingService(nsName *types.Namespace udpRouteList := &gwapiv1a2.UDPRouteList{} if err := r.client.List(ctx, udpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(serviceUDPRouteIndex, nsName.String()), + FieldSelector: fields.OneTermEqualSelector(backendUDPRouteIndex, nsName.String()), }); err != nil { r.log.Error(err, "unable to find associated UDPRoutes") return false } - // Check how many Route objects refer this Service + // Check how many Route objects refer this Backend allAssociatedRoutes := len(httpRouteList.Items) + len(grpcRouteList.Items) + len(tlsRouteList.Items) + @@ -183,8 +272,9 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje } svcName, ok := ep.GetLabels()[discoveryv1.LabelServiceName] - if !ok { - r.log.Info("endpointslice is missing kubernetes.io/service-name label", "object", obj) + multiClusterSvcName, isMCS := ep.GetLabels()[mcsapi.LabelServiceName] + if !ok && !isMCS { + r.log.Info("endpointslice is missing kubernetes.io/service-name or multicluster.kubernetes.io/service-name label", "object", obj) return false } @@ -193,7 +283,11 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje Name: svcName, } - return r.isRouteReferencingService(&nsName) + if isMCS { + nsName.Name = multiClusterSvcName + } + + return r.isRouteReferencingBackend(&nsName) } // validateDeploymentForReconcile tries finding the owning Gateway of the Deployment @@ -206,70 +300,40 @@ func (r *gatewayAPIReconciler) validateDeploymentForReconcile(obj client.Object) r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) return false } + labels := deployment.GetLabels() // Only deployments in the configured namespace should be reconciled. if deployment.Namespace == r.namespace { // Check if the deployment belongs to a Gateway, if so, update the Gateway status. - gtw := r.findOwningGateway(ctx, deployment.GetLabels()) + gtw := r.findOwningGateway(ctx, labels) if gtw != nil { r.statusUpdateForGateway(ctx, gtw) return false } } - // There is no need to reconcile the Deployment any further. - return false -} - -// httpRoutesForAuthenticationFilter tries finding HTTPRoute referents of the provided -// AuthenticationFilter and returns true if any exist. -func (r *gatewayAPIReconciler) httpRoutesForAuthenticationFilter(obj client.Object) bool { - ctx := context.Background() - filter, ok := obj.(*egv1a1.AuthenticationFilter) - if !ok { - r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) - return false - } - - // Check if the AuthenticationFilter belongs to a managed HTTPRoute. - httpRouteList := &gwapiv1b1.HTTPRouteList{} - if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(authenFilterHTTPRouteIndex, utils.NamespacedName(filter).String()), - }); err != nil { - r.log.Error(err, "unable to find associated HTTPRoutes") - return false - } - - return len(httpRouteList.Items) != 0 -} - -// httpRoutesForRateLimitFilter tries finding HTTPRoute referents of the provided -// RateLimitFilter and returns true if any exist. -func (r *gatewayAPIReconciler) httpRoutesForRateLimitFilter(obj client.Object) bool { - ctx := context.Background() - filter, ok := obj.(*egv1a1.RateLimitFilter) - if !ok { - r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) - return false - } - - // Check if the RateLimitFilter belongs to a managed HTTPRoute. - httpRouteList := &gwapiv1b1.HTTPRouteList{} - if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(rateLimitFilterHTTPRouteIndex, utils.NamespacedName(filter).String()), - }); err != nil { - r.log.Error(err, "unable to find associated HTTPRoutes") + // Only merged gateways will have this label, update status of all Gateways under found GatewayClass. + gclass, ok := labels[gatewayapi.OwningGatewayClassLabel] + if ok { + res, _ := r.resources.GatewayAPIResources.Load(gclass) + if res != nil && len(res.Gateways) > 0 { + for _, gw := range res.Gateways { + gw := gw + r.statusUpdateForGateway(ctx, gw) + } + } return false } - return len(httpRouteList.Items) != 0 + // There is no need to reconcile the Deployment any further. + return false } // envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist. -func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*appsv1.Deployment, error) { +func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1.Gateway) (*appsv1.Deployment, error) { key := types.NamespacedName{ Namespace: r.namespace, - Name: infraDeploymentName(gateway), + Name: infraName(gateway, r.mergeGateways), } deployment := new(appsv1.Deployment) if err := r.client.Get(ctx, key, deployment); err != nil { @@ -282,10 +346,10 @@ func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, ga } // envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist. -func (r *gatewayAPIReconciler) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1b1.Gateway) (*corev1.Service, error) { +func (r *gatewayAPIReconciler) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1.Gateway) (*corev1.Service, error) { key := types.NamespacedName{ Namespace: r.namespace, - Name: infraServiceName(gateway), + Name: infraName(gateway, r.mergeGateways), } svc := new(corev1.Service) if err := r.client.Get(ctx, key, svc); err != nil { @@ -298,7 +362,7 @@ func (r *gatewayAPIReconciler) envoyServiceForGateway(ctx context.Context, gatew } // findOwningGateway attempts finds a Gateway using "labels". -func (r *gatewayAPIReconciler) findOwningGateway(ctx context.Context, labels map[string]string) *gwapiv1b1.Gateway { +func (r *gatewayAPIReconciler) findOwningGateway(ctx context.Context, labels map[string]string) *gwapiv1.Gateway { gwName, ok := labels[gatewayapi.OwningGatewayNameLabel] if !ok { return nil @@ -310,7 +374,7 @@ func (r *gatewayAPIReconciler) findOwningGateway(ctx context.Context, labels map } gatewayKey := types.NamespacedName{Namespace: gwNamespace, Name: gwName} - gtw := new(gwapiv1b1.Gateway) + gtw := new(gwapiv1.Gateway) if err := r.client.Get(ctx, gatewayKey, gtw); err != nil { r.log.Info("gateway not found", "namespace", gtw.Namespace, "name", gtw.Name) return nil diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go index d5c27d8ba7b..18eeba59550 100644 --- a/internal/provider/kubernetes/predicates_test.go +++ b/internal/provider/kubernetes/predicates_test.go @@ -9,13 +9,15 @@ import ( "testing" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/logging" @@ -28,6 +30,7 @@ func TestGatewayClassHasMatchingController(t *testing.T) { testCases := []struct { name string obj client.Object + client client.Client expect bool }{ { @@ -59,6 +62,77 @@ func TestGatewayClassHasMatchingController(t *testing.T) { } } +// TestGatewayClassHasMatchingNamespaceLabels tests the hasMatchingNamespaceLabels +// predicate function. +func TestGatewayClassHasMatchingNamespaceLabels(t *testing.T) { + ns := "namespace-1" + testCases := []struct { + name string + labels []string + namespaceLabels []string + expect bool + }{ + { + name: "matching one label when namespace has one label", + labels: []string{"label-1"}, + namespaceLabels: []string{"label-1"}, + expect: true, + }, + { + name: "matching one label when namespace has two labels", + labels: []string{"label-1"}, + namespaceLabels: []string{"label-1", "label-2"}, + expect: true, + }, + { + name: "namespace has less labels than the specified labels", + labels: []string{"label-1", "label-2"}, + namespaceLabels: []string{"label-1"}, + expect: false, + }, + } + + logger := logging.DefaultLogger(v1alpha1.LogLevelInfo) + + for _, tc := range testCases { + tc := tc + + namespaceLabelsToMap := make(map[string]string) + for _, l := range tc.namespaceLabels { + namespaceLabelsToMap[l] = "" + } + + r := gatewayAPIReconciler{ + classController: v1alpha1.GatewayControllerName, + namespaceLabels: tc.labels, + log: logger, + client: fakeclient.NewClientBuilder(). + WithScheme(envoygateway.GetScheme()). + WithObjects(&corev1.Namespace{ + TypeMeta: v1.TypeMeta{ + Kind: "Namespace", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{Name: ns, Labels: namespaceLabelsToMap}, + }). + Build(), + } + t.Run(tc.name, func(t *testing.T) { + res := r.hasMatchingNamespaceLabels( + test.GetHTTPRoute( + types.NamespacedName{ + Namespace: ns, + Name: "httproute-test", + }, + "scheduled-status-test", + types.NamespacedName{Name: "service"}, + 80, + )) + require.Equal(t, tc.expect, res) + }) + } +} + // TestValidateGatewayForReconcile tests the validateGatewayForReconcile // predicate function. func TestValidateGatewayForReconcile(t *testing.T) { @@ -156,7 +230,7 @@ func TestValidateSecretForReconcile(t *testing.T) { r.client = fakeclient.NewClientBuilder(). WithScheme(envoygateway.GetScheme()). WithObjects(tc.configs...). - WithIndex(&gwapiv1b1.Gateway{}, secretGatewayIndex, secretGatewayIndexFunc). + WithIndex(&gwapiv1.Gateway{}, secretGatewayIndex, secretGatewayIndexFunc). Build() t.Run(tc.name, func(t *testing.T) { res := r.validateSecretForReconcile(tc.secret) @@ -190,7 +264,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) { configs: []client.Object{ test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, - test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}), + test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80), }, endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "other-service"), expect: false, @@ -200,7 +274,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) { configs: []client.Object{ test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, - test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}), + test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80), }, endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service"), expect: true, @@ -220,11 +294,11 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) { r.client = fakeclient.NewClientBuilder(). WithScheme(envoygateway.GetScheme()). WithObjects(tc.configs...). - WithIndex(&gwapiv1b1.HTTPRoute{}, serviceHTTPRouteIndex, serviceHTTPRouteIndexFunc). - WithIndex(&gwapiv1a2.GRPCRoute{}, serviceGRPCRouteIndex, serviceGRPCRouteIndexFunc). - WithIndex(&gwapiv1a2.TLSRoute{}, serviceTLSRouteIndex, serviceTLSRouteIndexFunc). - WithIndex(&gwapiv1a2.TCPRoute{}, serviceTCPRouteIndex, serviceTCPRouteIndexFunc). - WithIndex(&gwapiv1a2.UDPRoute{}, serviceUDPRouteIndex, serviceUDPRouteIndexFunc). + WithIndex(&gwapiv1.HTTPRoute{}, backendHTTPRouteIndex, backendHTTPRouteIndexFunc). + WithIndex(&gwapiv1a2.GRPCRoute{}, backendGRPCRouteIndex, backendGRPCRouteIndexFunc). + WithIndex(&gwapiv1a2.TLSRoute{}, backendTLSRouteIndex, backendTLSRouteIndexFunc). + WithIndex(&gwapiv1a2.TCPRoute{}, backendTCPRouteIndex, backendTCPRouteIndexFunc). + WithIndex(&gwapiv1a2.UDPRoute{}, backendUDPRouteIndex, backendUDPRouteIndexFunc). Build() t.Run(tc.name, func(t *testing.T) { res := r.validateEndpointSliceForReconcile(tc.endpointSlice) @@ -261,7 +335,7 @@ func TestValidateServiceForReconcile(t *testing.T) { configs: []client.Object{ test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, - test.GetGatewayDeployment(types.NamespacedName{Name: infraDeploymentName(sampleGateway)}, nil), + test.GetGatewayDeployment(types.NamespacedName{Name: infraName(sampleGateway, false)}, nil), }, service: test.GetService(types.NamespacedName{Name: "service"}, map[string]string{ gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", @@ -285,7 +359,7 @@ func TestValidateServiceForReconcile(t *testing.T) { configs: []client.Object{ test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, - test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}), + test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80), }, service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil), expect: true, @@ -297,7 +371,7 @@ func TestValidateServiceForReconcile(t *testing.T) { name: "route service routes exist but with non-existing gateway reference", configs: []client.Object{ test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), - test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}), + test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80), }, service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil), expect: true, @@ -307,7 +381,7 @@ func TestValidateServiceForReconcile(t *testing.T) { configs: []client.Object{ test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, - test.GetGRPCRoute(types.NamespacedName{Name: "grpcroute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}), + test.GetGRPCRoute(types.NamespacedName{Name: "grpcroute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80), }, service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil), expect: true, @@ -318,7 +392,7 @@ func TestValidateServiceForReconcile(t *testing.T) { test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, test.GetTLSRoute(types.NamespacedName{Name: "tlsroute-test"}, "scheduled-status-test", - types.NamespacedName{Name: "service"}), + types.NamespacedName{Name: "service"}, 443), }, service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil), expect: true, @@ -329,7 +403,7 @@ func TestValidateServiceForReconcile(t *testing.T) { test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, test.GetUDPRoute(types.NamespacedName{Name: "udproute-test"}, "scheduled-status-test", - types.NamespacedName{Name: "service"}), + types.NamespacedName{Name: "service"}, 80), }, service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil), expect: true, @@ -340,7 +414,7 @@ func TestValidateServiceForReconcile(t *testing.T) { test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName), sampleGateway, test.GetTCPRoute(types.NamespacedName{Name: "tcproute-test"}, "scheduled-status-test", - types.NamespacedName{Name: "service"}), + types.NamespacedName{Name: "service"}, 80), }, service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil), expect: true, @@ -360,11 +434,11 @@ func TestValidateServiceForReconcile(t *testing.T) { r.client = fakeclient.NewClientBuilder(). WithScheme(envoygateway.GetScheme()). WithObjects(tc.configs...). - WithIndex(&gwapiv1b1.HTTPRoute{}, serviceHTTPRouteIndex, serviceHTTPRouteIndexFunc). - WithIndex(&gwapiv1a2.GRPCRoute{}, serviceGRPCRouteIndex, serviceGRPCRouteIndexFunc). - WithIndex(&gwapiv1a2.TLSRoute{}, serviceTLSRouteIndex, serviceTLSRouteIndexFunc). - WithIndex(&gwapiv1a2.TCPRoute{}, serviceTCPRouteIndex, serviceTCPRouteIndexFunc). - WithIndex(&gwapiv1a2.UDPRoute{}, serviceUDPRouteIndex, serviceUDPRouteIndexFunc). + WithIndex(&gwapiv1.HTTPRoute{}, backendHTTPRouteIndex, backendHTTPRouteIndexFunc). + WithIndex(&gwapiv1a2.GRPCRoute{}, backendGRPCRouteIndex, backendGRPCRouteIndexFunc). + WithIndex(&gwapiv1a2.TLSRoute{}, backendTLSRouteIndex, backendTLSRouteIndexFunc). + WithIndex(&gwapiv1a2.TCPRoute{}, backendTCPRouteIndex, backendTCPRouteIndexFunc). + WithIndex(&gwapiv1a2.UDPRoute{}, backendUDPRouteIndex, backendUDPRouteIndexFunc). Build() t.Run(tc.name, func(t *testing.T) { res := r.validateServiceForReconcile(tc.service) diff --git a/internal/provider/kubernetes/rbac.go b/internal/provider/kubernetes/rbac.go deleted file mode 100644 index d9fcc2c6ee0..00000000000 --- a/internal/provider/kubernetes/rbac.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package kubernetes - -// RBAC for Gateway API resources. -// +kubebuilder:rbac:groups="gateway.networking.k8s.io",resources=gatewayclasses;gateways;httproutes;grpcroutes;tlsroutes;tcproutes;udproutes;referencepolicies;referencegrants,verbs=get;list;watch;update;patch -// +kubebuilder:rbac:groups="gateway.networking.k8s.io",resources=gatewayclasses/status;gateways/status;httproutes/status;grpcroutes/status;tlsroutes/status;tcproutes/status;udproutes/status,verbs=update - -// RBAC for Envoy Gateway custom resources. -// +kubebuilder:rbac:groups="config.gateway.envoyproxy.io",resources=envoyproxies,verbs=get;list;watch;update -// +kubebuilder:rbac:groups="gateway.envoyproxy.io",resources=authenticationfilters;ratelimitfilters;envoypatchpolicies,verbs=get;list;watch;update;patch -// +kubebuilder:rbac:groups="gateway.envoyproxy.io",resources=envoypatchpolicies/status,verbs=update - -// RBAC for watched resources of Gateway API controllers. -// +kubebuilder:rbac:groups="",resources=secrets;services;namespaces;nodes,verbs=get;list;watch -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch -// +kubebuilder:rbac:groups=discovery.k8s.io,resources=endpointslices,verbs=get;list;watch diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go index 8f06d22fa23..a85277f9a29 100644 --- a/internal/provider/kubernetes/routes.go +++ b/internal/provider/kubernetes/routes.go @@ -8,15 +8,15 @@ package kubernetes import ( "context" "errors" + "fmt" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/provider/utils" ) @@ -33,7 +33,25 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName return err } - for _, tlsRoute := range tlsRouteList.Items { + tlsRoutes := tlsRouteList.Items + if len(r.namespaceLabels) != 0 { + var rts []gwapiv1a2.TLSRoute + for _, rt := range tlsRoutes { + ns := rt.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return fmt.Errorf("failed to check namespace labels for TLSRoute %s in namespace %s: %s", rt.GetName(), ns, err) + } + + if ok { + rts = append(rts, rt) + } + } + tlsRoutes = rts + } + + for _, tlsRoute := range tlsRoutes { tlsRoute := tlsRoute r.log.Info("processing TLSRoute", "namespace", tlsRoute.Namespace, "name", tlsRoute.Name) @@ -47,14 +65,16 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName } backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, tlsRoute.Namespace) - resourceMap.allAssociatedBackendRefs[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, }] = struct{}{} if backendNamespace != tlsRoute.Namespace { from := ObjectKindNamespacedName{kind: gatewayapi.KindTLSRoute, namespace: tlsRoute.Namespace, name: tlsRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} + to := ObjectKindNamespacedName{kind: gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService), namespace: backendNamespace, name: string(backendRef.Name)} refGrant, err := r.findReferenceGrant(ctx, from, to) switch { case err != nil: @@ -86,13 +106,33 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNamespaceName string, resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { grpcRouteList := &gwapiv1a2.GRPCRouteList{} + if err := r.client.List(ctx, grpcRouteList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(gatewayGRPCRouteIndex, gatewayNamespaceName), }); err != nil { r.log.Error(err, "failed to list GRPCRoutes") return err } - for _, grpcRoute := range grpcRouteList.Items { + + grpcRoutes := grpcRouteList.Items + if len(r.namespaceLabels) != 0 { + var grs []gwapiv1a2.GRPCRoute + for _, gr := range grpcRoutes { + ns := gr.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return fmt.Errorf("failed to check namespace labels for GRPCRoute %s in namespace %s: %s", gr.GetName(), ns, err) + } + if ok { + grs = append(grs, gr) + } + } + + grpcRoutes = grs + } + + for _, grpcRoute := range grpcRoutes { grpcRoute := grpcRoute r.log.Info("processing GRPCRoute", "namespace", grpcRoute.Namespace, "name", grpcRoute.Name) @@ -105,9 +145,11 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam } backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, grpcRoute.Namespace) - resourceMap.allAssociatedBackendRefs[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, }] = struct{}{} if backendNamespace != grpcRoute.Namespace { @@ -117,7 +159,7 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam name: grpcRoute.Name, } to := ObjectKindNamespacedName{ - kind: gatewayapi.KindService, + kind: gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService), namespace: backendNamespace, name: string(backendRef.Name), } @@ -146,6 +188,26 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam r.log.Error(err, "bypassing filter rule", "index", i) continue } + if filter.Type == gwapiv1a2.GRPCRouteFilterExtensionRef { + // NOTE: filters must be in the same namespace as the GRPCRoute + // Check if it's a Kind managed by an extension and add to resourceTree + key := types.NamespacedName{ + Namespace: grpcRoute.Namespace, + Name: string(filter.ExtensionRef.Name), + } + extRefFilter, ok := resourceMap.extensionRefFilters[key] + if !ok { + r.log.Error( + errors.New("filter not found; bypassing rule"), + "Filter not found; bypassing rule", + "name", + filter.ExtensionRef.Name, "index", i) + continue + } + + resourceTree.ExtensionRefFilters = append(resourceTree.ExtensionRefFilters, extRefFilter) + + } } } @@ -163,27 +225,7 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam // the backend references and pushes the HTTPRoutes to the resourceTree. func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNamespaceName string, resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { - httpRouteList := &gwapiv1b1.HTTPRouteList{} - - // An HTTPRoute may reference an AuthenticationFilter, RateLimitFilter, or a filter managed - // by an extension so add them to the resource map first (if they exist). - authenFilters, err := r.getAuthenticationFilters(ctx) - if err != nil { - return err - } - for i := range authenFilters { - filter := authenFilters[i] - resourceMap.authenFilters[utils.NamespacedName(&filter)] = &filter - } - - rateLimitFilters, err := r.getRateLimitFilters(ctx) - if err != nil { - return err - } - for i := range rateLimitFilters { - filter := rateLimitFilters[i] - resourceMap.rateLimitFilters[utils.NamespacedName(&filter)] = &filter - } + httpRouteList := &gwapiv1.HTTPRouteList{} extensionRefFilters, err := r.getExtensionRefFilters(ctx) if err != nil { @@ -200,7 +242,26 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Error(err, "failed to list HTTPRoutes") return err } - for _, httpRoute := range httpRouteList.Items { + + httpRoutes := httpRouteList.Items + if len(r.namespaceLabels) != 0 { + var hrs []gwapiv1.HTTPRoute + for _, hr := range httpRoutes { + ns := hr.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return fmt.Errorf("failed to check namespace labels for HTTPRoute %s in namespace %s: %s", hr.GetName(), ns, err) + } + + if ok { + hrs = append(hrs, hr) + } + } + httpRoutes = hrs + } + + for _, httpRoute := range httpRoutes { httpRoute := httpRoute r.log.Info("processing HTTPRoute", "namespace", httpRoute.Namespace, "name", httpRoute.Name) @@ -213,9 +274,11 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam } backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, httpRoute.Namespace) - resourceMap.allAssociatedBackendRefs[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, }] = struct{}{} if backendNamespace != httpRoute.Namespace { @@ -225,7 +288,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam name: httpRoute.Name, } to := ObjectKindNamespacedName{ - kind: gatewayapi.KindService, + kind: gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService), namespace: backendNamespace, name: string(backendRef.Name), } @@ -256,7 +319,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam } // Load in the backendRefs from any requestMirrorFilters on the HTTPRoute - if filter.Type == gwapiv1b1.HTTPRouteFilterRequestMirror { + if filter.Type == gwapiv1.HTTPRouteFilterRequestMirror { // Make sure the config actually exists mirrorFilter := filter.RequestMirror if mirrorFilter == nil { @@ -267,7 +330,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam mirrorBackendObj := mirrorFilter.BackendRef // Wrap the filter's BackendObjectReference into a BackendRef so we can use existing tooling to check it weight := int32(1) - mirrorBackendRef := gwapiv1b1.BackendRef{ + mirrorBackendRef := gwapiv1.BackendRef{ BackendObjectReference: mirrorBackendObj, Weight: &weight, } @@ -278,9 +341,11 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam } backendNamespace := gatewayapi.NamespaceDerefOr(mirrorBackendRef.Namespace, httpRoute.Namespace) - resourceMap.allAssociatedBackendRefs[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(mirrorBackendRef.Name), + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: mirrorBackendRef.BackendObjectReference.Group, + Kind: mirrorBackendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: mirrorBackendRef.Name, }] = struct{}{} if backendNamespace != httpRoute.Namespace { @@ -290,7 +355,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam name: httpRoute.Name, } to := ObjectKindNamespacedName{ - kind: gatewayapi.KindService, + kind: gatewayapi.KindDerefOr(mirrorBackendRef.Kind, gatewayapi.KindService), namespace: backendNamespace, name: string(mirrorBackendRef.Name), } @@ -307,48 +372,24 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam "name", refGrant.Name) } } - } else if filter.Type == gwapiv1b1.HTTPRouteFilterExtensionRef { + } else if filter.Type == gwapiv1.HTTPRouteFilterExtensionRef { // NOTE: filters must be in the same namespace as the HTTPRoute - switch string(filter.ExtensionRef.Kind) { - case egv1a1.KindAuthenticationFilter: - key := types.NamespacedName{ - Namespace: httpRoute.Namespace, - Name: string(filter.ExtensionRef.Name), - } - authFilter, ok := resourceMap.authenFilters[key] - if !ok { - r.log.Error(err, "AuthenticationFilter not found; bypassing rule", "index", i) - continue - } - - resourceTree.AuthenticationFilters = append(resourceTree.AuthenticationFilters, authFilter) - case egv1a1.KindRateLimitFilter: - key := types.NamespacedName{ - Namespace: httpRoute.Namespace, - Name: string(filter.ExtensionRef.Name), - } - rateLimitFilter, ok := resourceMap.rateLimitFilters[key] - if !ok { - r.log.Error(err, "RateLimitFilter not found; bypassing rule", "index", i) - continue - } - - resourceTree.RateLimitFilters = append(resourceTree.RateLimitFilters, rateLimitFilter) - default: - // If the Kind does not match any Envoy Gateway resources, check if it's a Kind - // managed by an extension and add to resourceTree - key := types.NamespacedName{ - Namespace: httpRoute.Namespace, - Name: string(filter.ExtensionRef.Name), - } - extRefFilter, ok := resourceMap.extensionRefFilters[key] - if !ok { - r.log.Error(err, "Filter not found; bypassing rule", "name", filter.ExtensionRef.Name, "index", i) - continue - } - - resourceTree.ExtensionRefFilters = append(resourceTree.ExtensionRefFilters, extRefFilter) + // Check if it's a Kind managed by an extension and add to resourceTree + key := types.NamespacedName{ + Namespace: httpRoute.Namespace, + Name: string(filter.ExtensionRef.Name), + } + extRefFilter, ok := resourceMap.extensionRefFilters[key] + if !ok { + r.log.Error( + errors.New("filter not found; bypassing rule"), + "Filter not found; bypassing rule", + "name", filter.ExtensionRef.Name, + "index", i) + continue } + + resourceTree.ExtensionRefFilters = append(resourceTree.ExtensionRefFilters, extRefFilter) } } } @@ -356,7 +397,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam resourceMap.allAssociatedNamespaces[httpRoute.Namespace] = struct{}{} // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - httpRoute.Status = gwapiv1b1.HTTPRouteStatus{} + httpRoute.Status = gwapiv1.HTTPRouteStatus{} resourceTree.HTTPRoutes = append(resourceTree.HTTPRoutes, &httpRoute) } @@ -375,7 +416,26 @@ func (r *gatewayAPIReconciler) processTCPRoutes(ctx context.Context, gatewayName return err } - for _, tcpRoute := range tcpRouteList.Items { + tcpRoutes := tcpRouteList.Items + if len(r.namespaceLabels) != 0 { + var trs []gwapiv1a2.TCPRoute + for _, tr := range tcpRoutes { + ns := tr.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return fmt.Errorf("failed to check namespace labels for TCPRoute %s in namespace %s: %s", tr.GetName(), ns, err) + } + + if ok { + trs = append(trs, tr) + } + } + + tcpRoutes = trs + } + + for _, tcpRoute := range tcpRoutes { tcpRoute := tcpRoute r.log.Info("processing TCPRoute", "namespace", tcpRoute.Namespace, "name", tcpRoute.Name) @@ -389,14 +449,16 @@ func (r *gatewayAPIReconciler) processTCPRoutes(ctx context.Context, gatewayName } backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, tcpRoute.Namespace) - resourceMap.allAssociatedBackendRefs[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, }] = struct{}{} if backendNamespace != tcpRoute.Namespace { from := ObjectKindNamespacedName{kind: gatewayapi.KindTCPRoute, namespace: tcpRoute.Namespace, name: tcpRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} + to := ObjectKindNamespacedName{kind: gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService), namespace: backendNamespace, name: string(backendRef.Name)} refGrant, err := r.findReferenceGrant(ctx, from, to) switch { case err != nil: @@ -435,7 +497,26 @@ func (r *gatewayAPIReconciler) processUDPRoutes(ctx context.Context, gatewayName return err } - for _, udpRoute := range udpRouteList.Items { + udpRoutes := udpRouteList.Items + if len(r.namespaceLabels) != 0 { + var urs []gwapiv1a2.UDPRoute + for _, ur := range udpRoutes { + ns := ur.GetNamespace() + ok, err := r.checkObjectNamespaceLabels(ns) + if err != nil { + // TODO: should return? or just proceed? + return fmt.Errorf("failed to check namespace labels for UDPRoute %s in namespace %s: %s", ur.GetName(), ns, err) + } + + if ok { + urs = append(urs, ur) + } + } + + udpRoutes = urs + } + + for _, udpRoute := range udpRoutes { udpRoute := udpRoute r.log.Info("processing UDPRoute", "namespace", udpRoute.Namespace, "name", udpRoute.Name) @@ -449,14 +530,16 @@ func (r *gatewayAPIReconciler) processUDPRoutes(ctx context.Context, gatewayName } backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, udpRoute.Namespace) - resourceMap.allAssociatedBackendRefs[types.NamespacedName{ - Namespace: backendNamespace, - Name: string(backendRef.Name), + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, }] = struct{}{} if backendNamespace != udpRoute.Namespace { from := ObjectKindNamespacedName{kind: gatewayapi.KindUDPRoute, namespace: udpRoute.Namespace, name: udpRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindService, namespace: backendNamespace, name: string(backendRef.Name)} + to := ObjectKindNamespacedName{kind: gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService), namespace: backendNamespace, name: string(backendRef.Name)} refGrant, err := r.findReferenceGrant(ctx, from, to) switch { case err != nil: diff --git a/internal/provider/kubernetes/routes_test.go b/internal/provider/kubernetes/routes_test.go index d6515946298..c4f46b5693e 100644 --- a/internal/provider/kubernetes/routes_test.go +++ b/internal/provider/kubernetes/routes_test.go @@ -16,11 +16,12 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - cfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -30,29 +31,29 @@ import ( func TestProcessHTTPRoutes(t *testing.T) { // The gatewayclass configured for the reconciler and referenced by test cases. - gcCtrlName := gwapiv1b1.GatewayController(cfgv1a1.GatewayControllerName) - gc := &gwapiv1b1.GatewayClass{ + gcCtrlName := gwapiv1.GatewayController(egv1a1.GatewayControllerName) + gc := &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: gcCtrlName, }, } // The gateway referenced by test cases. - gw := &gwapiv1b1.Gateway{ + gw := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gc.Name), - Listeners: []gwapiv1b1.Listener{ + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), + Listeners: []gwapiv1.Listener{ { Name: "http", - Protocol: gwapiv1b1.HTTPProtocolType, - Port: gwapiv1b1.PortNumber(int32(8080)), + Protocol: gwapiv1.HTTPProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), }, }, }, @@ -61,43 +62,41 @@ func TestProcessHTTPRoutes(t *testing.T) { testCases := []struct { name string - routes []*gwapiv1b1.HTTPRoute - authenFilters []*egv1a1.AuthenticationFilter - rateLimitFilters []*egv1a1.RateLimitFilter + routes []*gwapiv1.HTTPRoute extensionFilters []*unstructured.Unstructured extensionAPIGroups []schema.GroupVersionKind expected bool }{ { name: "valid httproute", - routes: []*gwapiv1b1.HTTPRoute{ + routes: []*gwapiv1.HTTPRoute{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { Name: "test", }, }, }, - Rules: []gwapiv1b1.HTTPRouteRule{ + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Group: gatewayapi.GroupPtr(corev1.GroupName), Kind: gatewayapi.KindPtr(gatewayapi.KindService), Name: "test", @@ -113,45 +112,45 @@ func TestProcessHTTPRoutes(t *testing.T) { expected: true, }, { - name: "httproute with one authenticationfilter", - routes: []*gwapiv1b1.HTTPRoute{ + name: "httproute with one filter_from_extension", + routes: []*gwapiv1.HTTPRoute{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { Name: "test", }, }, }, - Rules: []gwapiv1b1.HTTPRouteRule{ + Rules: []gwapiv1.HTTPRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1.HTTPRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), + Path: &gwapiv1.HTTPPathMatch{ + Type: ptr.To(gwapiv1.PathMatchPathPrefix), + Value: ptr.To("/"), }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + Filters: []gwapiv1.HTTPRouteFilter{ { - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egv1a1.KindAuthenticationFilter), - Name: gwapiv1b1.ObjectName("test"), + Type: gwapiv1.HTTPRouteFilterExtensionRef, + ExtensionRef: &gwapiv1.LocalObjectReference{ + Group: gwapiv1.Group("gateway.example.io"), + Kind: gwapiv1.Kind("Foo"), + Name: gwapiv1.ObjectName("test"), }, }, }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Group: gatewayapi.GroupPtr(corev1.GroupName), Kind: gatewayapi.KindPtr(gatewayapi.KindService), Name: "test", @@ -164,276 +163,152 @@ func TestProcessHTTPRoutes(t *testing.T) { }, }, }, - authenFilters: []*egv1a1.AuthenticationFilter{ - { - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "httproute with one rateLimitfilter", - routes: []*gwapiv1b1.HTTPRoute{ + extensionFilters: []*unstructured.Unstructured{ { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ - { - Name: "test", - }, - }, - }, - Rules: []gwapiv1b1.HTTPRouteRule{ - { - Matches: []gwapiv1b1.HTTPRouteMatch{ - { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), - }, - }, - }, - Filters: []gwapiv1b1.HTTPRouteFilter{ - { - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egv1a1.KindRateLimitFilter), - Name: gwapiv1b1.ObjectName("test"), - }, - }, - }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ - { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Group: gatewayapi.GroupPtr(corev1.GroupName), - Kind: gatewayapi.KindPtr(gatewayapi.KindService), - Name: "test", - }, - }, - }, - }, - }, + Object: map[string]interface{}{ + "apiVersion": "gateway.example.io/v1alpha1", + "kind": "Foo", + "metadata": map[string]interface{}{ + "name": "test", + "namespace": "test", }, }, }, }, - rateLimitFilters: []*egv1a1.RateLimitFilter{ + extensionAPIGroups: []schema.GroupVersionKind{ { - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindRateLimitFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.RateLimitFilterSpec{ - Type: egv1a1.GlobalRateLimitType, - Global: &egv1a1.GlobalRateLimit{ - Rules: []egv1a1.RateLimitRule{ - { - ClientSelectors: []egv1a1.RateLimitSelectCondition{ - { - Headers: []egv1a1.HeaderMatch{ - { - Name: "x-user-id", - Value: gatewayapi.StringPtr("one"), - }, - }, - }, - }, - Limit: egv1a1.RateLimitValue{ - Requests: 5, - Unit: "Second", - }, - }, - }, - }, - }, + Group: "gateway.example.io", + Version: "v1alpha1", + Kind: "Foo", }, }, expected: true, }, - { - name: "httproute with one authenticationfilter and ratelimitfilter", - routes: []*gwapiv1b1.HTTPRoute{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ - { - Name: "test", - }, - }, - }, - Rules: []gwapiv1b1.HTTPRouteRule{ - { - Matches: []gwapiv1b1.HTTPRouteMatch{ - { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), - }, - }, - }, - Filters: []gwapiv1b1.HTTPRouteFilter{ - { - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), - Kind: gwapiv1b1.Kind(egv1a1.KindAuthenticationFilter), - Name: gwapiv1b1.ObjectName("test"), - }, - }, - }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ - { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Group: gatewayapi.GroupPtr(corev1.GroupName), - Kind: gatewayapi.KindPtr(gatewayapi.KindService), - Name: "test", - }, - }, - }, - }, - }, - }, - }, - }, - }, - authenFilters: []*egv1a1.AuthenticationFilter{ - { - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{ - { - Name: "test", - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - }, - }, - }, - }, - }, - rateLimitFilters: []*egv1a1.RateLimitFilter{ + } + + for i := range testCases { + tc := testCases[i] + // Run the test cases. + t.Run(tc.name, func(t *testing.T) { + // Add objects referenced by test cases. + objs := []client.Object{gc, gw} + + // Create the reconciler. + logger := logging.DefaultLogger(egv1a1.LogLevelInfo) + + ctx := context.Background() + + r := &gatewayAPIReconciler{ + log: logger, + classController: gcCtrlName, + } + + // Add the test case objects to the reconciler client. + for _, route := range tc.routes { + objs = append(objs, route) + } + for _, filter := range tc.extensionFilters { + objs = append(objs, filter) + } + if len(tc.extensionAPIGroups) > 0 { + r.extGVKs = append(r.extGVKs, tc.extensionAPIGroups...) + } + r.client = fakeclient.NewClientBuilder(). + WithScheme(envoygateway.GetScheme()). + WithObjects(objs...). + WithIndex(&gwapiv1.HTTPRoute{}, gatewayHTTPRouteIndex, gatewayHTTPRouteIndexFunc). + Build() + + // Process the test case httproutes. + resourceTree := gatewayapi.NewResources() + resourceMap := newResourceMapping() + err := r.processHTTPRoutes(ctx, gwNsName, resourceMap, resourceTree) + if tc.expected { + require.NoError(t, err) + // Ensure the resource tree and map are as expected. + require.Equal(t, tc.routes, resourceTree.HTTPRoutes) + if tc.extensionFilters != nil { + for i, filter := range tc.extensionFilters { + key := types.NamespacedName{ + Namespace: tc.routes[i].Namespace, + Name: filter.GetName(), + } + require.Equal(t, *filter, resourceMap.extensionRefFilters[key]) + } + } + } else { + require.Error(t, err) + } + }) + } +} + +func TestProcessGRPCRoutes(t *testing.T) { + // The gatewayclass configured for the reconciler and referenced by test cases. + gcCtrlName := gwapiv1.GatewayController(egv1a1.GatewayControllerName) + gc := &gwapiv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gcCtrlName, + }, + } + + // The gateway referenced by test cases. + gw := &gwapiv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gc.Name), + Listeners: []gwapiv1.Listener{ { - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindRateLimitFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "test", - }, - Spec: egv1a1.RateLimitFilterSpec{ - Type: egv1a1.GlobalRateLimitType, - Global: &egv1a1.GlobalRateLimit{ - Rules: []egv1a1.RateLimitRule{ - { - ClientSelectors: []egv1a1.RateLimitSelectCondition{ - { - Headers: []egv1a1.HeaderMatch{ - { - Name: "x-user-id", - Value: gatewayapi.StringPtr("one"), - }, - }, - }, - }, - Limit: egv1a1.RateLimitValue{ - Requests: 5, - Unit: "Second", - }, - }, - }, - }, - }, + Name: "http", + Protocol: gwapiv1.HTTPProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), }, }, - expected: true, }, + } + gwNsName := utils.NamespacedName(gw).String() + + testCases := []struct { + name string + routes []*gwapiv1a2.GRPCRoute + extensionAPIGroups []schema.GroupVersionKind + expected bool + }{ { - name: "httproute with one filter_from_extension", - routes: []*gwapiv1b1.HTTPRoute{ + name: "valid grpcroute", + routes: []*gwapiv1a2.GRPCRoute{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1a2.GRPCRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { Name: "test", }, }, }, - Rules: []gwapiv1b1.HTTPRouteRule{ + Rules: []gwapiv1a2.GRPCRouteRule{ { - Matches: []gwapiv1b1.HTTPRouteMatch{ + Matches: []gwapiv1a2.GRPCRouteMatch{ { - Path: &gwapiv1b1.HTTPPathMatch{ - Type: gatewayapi.PathMatchTypePtr(gwapiv1b1.PathMatchPathPrefix), - Value: gatewayapi.StringPtr("/"), + Method: &gwapiv1a2.GRPCMethodMatch{ + Method: ptr.To("Ping"), }, }, }, - Filters: []gwapiv1b1.HTTPRouteFilter{ + BackendRefs: []gwapiv1a2.GRPCBackendRef{ { - Type: gwapiv1b1.HTTPRouteFilterExtensionRef, - ExtensionRef: &gwapiv1b1.LocalObjectReference{ - Group: gwapiv1b1.Group("gateway.example.io"), - Kind: gwapiv1b1.Kind("Foo"), - Name: gwapiv1b1.ObjectName("test"), - }, - }, - }, - BackendRefs: []gwapiv1b1.HTTPBackendRef{ - { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ Group: gatewayapi.GroupPtr(corev1.GroupName), Kind: gatewayapi.KindPtr(gatewayapi.KindService), Name: "test", @@ -446,25 +321,6 @@ func TestProcessHTTPRoutes(t *testing.T) { }, }, }, - extensionFilters: []*unstructured.Unstructured{ - { - Object: map[string]interface{}{ - "apiVersion": "gateway.example.io/v1alpha1", - "kind": "Foo", - "metadata": map[string]interface{}{ - "name": "test", - "namespace": "test", - }, - }, - }, - }, - extensionAPIGroups: []schema.GroupVersionKind{ - { - Group: "gateway.example.io", - Version: "v1alpha1", - Kind: "Foo", - }, - }, expected: true, }, } @@ -477,7 +333,7 @@ func TestProcessHTTPRoutes(t *testing.T) { objs := []client.Object{gc, gw} // Create the reconciler. - logger := logging.DefaultLogger(cfgv1a1.LogLevelInfo) + logger := logging.DefaultLogger(egv1a1.LogLevelInfo) ctx := context.Background() @@ -490,60 +346,23 @@ func TestProcessHTTPRoutes(t *testing.T) { for _, route := range tc.routes { objs = append(objs, route) } - for _, filter := range tc.authenFilters { - objs = append(objs, filter) - } - for _, filter := range tc.rateLimitFilters { - objs = append(objs, filter) - } - for _, filter := range tc.extensionFilters { - objs = append(objs, filter) - } if len(tc.extensionAPIGroups) > 0 { r.extGVKs = append(r.extGVKs, tc.extensionAPIGroups...) } r.client = fakeclient.NewClientBuilder(). WithScheme(envoygateway.GetScheme()). WithObjects(objs...). - WithIndex(&gwapiv1b1.HTTPRoute{}, gatewayHTTPRouteIndex, gatewayHTTPRouteIndexFunc). + WithIndex(&gwapiv1a2.GRPCRoute{}, gatewayGRPCRouteIndex, gatewayGRPCRouteIndexFunc). Build() // Process the test case httproutes. resourceTree := gatewayapi.NewResources() resourceMap := newResourceMapping() - err := r.processHTTPRoutes(ctx, gwNsName, resourceMap, resourceTree) + err := r.processGRPCRoutes(ctx, gwNsName, resourceMap, resourceTree) if tc.expected { require.NoError(t, err) // Ensure the resource tree and map are as expected. - require.Equal(t, tc.routes, resourceTree.HTTPRoutes) - // NOTE: filters must be in the same namespace as the HTTPRoute - if tc.authenFilters != nil { - for i, filter := range tc.authenFilters { - key := types.NamespacedName{ - Namespace: tc.routes[i].Namespace, - Name: filter.Name, - } - require.Equal(t, filter, resourceMap.authenFilters[key]) - } - } - if tc.rateLimitFilters != nil { - for i, filter := range tc.rateLimitFilters { - key := types.NamespacedName{ - Namespace: tc.routes[i].Namespace, - Name: filter.Name, - } - require.Equal(t, filter, resourceMap.rateLimitFilters[key]) - } - } - if tc.extensionFilters != nil { - for i, filter := range tc.extensionFilters { - key := types.NamespacedName{ - Namespace: tc.routes[i].Namespace, - Name: filter.GetName(), - } - require.Equal(t, *filter, resourceMap.extensionRefFilters[key]) - } - } + require.Equal(t, tc.routes, resourceTree.GRPCRoutes) } else { require.Error(t, err) } @@ -554,24 +373,24 @@ func TestProcessHTTPRoutes(t *testing.T) { func TestValidateHTTPRouteParentRefs(t *testing.T) { testCases := []struct { name string - route *gwapiv1b1.HTTPRoute - gateways []*gwapiv1b1.Gateway - classes []*gwapiv1b1.GatewayClass - expect []gwapiv1b1.Gateway + route *gwapiv1.HTTPRoute + gateways []*gwapiv1.Gateway + classes []*gwapiv1.GatewayClass + expect []gwapiv1.Gateway expected bool }{ { name: "valid parentRef", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "test", }, @@ -579,39 +398,39 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, }, }, - gateways: []*gwapiv1b1.Gateway{ + gateways: []*gwapiv1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "gc1", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController(cfgv1a1.GatewayControllerName), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController(egv1a1.GatewayControllerName), }, }, }, - expect: []gwapiv1b1.Gateway{ + expect: []gwapiv1.Gateway{ { TypeMeta: metav1.TypeMeta{ Kind: "Gateway", - APIVersion: gwapiv1b1.GroupVersion.String(), + APIVersion: gwapiv1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", ResourceVersion: "999", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, @@ -620,14 +439,14 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, { name: "invalid parentRef group", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { Group: gatewayapi.GroupPtr("unsupported.group"), Kind: gatewayapi.KindPtr("Gateway"), @@ -641,16 +460,16 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, { name: "invalid parentRef kind", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("UnsupportedKind"), Name: "test", }, @@ -662,16 +481,16 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, { name: "non-existent parentRef name", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "no-existent", }, @@ -683,21 +502,21 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, { name: "valid parentRefs", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "test", }, { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "test2", }, @@ -705,13 +524,13 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, }, }, - gateways: []*gwapiv1b1.Gateway{ + gateways: []*gwapiv1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, @@ -720,47 +539,47 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { Namespace: "test", Name: "test2", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "gc1", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController(cfgv1a1.GatewayControllerName), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController(egv1a1.GatewayControllerName), }, }, }, - expect: []gwapiv1b1.Gateway{ + expect: []gwapiv1.Gateway{ { TypeMeta: metav1.TypeMeta{ Kind: "Gateway", - APIVersion: gwapiv1b1.GroupVersion.String(), + APIVersion: gwapiv1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", ResourceVersion: "999", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, { TypeMeta: metav1.TypeMeta{ Kind: "Gateway", - APIVersion: gwapiv1b1.GroupVersion.String(), + APIVersion: gwapiv1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test2", ResourceVersion: "999", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, @@ -769,21 +588,21 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, { name: "one of two parentRefs are managed", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "test", }, { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "test2", }, @@ -791,13 +610,13 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, }, }, - gateways: []*gwapiv1b1.Gateway{ + gateways: []*gwapiv1.Gateway{ { ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, @@ -806,41 +625,41 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { Namespace: "test", Name: "test2", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc2", }, }, }, - classes: []*gwapiv1b1.GatewayClass{ + classes: []*gwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "gc1", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController(cfgv1a1.GatewayControllerName), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController(egv1a1.GatewayControllerName), }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "gc2", }, - Spec: gwapiv1b1.GatewayClassSpec{ - ControllerName: gwapiv1b1.GatewayController("unmanaged.controller"), + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: gwapiv1.GatewayController("unmanaged.controller"), }, }, }, - expect: []gwapiv1b1.Gateway{ + expect: []gwapiv1.Gateway{ { TypeMeta: metav1.TypeMeta{ Kind: "Gateway", - APIVersion: gwapiv1b1.GroupVersion.String(), + APIVersion: gwapiv1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", ResourceVersion: "999", }, - Spec: gwapiv1b1.GatewaySpec{ + Spec: gwapiv1.GatewaySpec{ GatewayClassName: "gc1", }, }, @@ -849,21 +668,21 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { }, { name: "one of two valid parentRefs kind", - route: &gwapiv1b1.HTTPRoute{ + route: &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Gateway"), Name: "test", }, { - Group: gatewayapi.GroupPtr(gwapiv1b1.GroupName), + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), Kind: gatewayapi.KindPtr("Unsupported"), Name: "test2", }, @@ -876,7 +695,7 @@ func TestValidateHTTPRouteParentRefs(t *testing.T) { } // Create the reconciler. - r := &gatewayAPIReconciler{classController: gwapiv1b1.GatewayController(cfgv1a1.GatewayControllerName)} + r := &gatewayAPIReconciler{classController: gwapiv1.GatewayController(egv1a1.GatewayControllerName)} ctx := context.Background() for _, tc := range testCases { diff --git a/internal/provider/kubernetes/secrets.go b/internal/provider/kubernetes/secrets.go index 1991ed735f7..e14f67b7ccb 100644 --- a/internal/provider/kubernetes/secrets.go +++ b/internal/provider/kubernetes/secrets.go @@ -96,6 +96,8 @@ func CreateOrUpdateSecrets(ctx context.Context, client client.Client, secrets [] if err := client.Create(ctx, &secret); err != nil { return nil, fmt.Errorf("failed to create secret %s/%s: %w", secret.Namespace, secret.Name, err) } + } else { + return nil, fmt.Errorf("failed to get secret %s/%s: %w", secret.Namespace, secret.Name, err) } // Update if current value is different and update arg is set. } else { diff --git a/internal/provider/kubernetes/test/utils.go b/internal/provider/kubernetes/test/utils.go index af83b9e3c3b..bb1fe05b688 100644 --- a/internal/provider/kubernetes/test/utils.go +++ b/internal/provider/kubernetes/test/utils.go @@ -11,12 +11,11 @@ import ( discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" - "github.com/envoyproxy/gateway/internal/gatewayapi" ) type ObjectKindNamespacedName struct { @@ -26,8 +25,8 @@ type ObjectKindNamespacedName struct { } // NewEnvoyProxy returns an EnvoyProxy object with the provided ns/name. -func NewEnvoyProxy(ns, name string) *egcfgv1a1.EnvoyProxy { - return &egcfgv1a1.EnvoyProxy{ +func NewEnvoyProxy(ns, name string) *egv1a1.EnvoyProxy { + return &egv1a1.EnvoyProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: ns, Name: name, @@ -36,31 +35,31 @@ func NewEnvoyProxy(ns, name string) *egcfgv1a1.EnvoyProxy { } // GetGatewayClass returns a sample GatewayClass. -func GetGatewayClass(name string, controller gwapiv1b1.GatewayController) *gwapiv1b1.GatewayClass { - return &gwapiv1b1.GatewayClass{ +func GetGatewayClass(name string, controller gwapiv1.GatewayController) *gwapiv1.GatewayClass { + return &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Spec: gwapiv1b1.GatewayClassSpec{ + Spec: gwapiv1.GatewayClassSpec{ ControllerName: controller, }, } } // GetGateway returns a sample Gateway with single listener. -func GetGateway(nsname types.NamespacedName, gwclass string) *gwapiv1b1.Gateway { - return &gwapiv1b1.Gateway{ +func GetGateway(nsname types.NamespacedName, gwclass string) *gwapiv1.Gateway { + return &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsname.Namespace, Name: nsname.Name, }, - Spec: gwapiv1b1.GatewaySpec{ - GatewayClassName: gwapiv1b1.ObjectName(gwclass), - Listeners: []gwapiv1b1.Listener{ + Spec: gwapiv1.GatewaySpec{ + GatewayClassName: gwapiv1.ObjectName(gwclass), + Listeners: []gwapiv1.Listener{ { Name: "test", - Port: gwapiv1b1.PortNumber(int32(8080)), - Protocol: gwapiv1b1.HTTPProtocolType, + Port: gwapiv1.PortNumber(int32(8080)), + Protocol: gwapiv1.HTTPProtocolType, }, }, }, @@ -68,14 +67,14 @@ func GetGateway(nsname types.NamespacedName, gwclass string) *gwapiv1b1.Gateway } // GetSecureGateway returns a sample Gateway with single TLS listener. -func GetSecureGateway(nsname types.NamespacedName, gwclass string, secretKindNSName ObjectKindNamespacedName) *gwapiv1b1.Gateway { +func GetSecureGateway(nsname types.NamespacedName, gwclass string, secretKindNSName ObjectKindNamespacedName) *gwapiv1.Gateway { secureGateway := GetGateway(nsname, gwclass) - secureGateway.Spec.Listeners[0].TLS = &gwapiv1b1.GatewayTLSConfig{ - Mode: gatewayapi.TLSModeTypePtr(gwapiv1b1.TLSModeTerminate), - CertificateRefs: []gwapiv1b1.SecretObjectReference{{ - Kind: (*gwapiv1b1.Kind)(&secretKindNSName.Kind), - Namespace: (*gwapiv1b1.Namespace)(&secretKindNSName.Namespace), - Name: gwapiv1b1.ObjectName(secretKindNSName.Name), + secureGateway.Spec.Listeners[0].TLS = &gwapiv1.GatewayTLSConfig{ + Mode: ptr.To(gwapiv1.TLSModeTerminate), + CertificateRefs: []gwapiv1.SecretObjectReference{{ + Kind: (*gwapiv1.Kind)(&secretKindNSName.Kind), + Namespace: (*gwapiv1.Namespace)(&secretKindNSName.Namespace), + Name: gwapiv1.ObjectName(secretKindNSName.Name), }}, } @@ -93,25 +92,26 @@ func GetSecret(nsname types.NamespacedName) *corev1.Secret { } // GetHTTPRoute returns a sample HTTPRoute with a parent reference. -func GetHTTPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName) *gwapiv1b1.HTTPRoute { - return &gwapiv1b1.HTTPRoute{ +func GetHTTPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName, port int32) *gwapiv1.HTTPRoute { + return &gwapiv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsName.Namespace, Name: nsName.Name, }, - Spec: gwapiv1b1.HTTPRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ - {Name: gwapiv1b1.ObjectName(parent)}, + Spec: gwapiv1.HTTPRouteSpec{ + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ + {Name: gwapiv1.ObjectName(parent)}, }, }, - Rules: []gwapiv1b1.HTTPRouteRule{ + Rules: []gwapiv1.HTTPRouteRule{ { - BackendRefs: []gwapiv1b1.HTTPBackendRef{ + BackendRefs: []gwapiv1.HTTPBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Name: gwapiv1b1.ObjectName(serviceName.Name), + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: gwapiv1.ObjectName(serviceName.Name), + Port: ptr.To(gwapiv1.PortNumber(port)), }, }, }, @@ -123,25 +123,26 @@ func GetHTTPRoute(nsName types.NamespacedName, parent string, serviceName types. } // GetGRPCRoute returns a sample GRPCRoute with a parent reference. -func GetGRPCRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName) *gwapiv1a2.GRPCRoute { +func GetGRPCRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName, port int32) *gwapiv1a2.GRPCRoute { return &gwapiv1a2.GRPCRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsName.Namespace, Name: nsName.Name, }, Spec: gwapiv1a2.GRPCRouteSpec{ - CommonRouteSpec: gwapiv1b1.CommonRouteSpec{ - ParentRefs: []gwapiv1b1.ParentReference{ - {Name: gwapiv1b1.ObjectName(parent)}, + CommonRouteSpec: gwapiv1.CommonRouteSpec{ + ParentRefs: []gwapiv1.ParentReference{ + {Name: gwapiv1.ObjectName(parent)}, }, }, Rules: []gwapiv1a2.GRPCRouteRule{ { BackendRefs: []gwapiv1a2.GRPCBackendRef{ { - BackendRef: gwapiv1b1.BackendRef{ - BackendObjectReference: gwapiv1b1.BackendObjectReference{ - Name: gwapiv1b1.ObjectName(serviceName.Name), + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: gwapiv1.ObjectName(serviceName.Name), + Port: ptr.To(gwapiv1.PortNumber(port)), }, }, }, @@ -153,7 +154,7 @@ func GetGRPCRoute(nsName types.NamespacedName, parent string, serviceName types. } // GetTLSRoute returns a sample TLSRoute with a parent reference. -func GetTLSRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName) *gwapiv1a2.TLSRoute { +func GetTLSRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName, port int32) *gwapiv1a2.TLSRoute { return &gwapiv1a2.TLSRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsName.Namespace, @@ -171,6 +172,7 @@ func GetTLSRoute(nsName types.NamespacedName, parent string, serviceName types.N { BackendObjectReference: gwapiv1a2.BackendObjectReference{ Name: gwapiv1a2.ObjectName(serviceName.Name), + Port: ptr.To(gwapiv1.PortNumber(port)), }, }, }, @@ -181,7 +183,7 @@ func GetTLSRoute(nsName types.NamespacedName, parent string, serviceName types.N } // GetTCPRoute returns a sample TCPRoute with a parent reference. -func GetTCPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName) *gwapiv1a2.TCPRoute { +func GetTCPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName, port int32) *gwapiv1a2.TCPRoute { return &gwapiv1a2.TCPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsName.Namespace, @@ -199,6 +201,7 @@ func GetTCPRoute(nsName types.NamespacedName, parent string, serviceName types.N { BackendObjectReference: gwapiv1a2.BackendObjectReference{ Name: gwapiv1a2.ObjectName(serviceName.Name), + Port: ptr.To(gwapiv1a2.PortNumber(port)), }, }, }, @@ -209,7 +212,7 @@ func GetTCPRoute(nsName types.NamespacedName, parent string, serviceName types.N } // GetUDPRoute returns a sample UDPRoute with a parent reference. -func GetUDPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName) *gwapiv1a2.UDPRoute { +func GetUDPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName, port int32) *gwapiv1a2.UDPRoute { return &gwapiv1a2.UDPRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsName.Namespace, @@ -227,6 +230,7 @@ func GetUDPRoute(nsName types.NamespacedName, parent string, serviceName types.N { BackendObjectReference: gwapiv1a2.BackendObjectReference{ Name: gwapiv1a2.ObjectName(serviceName.Name), + Port: ptr.To(gwapiv1a2.PortNumber(port)), }, }, }, @@ -310,97 +314,3 @@ func GetEndpointSlice(nsName types.NamespacedName, svcName string) *discoveryv1. }, } } - -// GetAuthenticationFilter returns a pointer to an AuthenticationFilter with the -// provided ns/name. The AuthenticationFilter uses a JWT provider with dummy issuer, -// audiences, and remoteJWKS settings. -func GetAuthenticationFilter(name, ns string) *egv1a1.AuthenticationFilter { - provider := GetAuthenticationProvider("test") - return &egv1a1.AuthenticationFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindAuthenticationFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - Spec: egv1a1.AuthenticationFilterSpec{ - Type: egv1a1.JwtAuthenticationFilterProviderType, - JwtProviders: []egv1a1.JwtAuthenticationFilterProvider{provider}, - }, - } -} - -// GetAuthenticationProvider returns a JwtAuthenticationFilterProvider using the provided name. -func GetAuthenticationProvider(name string) egv1a1.JwtAuthenticationFilterProvider { - return egv1a1.JwtAuthenticationFilterProvider{ - Name: name, - Issuer: "https://www.test.local", - Audiences: []string{"test.local"}, - RemoteJWKS: egv1a1.RemoteJWKS{ - URI: "https://test.local/jwt/public-key/jwks.json", - }, - } -} - -// GetRateLimitFilter returns a pointer to an RateLimitFilter with dummy rules. -func GetRateLimitFilter(name, ns string) *egv1a1.RateLimitFilter { - rule := GetRateLimitGlobalRule("one") - return &egv1a1.RateLimitFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: egv1a1.KindRateLimitFilter, - APIVersion: egv1a1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - Spec: egv1a1.RateLimitFilterSpec{ - Type: egv1a1.GlobalRateLimitType, - Global: &egv1a1.GlobalRateLimit{ - Rules: []egv1a1.RateLimitRule{rule}, - }, - }, - } -} - -// GetRateLimitGlobalRule returns a RateLimitRule using the val as the ClientSelectors -// headers value. -func GetRateLimitGlobalRule(val string) egv1a1.RateLimitRule { - return egv1a1.RateLimitRule{ - ClientSelectors: []egv1a1.RateLimitSelectCondition{ - { - Headers: []egv1a1.HeaderMatch{ - { - Name: "x-user-id", - Value: gatewayapi.StringPtr(val), - }, - }, - }, - }, - Limit: egv1a1.RateLimitValue{ - Requests: 5, - Unit: "Second", - }, - } -} - -func ContainsAuthenFilter(hroute *gwapiv1b1.HTTPRoute) bool { - if hroute == nil { - return false - } - - for _, rule := range hroute.Spec.Rules { - for _, filter := range rule.Filters { - if filter.Type == gwapiv1b1.HTTPRouteFilterExtensionRef && - filter.ExtensionRef != nil && - string(filter.ExtensionRef.Group) == egv1a1.GroupVersion.Group && - filter.ExtensionRef.Kind == egv1a1.KindAuthenticationFilter { - return true - } - } - } - - return false -} diff --git a/internal/provider/kubernetes/testdata/crds/multicluster-svc.yaml b/internal/provider/kubernetes/testdata/crds/multicluster-svc.yaml new file mode 100644 index 00000000000..b006849f234 --- /dev/null +++ b/internal/provider/kubernetes/testdata/crds/multicluster-svc.yaml @@ -0,0 +1,151 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serviceimports.multicluster.x-k8s.io +spec: + conversion: + strategy: None + group: multicluster.x-k8s.io + names: + kind: ServiceImport + listKind: ServiceImportList + plural: serviceimports + shortNames: + - svcim + singular: serviceimport + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The type of this ServiceImport + jsonPath: .spec.type + name: Type + type: string + - description: The VIP for this ServiceImport + jsonPath: .spec.ips + name: IP + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceImport describes a service imported from clusters in a + ClusterSet. + 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 ServiceImport. + properties: + ips: + description: ip will be used as the VIP for this service when type + is ClusterSetIP. + items: + type: string + maxItems: 1 + type: array + ports: + items: + description: ServicePort represents the port on which the service + is exposed + properties: + appProtocol: + description: The application protocol for this port. This field + follows standard Kubernetes label syntax. Un-prefixed names + are reserved for IANA standard service names (as per RFC-6335 + and http://www.iana.org/assignments/service-names). Non-standard + protocols should use prefixed names such as mycompany.com/my-custom-protocol. + Field can be enabled with ServiceAppProtocol feature gate. + 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 + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + required: + - port + type: object + type: array + x-kubernetes-list-type: atomic + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain session + affinity. Enable client IP based session affinity. Must be ClientIP + or None. Defaults to None. Ignored when type is Headless More info: + https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains session affinity configuration. + properties: + clientIP: + description: clientIP contains the configurations of Client IP + based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of ClientIP + type session sticky time. The value must be >0 && <=86400(for + 1 day) if ServiceAffinity == "ClientIP". Default value is + 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: type defines the type of this service. Must be ClusterSetIP + or Headless. + enum: + - ClusterSetIP + - Headless + type: string + required: + - ports + - type + type: object + status: + description: status contains information about the exported services that + form the multi-cluster service referenced by this ServiceImport. + properties: + clusters: + description: clusters is the list of exporting clusters from which + this service was derived. + items: + description: ClusterStatus contains service configuration mapped + to a specific source cluster + properties: + cluster: + description: cluster is the name of the exporting cluster. Must + be a valid RFC-1123 DNS label. + type: string + required: + - cluster + type: object + type: array + x-kubernetes-list-map-keys: + - cluster + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/internal/provider/runner/runner.go b/internal/provider/runner/runner.go index 5a6880337bd..7e298948321 100644 --- a/internal/provider/runner/runner.go +++ b/internal/provider/runner/runner.go @@ -11,7 +11,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/message" "github.com/envoyproxy/gateway/internal/provider/kubernetes" @@ -19,8 +19,7 @@ import ( type Config struct { config.Server - ProviderResources *message.ProviderResources - EnvoyPatchPolicyStatuses *message.EnvoyPatchPolicyStatuses + ProviderResources *message.ProviderResources } type Runner struct { @@ -36,7 +35,7 @@ func (r *Runner) Name() string { } // Start the provider runner -func (r *Runner) Start(ctx context.Context) error { +func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) if r.EnvoyGateway.Provider.Type == v1alpha1.ProviderTypeKubernetes { r.Logger.Info("Using provider", "type", v1alpha1.ProviderTypeKubernetes) @@ -44,7 +43,7 @@ func (r *Runner) Start(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to get kubeconfig: %w", err) } - p, err := kubernetes.New(cfg, &r.Config.Server, r.ProviderResources, r.EnvoyPatchPolicyStatuses) + p, err := kubernetes.New(cfg, &r.Config.Server, r.ProviderResources) if err != nil { return fmt.Errorf("failed to create provider %s: %w", v1alpha1.ProviderTypeKubernetes, err) } diff --git a/internal/provider/runner/runner_test.go b/internal/provider/runner/runner_test.go index b955b0862c1..d7fcd1092af 100644 --- a/internal/provider/runner/runner_test.go +++ b/internal/provider/runner/runner_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/logging" "github.com/envoyproxy/gateway/internal/message" diff --git a/internal/provider/utils/utils.go b/internal/provider/utils/utils.go index 2796434cfbb..48f3f8703f6 100644 --- a/internal/provider/utils/utils.go +++ b/internal/provider/utils/utils.go @@ -22,18 +22,22 @@ func NamespacedName(obj client.Object) types.NamespacedName { } } -// GetHashedName returns a partially hashed name for the string including up to 48 characters of the original name before the hash -func GetHashedName(nsName string) string { - - h := sha256.New() // Using sha256 instead of sha1 due to Blocklisted import crypto/sha1: weak cryptographic primitive (gosec) - hSum := h.Sum([]byte(nsName)) - hashedName := strings.ToLower(fmt.Sprintf("%x", hSum)) - +// GetHashedName returns a partially hashed name for the string including up to the given length of the original name characters before the hash. +// Input `nsName` should be formatted as `{Namespace}/{ResourceName}`. +func GetHashedName(nsName string, length int) string { + hashedName := HashString(nsName) // replace `/` with `-` to create a valid K8s resource name resourceName := strings.ReplaceAll(nsName, "/", "-") - - if len(resourceName) > 48 { - return fmt.Sprintf("%s-%s", resourceName[0:48], hashedName[0:8]) + if length > 0 && len(resourceName) > length { + // resource name needs to be trimmed, as container port name must not contain consecutive hyphens + trimmedName := strings.TrimSuffix(resourceName[0:length], "-") + return fmt.Sprintf("%s-%s", trimmedName, hashedName[0:8]) } return fmt.Sprintf("%s-%s", resourceName, hashedName[0:8]) } + +func HashString(str string) string { + h := sha256.New() // Using sha256 instead of sha1 due to Blocklisted import crypto/sha1: weak cryptographic primitive (gosec) + h.Write([]byte(str)) + return strings.ToLower(fmt.Sprintf("%x", h.Sum(nil))) +} diff --git a/internal/provider/utils/utils_test.go b/internal/provider/utils/utils_test.go new file mode 100644 index 00000000000..5955f28aecd --- /dev/null +++ b/internal/provider/utils/utils_test.go @@ -0,0 +1,33 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetHashedName(t *testing.T) { + testCases := []struct { + name string + nsName string + length int + expected string + }{ + {"test default name", "http", 6, "http-e0603c49"}, + {"test removing trailing slash", "namespace/name", 10, "namespace-18a6500f"}, + {"test removing trailing hyphen", "envoy-gateway-system/eg/http", 6, "envoy-2ecf157b"}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + result := GetHashedName(tc.nsName, tc.length) + require.Equal(t, tc.expected, result, "Result does not match expected string") + }) + } +} diff --git a/internal/status/backendtrafficpolicy.go b/internal/status/backendtrafficpolicy.go new file mode 100644 index 00000000000..0cfc9347355 --- /dev/null +++ b/internal/status/backendtrafficpolicy.go @@ -0,0 +1,32 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package status + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func SetBackendTrafficPolicyCondition(c *egv1a1.BackendTrafficPolicy, conditionType gwv1a2.PolicyConditionType, status metav1.ConditionStatus, reason gwv1a2.PolicyConditionReason, message string) { + cond := newCondition(string(conditionType), status, string(reason), message, time.Now(), c.Generation) + c.Status.Conditions = MergeConditions(c.Status.Conditions, cond) +} + +func SetBackendTrafficPolicyAcceptedIfUnset(s *egv1a1.BackendTrafficPolicyStatus, message string) { + // Return early if Accepted condition is already set + for _, c := range s.Conditions { + if c.Type == string(gwv1a2.PolicyConditionAccepted) { + return + } + } + + cond := newCondition(string(gwv1a2.PolicyConditionAccepted), metav1.ConditionTrue, string(gwv1a2.PolicyReasonAccepted), message, time.Now(), 0) + s.Conditions = MergeConditions(s.Conditions, cond) +} diff --git a/internal/status/clienttrafficpolicy.go b/internal/status/clienttrafficpolicy.go new file mode 100644 index 00000000000..6c37e487022 --- /dev/null +++ b/internal/status/clienttrafficpolicy.go @@ -0,0 +1,20 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package status + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func SetClientTrafficPolicyCondition(c *egv1a1.ClientTrafficPolicy, conditionType gwv1a2.PolicyConditionType, status metav1.ConditionStatus, reason gwv1a2.PolicyConditionReason, message string) { + cond := newCondition(string(conditionType), status, string(reason), message, time.Now(), c.Generation) + c.Status.Conditions = MergeConditions(c.Status.Conditions, cond) +} diff --git a/internal/status/conditions.go b/internal/status/conditions.go index ecb341f2739..eb6419d1541 100644 --- a/internal/status/conditions.go +++ b/internal/status/conditions.go @@ -16,14 +16,15 @@ package status import ( "fmt" "time" + "unicode" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) const ( - ReasonOlderGatewayClassExists gwapiv1b1.GatewayClassConditionReason = "OlderGatewayClassExists" + ReasonOlderGatewayClassExists gwapiv1.GatewayClassConditionReason = "OlderGatewayClassExists" MsgOlderGatewayClassExists = "Invalid GatewayClass: another older GatewayClass with the same Spec.Controller exists" MsgValidGatewayClass = "Valid GatewayClass" @@ -31,13 +32,13 @@ const ( ) // computeGatewayClassAcceptedCondition computes the GatewayClass Accepted status condition. -func computeGatewayClassAcceptedCondition(gatewayClass *gwapiv1b1.GatewayClass, +func computeGatewayClassAcceptedCondition(gatewayClass *gwapiv1.GatewayClass, accepted bool, reason, msg string) metav1.Condition { switch accepted { case true: return metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, Reason: reason, Message: msg, @@ -46,7 +47,7 @@ func computeGatewayClassAcceptedCondition(gatewayClass *gwapiv1b1.GatewayClass, } default: return metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, Reason: reason, Message: msg, @@ -57,25 +58,25 @@ func computeGatewayClassAcceptedCondition(gatewayClass *gwapiv1b1.GatewayClass, } // computeGatewayAcceptedCondition computes the Gateway Accepted status condition. -func computeGatewayAcceptedCondition(gw *gwapiv1b1.Gateway, accepted bool) metav1.Condition { +func computeGatewayAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) metav1.Condition { switch accepted { case true: - return newCondition(string(gwapiv1b1.GatewayReasonAccepted), metav1.ConditionTrue, - string(gwapiv1b1.GatewayReasonAccepted), + return newCondition(string(gwapiv1.GatewayReasonAccepted), metav1.ConditionTrue, + string(gwapiv1.GatewayReasonAccepted), "The Gateway has been scheduled by Envoy Gateway", time.Now(), gw.Generation) default: - return newCondition(string(gwapiv1b1.GatewayReasonAccepted), metav1.ConditionFalse, - string(gwapiv1b1.GatewayReasonAccepted), + return newCondition(string(gwapiv1.GatewayReasonAccepted), metav1.ConditionFalse, + string(gwapiv1.GatewayReasonAccepted), "The Gateway has not been scheduled by Envoy Gateway", time.Now(), gw.Generation) } } // computeGatewayProgrammedCondition computes the Gateway Programmed status condition. // Programmed condition surfaces true when the Envoy Deployment status is ready. -func computeGatewayProgrammedCondition(gw *gwapiv1b1.Gateway, deployment *appsv1.Deployment) metav1.Condition { +func computeGatewayProgrammedCondition(gw *gwapiv1.Gateway, deployment *appsv1.Deployment) metav1.Condition { if len(gw.Status.Addresses) == 0 { - return newCondition(string(gwapiv1b1.GatewayConditionProgrammed), metav1.ConditionFalse, - string(gwapiv1b1.GatewayReasonAddressNotAssigned), + return newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, + string(gwapiv1.GatewayReasonAddressNotAssigned), "No addresses have been assigned to the Gateway", time.Now(), gw.Generation) } @@ -83,15 +84,15 @@ func computeGatewayProgrammedCondition(gw *gwapiv1b1.Gateway, deployment *appsv1 // mark the Gateway as ready yet. if deployment == nil || deployment.Status.AvailableReplicas == 0 { - return newCondition(string(gwapiv1b1.GatewayConditionProgrammed), metav1.ConditionFalse, - string(gwapiv1b1.GatewayReasonNoResources), + return newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, + string(gwapiv1.GatewayReasonNoResources), "Deployment replicas unavailable", time.Now(), gw.Generation) } message := fmt.Sprintf("Address assigned to the Gateway, %d/%d envoy Deployment replicas available", deployment.Status.AvailableReplicas, deployment.Status.Replicas) - return newCondition(string(gwapiv1b1.GatewayConditionProgrammed), metav1.ConditionTrue, - string(gwapiv1b1.GatewayConditionProgrammed), message, time.Now(), gw.Generation) + return newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionTrue, + string(gwapiv1.GatewayConditionProgrammed), message, time.Now(), gw.Generation) } // MergeConditions adds or updates matching conditions, and updates the transition @@ -138,3 +139,33 @@ func conditionChanged(a, b metav1.Condition) bool { (a.Message != b.Message) || (a.ObservedGeneration != b.ObservedGeneration) } + +// Error2ConditionMsg format the error string to a Status condition message. +// * Convert the first letter to capital +// * Append "." to the string if it doesn't exit +func Error2ConditionMsg(err error) string { + if err == nil { + return "" + } + + message := err.Error() + if message == "" { + return message + } + + // Convert the string to a rune slice for easier manipulation + runes := []rune(message) + + // Check if the first rune is a letter and convert it to uppercase + if unicode.IsLetter(runes[0]) { + runes[0] = unicode.ToUpper(runes[0]) + } + + // check if the last rune is . + if runes[len(runes)-1] != '.' { + return string(runes) + "." + } + + // Convert the rune slice back to a string + return string(runes) +} diff --git a/internal/status/conditions_test.go b/internal/status/conditions_test.go index 1dc4b3c5e0a..88620b7aa0c 100644 --- a/internal/status/conditions_test.go +++ b/internal/status/conditions_test.go @@ -14,6 +14,7 @@ package status import ( + "errors" "testing" "time" @@ -22,9 +23,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilclock "k8s.io/utils/clock" fakeclock "k8s.io/utils/clock/testing" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/envoyproxy/gateway/internal/utils/ptr" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) var clock utilclock.Clock = utilclock.RealClock{} @@ -39,9 +39,9 @@ func TestComputeGatewayClassAcceptedCondition(t *testing.T) { name: "accepted gatewayclass", accepted: true, expect: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, - Reason: string(gwapiv1b1.GatewayClassReasonAccepted), + Reason: string(gwapiv1.GatewayClassReasonAccepted), Message: MsgValidGatewayClass, }, }, @@ -49,7 +49,7 @@ func TestComputeGatewayClassAcceptedCondition(t *testing.T) { name: "not accepted gatewayclass", accepted: false, expect: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, Reason: string(ReasonOlderGatewayClassExists), Message: MsgOlderGatewayClassExists, @@ -59,16 +59,16 @@ func TestComputeGatewayClassAcceptedCondition(t *testing.T) { name: "invalid parameters gatewayclass", accepted: false, expect: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, - Reason: string(gwapiv1b1.GatewayClassReasonInvalidParameters), + Reason: string(gwapiv1.GatewayClassReasonInvalidParameters), Message: MsgGatewayClassInvalidParams, }, }, } for _, tc := range testCases { - gc := &gwapiv1b1.GatewayClass{ + gc := &gwapiv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Generation: 7, }, @@ -93,7 +93,7 @@ func TestComputeGatewayScheduledCondition(t *testing.T) { name: "scheduled gateway", sched: true, expect: metav1.Condition{ - Type: string(gwapiv1b1.GatewayReasonAccepted), + Type: string(gwapiv1.GatewayReasonAccepted), Status: metav1.ConditionTrue, }, }, @@ -101,14 +101,14 @@ func TestComputeGatewayScheduledCondition(t *testing.T) { name: "not scheduled gateway", sched: false, expect: metav1.Condition{ - Type: string(gwapiv1b1.GatewayReasonAccepted), + Type: string(gwapiv1.GatewayReasonAccepted), Status: metav1.ConditionFalse, }, }, } for _, tc := range testCases { - gw := &gwapiv1b1.Gateway{ + gw := &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "test", @@ -143,12 +143,12 @@ func TestConditionChanged(t *testing.T) { name: "condition LastTransitionTime should be ignored", expected: false, a: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, LastTransitionTime: metav1.Unix(0, 0), }, b: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, LastTransitionTime: metav1.Unix(1, 0), }, @@ -157,12 +157,12 @@ func TestConditionChanged(t *testing.T) { name: "check condition reason differs", expected: true, a: metav1.Condition{ - Type: string(gwapiv1b1.GatewayConditionProgrammed), + Type: string(gwapiv1.GatewayConditionProgrammed), Status: metav1.ConditionFalse, Reason: "foo", }, b: metav1.Condition{ - Type: string(gwapiv1b1.GatewayConditionProgrammed), + Type: string(gwapiv1.GatewayConditionProgrammed), Status: metav1.ConditionFalse, Reason: "bar", }, @@ -171,11 +171,11 @@ func TestConditionChanged(t *testing.T) { name: "condition status differs", expected: true, a: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, }, b: metav1.Condition{ - Type: string(gwapiv1b1.GatewayClassConditionStatusAccepted), + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, }, }, @@ -293,7 +293,7 @@ func TestGatewayReadyCondition(t *testing.T) { deploymentStatus: appsv1.DeploymentStatus{AvailableReplicas: 1}, expect: metav1.Condition{ Status: metav1.ConditionTrue, - Reason: string(gwapiv1b1.GatewayConditionProgrammed), + Reason: string(gwapiv1.GatewayConditionProgrammed), }, }, { @@ -302,7 +302,7 @@ func TestGatewayReadyCondition(t *testing.T) { deploymentStatus: appsv1.DeploymentStatus{AvailableReplicas: 1}, expect: metav1.Condition{ Status: metav1.ConditionFalse, - Reason: string(gwapiv1b1.GatewayReasonAddressNotAssigned), + Reason: string(gwapiv1.GatewayReasonAddressNotAssigned), }, }, { @@ -311,7 +311,7 @@ func TestGatewayReadyCondition(t *testing.T) { deploymentStatus: appsv1.DeploymentStatus{AvailableReplicas: 0}, expect: metav1.Condition{ Status: metav1.ConditionFalse, - Reason: string(gwapiv1b1.GatewayReasonNoResources), + Reason: string(gwapiv1.GatewayReasonNoResources), }, }, } @@ -320,12 +320,12 @@ func TestGatewayReadyCondition(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - gtw := &gwapiv1b1.Gateway{} + gtw := &gwapiv1.Gateway{} if tc.serviceAddress { - gtw.Status = gwapiv1b1.GatewayStatus{ - Addresses: []gwapiv1b1.GatewayAddress{ + gtw.Status = gwapiv1.GatewayStatus{ + Addresses: []gwapiv1.GatewayStatusAddress{ { - Type: ptr.To(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1.IPAddressType), Value: "1.1.1.1", }, }, @@ -335,9 +335,33 @@ func TestGatewayReadyCondition(t *testing.T) { deployment := &appsv1.Deployment{Status: tc.deploymentStatus} got := computeGatewayProgrammedCondition(gtw, deployment) - assert.Equal(t, string(gwapiv1b1.GatewayConditionProgrammed), got.Type) + assert.Equal(t, string(gwapiv1.GatewayConditionProgrammed), got.Type) assert.Equal(t, tc.expect.Status, got.Status) assert.Equal(t, tc.expect.Reason, got.Reason) }) } } + +func TestError2ConditionMsg(t *testing.T) { + testCases := []struct { + name string + err error + expect string + }{ + { + name: "nil error", + err: nil, + expect: "", + }, + { + name: "error with message", + err: errors.New("something is wrong"), + expect: "Something is wrong.", + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.expect, Error2ConditionMsg(tt.err), "Error2ConditionMsg(%v)", tt.err) + }) + } +} diff --git a/internal/status/gateway.go b/internal/status/gateway.go index d7f5695f723..b7245c206aa 100644 --- a/internal/status/gateway.go +++ b/internal/status/gateway.go @@ -8,13 +8,12 @@ package status import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/envoyproxy/gateway/internal/utils/ptr" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) // UpdateGatewayStatusAcceptedCondition updates the status condition for the provided Gateway based on the accepted state. -func UpdateGatewayStatusAcceptedCondition(gw *gwapiv1b1.Gateway, accepted bool) *gwapiv1b1.Gateway { +func UpdateGatewayStatusAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) *gwapiv1.Gateway { gw.Status.Conditions = MergeConditions(gw.Status.Conditions, computeGatewayAcceptedCondition(gw, accepted)) return gw } @@ -22,7 +21,7 @@ func UpdateGatewayStatusAcceptedCondition(gw *gwapiv1b1.Gateway, accepted bool) // UpdateGatewayStatusProgrammedCondition updates the status addresses for the provided gateway // based on the status IP/Hostname of svc and updates the Programmed condition based on the // service and deployment state. -func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1b1.Gateway, svc *corev1.Service, deployment *appsv1.Deployment, nodeAddresses ...string) { +func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1.Gateway, svc *corev1.Service, deployment *appsv1.Deployment, nodeAddresses ...string) { var addresses, hostnames []string // Update the status addresses field. if svc != nil { @@ -64,18 +63,18 @@ func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1b1.Gateway, svc *corev1.S } } - var gwAddresses []gwapiv1b1.GatewayAddress + var gwAddresses []gwapiv1.GatewayStatusAddress for i := range addresses { - addr := gwapiv1b1.GatewayAddress{ - Type: ptr.To(gwapiv1b1.IPAddressType), + addr := gwapiv1.GatewayStatusAddress{ + Type: ptr.To(gwapiv1.IPAddressType), Value: addresses[i], } gwAddresses = append(gwAddresses, addr) } for i := range hostnames { - addr := gwapiv1b1.GatewayAddress{ - Type: ptr.To(gwapiv1b1.HostnameAddressType), + addr := gwapiv1.GatewayStatusAddress{ + Type: ptr.To(gwapiv1.HostnameAddressType), Value: hostnames[i], } gwAddresses = append(gwAddresses, addr) diff --git a/internal/status/gateway_test.go b/internal/status/gateway_test.go index 6b404cd88d9..c20d29b8ec0 100644 --- a/internal/status/gateway_test.go +++ b/internal/status/gateway_test.go @@ -13,17 +13,16 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/envoyproxy/gateway/internal/utils/ptr" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { type args struct { - gw *gwapiv1b1.Gateway + gw *gwapiv1.Gateway svc *corev1.Service deployment *appsv1.Deployment - addresses []gwapiv1b1.GatewayAddress + addresses []gwapiv1.GatewayStatusAddress } tests := []struct { name string @@ -32,7 +31,7 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { { name: "nil svc", args: args{ - gw: &gwapiv1b1.Gateway{}, + gw: &gwapiv1.Gateway{}, svc: nil, addresses: nil, }, @@ -40,7 +39,7 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { { name: "LoadBalancer svc with ingress ip", args: args{ - gw: &gwapiv1b1.Gateway{}, + gw: &gwapiv1.Gateway{}, svc: &corev1.Service{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, @@ -58,9 +57,9 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { }, }, }, - addresses: []gwapiv1b1.GatewayAddress{ + addresses: []gwapiv1.GatewayStatusAddress{ { - Type: ptr.To(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1.IPAddressType), Value: "127.0.0.1", }, }, @@ -69,7 +68,7 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { { name: "LoadBalancer svc with ingress hostname", args: args{ - gw: &gwapiv1b1.Gateway{}, + gw: &gwapiv1.Gateway{}, svc: &corev1.Service{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, @@ -87,13 +86,13 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { }, }, }, - addresses: []gwapiv1b1.GatewayAddress{ + addresses: []gwapiv1.GatewayStatusAddress{ { - Type: ptr.To(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1.IPAddressType), Value: "127.0.0.1", }, { - Type: ptr.To(gwapiv1b1.HostnameAddressType), + Type: ptr.To(gwapiv1.HostnameAddressType), Value: "localhost", }, }, @@ -102,7 +101,7 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { { name: "ClusterIP svc", args: args{ - gw: &gwapiv1b1.Gateway{}, + gw: &gwapiv1.Gateway{}, svc: &corev1.Service{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, @@ -111,9 +110,9 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { Type: corev1.ServiceTypeClusterIP, }, }, - addresses: []gwapiv1b1.GatewayAddress{ + addresses: []gwapiv1.GatewayStatusAddress{ { - Type: ptr.To(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1.IPAddressType), Value: "127.0.0.1", }, }, diff --git a/internal/status/gatewayclass.go b/internal/status/gatewayclass.go index ba7168d2a21..7a71ab23646 100644 --- a/internal/status/gatewayclass.go +++ b/internal/status/gatewayclass.go @@ -14,12 +14,12 @@ package status import ( - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) // SetGatewayClassAccepted inserts or updates the Accepted condition // for the provided GatewayClass. -func SetGatewayClassAccepted(gc *gwapiv1b1.GatewayClass, accepted bool, reason, msg string) *gwapiv1b1.GatewayClass { +func SetGatewayClassAccepted(gc *gwapiv1.GatewayClass, accepted bool, reason, msg string) *gwapiv1.GatewayClass { gc.Status.Conditions = MergeConditions(gc.Status.Conditions, computeGatewayClassAcceptedCondition(gc, accepted, reason, msg)) return gc } diff --git a/internal/status/securitypolicy.go b/internal/status/securitypolicy.go new file mode 100644 index 00000000000..cf9e478c6a1 --- /dev/null +++ b/internal/status/securitypolicy.go @@ -0,0 +1,25 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package status + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func SetSecurityPolicyCondition(c *egv1a1.SecurityPolicy, conditionType gwv1a2.PolicyConditionType, status metav1.ConditionStatus, reason gwv1a2.PolicyConditionReason, message string) { + cond := newCondition(string(conditionType), status, string(reason), message, time.Now(), c.Generation) + c.Status.Conditions = MergeConditions(c.Status.Conditions, cond) +} + +func SetSecurityPolicyAccepted(s *egv1a1.SecurityPolicyStatus, message string) { + cond := newCondition(string(gwv1a2.PolicyConditionAccepted), metav1.ConditionTrue, string(gwv1a2.PolicyReasonAccepted), message, time.Now(), 0) + s.Conditions = MergeConditions(s.Conditions, cond) +} diff --git a/internal/status/status.go b/internal/status/status.go index 1af10030adf..5ffa6ed4ad1 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -24,8 +24,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) @@ -168,23 +168,25 @@ func (u *UpdateWriter) Send(update Update) { // UDPRoute // GRPCRoute // EnvoyPatchPolicy +// ClientTrafficPolicy +// SecurityPolicy func isStatusEqual(objA, objB interface{}) bool { opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") switch a := objA.(type) { - case *gwapiv1b1.GatewayClass: - if b, ok := objB.(*gwapiv1b1.GatewayClass); ok { + case *gwapiv1.GatewayClass: + if b, ok := objB.(*gwapiv1.GatewayClass); ok { if cmp.Equal(a.Status, b.Status, opts) { return true } } - case *gwapiv1b1.Gateway: - if b, ok := objB.(*gwapiv1b1.Gateway); ok { + case *gwapiv1.Gateway: + if b, ok := objB.(*gwapiv1.Gateway); ok { if cmp.Equal(a.Status, b.Status, opts) { return true } } - case *gwapiv1b1.HTTPRoute: - if b, ok := objB.(*gwapiv1b1.HTTPRoute); ok { + case *gwapiv1.HTTPRoute: + if b, ok := objB.(*gwapiv1.HTTPRoute); ok { if cmp.Equal(a.Status, b.Status, opts) { return true } @@ -219,6 +221,25 @@ func isStatusEqual(objA, objB interface{}) bool { return true } } + case *egv1a1.ClientTrafficPolicy: + if b, ok := objB.(*egv1a1.ClientTrafficPolicy); ok { + if cmp.Equal(a.Status, b.Status, opts) { + return true + } + } + case *egv1a1.BackendTrafficPolicy: + if b, ok := objB.(*egv1a1.BackendTrafficPolicy); ok { + if cmp.Equal(a.Status, b.Status, opts) { + return true + } + } + case *egv1a1.SecurityPolicy: + if b, ok := objB.(*egv1a1.SecurityPolicy); ok { + if cmp.Equal(a.Status, b.Status, opts) { + return true + } + } } + return false } diff --git a/internal/utils/ptr/ptr.go b/internal/utils/ptr/ptr.go deleted file mode 100644 index 368c36c5b83..00000000000 --- a/internal/utils/ptr/ptr.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package ptr - -func To[T any](in T) *T { - return &in -} diff --git a/internal/utils/yaml/yaml.go b/internal/utils/yaml/yaml.go new file mode 100644 index 00000000000..42e87f97dd5 --- /dev/null +++ b/internal/utils/yaml/yaml.go @@ -0,0 +1,62 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package yaml + +import ( + "reflect" + + "sigs.k8s.io/yaml" +) + +// MergeYAML merges two yaml files. The second yaml file will override the first one if the same key exists. +// This method can add or override a value within a map, or add a new value to a list. +// Please note that this method can't override a value within a list. +func MergeYAML(base, override string) (string, error) { + // declare two map to hold the yaml content + map1 := map[string]interface{}{} + map2 := map[string]interface{}{} + + if err := yaml.Unmarshal([]byte(base), &map1); err != nil { + return "", err + } + + if err := yaml.Unmarshal([]byte(override), &map2); err != nil { + return "", err + } + + // merge both yaml data recursively + result := mergeMaps(map1, map2) + + out, err := yaml.Marshal(result) + if err != nil { + return "", err + } + return string(out), nil +} + +func mergeMaps(map1, map2 map[string]interface{}) map[string]interface{} { + out := make(map[string]interface{}, len(map1)) + for k, v := range map1 { + out[k] = v + } + for k, v := range map2 { + if v, ok := v.(map[string]interface{}); ok { + if bv, ok := out[k]; ok { + if bv, ok := bv.(map[string]interface{}); ok { + out[k] = mergeMaps(bv, v) + continue + } + } + } + value := reflect.ValueOf(v) + if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { + out[k] = append(out[k].([]interface{}), v.([]interface{})...) + } else { + out[k] = v + } + } + return out +} diff --git a/internal/utils/yaml/yaml_test.go b/internal/utils/yaml/yaml_test.go new file mode 100644 index 00000000000..1ba6c90faed --- /dev/null +++ b/internal/utils/yaml/yaml_test.go @@ -0,0 +1,67 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package yaml + +import ( + "reflect" + "testing" +) + +func TestMergeYAML(t *testing.T) { + tests := []struct { + name string + yaml1 string + yaml2 string + want string + }{ + { + name: "test1", + yaml1: ` +a: a +b: + c: + d: d +e: + f: + - g +k: + l: l +`, + yaml2: ` +a: a1 +b: + c: + d: d1 +e: + f: + - h +i: + j: j +`, + want: `a: a1 +b: + c: + d: d1 +e: + f: + - g + - h +i: + j: j +k: + l: l +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := MergeYAML(tt.yaml1, tt.yaml2) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MergeYAML() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/xds/bootstrap/bootstrap.go b/internal/xds/bootstrap/bootstrap.go index 9cb0a49f4d6..f8418e0e750 100644 --- a/internal/xds/bootstrap/bootstrap.go +++ b/internal/xds/bootstrap/bootstrap.go @@ -14,7 +14,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) const ( @@ -63,6 +63,11 @@ type bootstrapParameters struct { EnablePrometheus bool // OtelMetricSinks defines the configuration of the OpenTelemetry sinks. OtelMetricSinks []metricSink + // EnableStatConfig defines whether to to customize the Envoy proxy stats. + EnableStatConfig bool + // StatsMatcher is to control creation of custom Envoy stats with prefix, + // suffix, and regex expressions match on the name of the stats. + StatsMatcher *StatsMatcherParameters } type xdsServerParameters struct { @@ -97,6 +102,13 @@ type readyServerParameters struct { ReadinessPath string } +type StatsMatcherParameters struct { + Exacts []string + Prefixs []string + Suffixs []string + RegularExpressions []string +} + // render the stringified bootstrap config in yaml format. func (b *bootstrapConfig) render() error { buf := new(strings.Builder) @@ -109,15 +121,16 @@ func (b *bootstrapConfig) render() error { } // GetRenderedBootstrapConfig renders the bootstrap YAML string -func GetRenderedBootstrapConfig(proxyMetrics *egcfgv1a1.ProxyMetrics) (string, error) { +func GetRenderedBootstrapConfig(proxyMetrics *egv1a1.ProxyMetrics) (string, error) { var ( - enablePrometheus bool + enablePrometheus = true metricSinks []metricSink + StatsMatcher StatsMatcherParameters ) if proxyMetrics != nil { if proxyMetrics.Prometheus != nil { - enablePrometheus = true + enablePrometheus = !proxyMetrics.Prometheus.Disable } addresses := sets.NewString() @@ -138,6 +151,27 @@ func GetRenderedBootstrapConfig(proxyMetrics *egcfgv1a1.ProxyMetrics) (string, e Port: sink.OpenTelemetry.Port, }) } + + if proxyMetrics.Matches != nil { + // Add custom envoy proxy stats + for _, match := range proxyMetrics.Matches { + // matchType default to exact + matchType := egv1a1.StringMatchExact + if match.Type != nil { + matchType = *match.Type + } + switch matchType { + case egv1a1.StringMatchExact: + StatsMatcher.Exacts = append(StatsMatcher.Exacts, match.Value) + case egv1a1.StringMatchPrefix: + StatsMatcher.Prefixs = append(StatsMatcher.Prefixs, match.Value) + case egv1a1.StringMatchSuffix: + StatsMatcher.Suffixs = append(StatsMatcher.Suffixs, match.Value) + case egv1a1.StringMatchRegularExpression: + StatsMatcher.RegularExpressions = append(StatsMatcher.RegularExpressions, match.Value) + } + } + } } cfg := &bootstrapConfig{ @@ -160,6 +194,9 @@ func GetRenderedBootstrapConfig(proxyMetrics *egcfgv1a1.ProxyMetrics) (string, e OtelMetricSinks: metricSinks, }, } + if proxyMetrics != nil && proxyMetrics.Matches != nil { + cfg.parameters.StatsMatcher = &StatsMatcher + } if err := cfg.render(); err != nil { return "", err diff --git a/internal/xds/bootstrap/bootstrap.yaml.tpl b/internal/xds/bootstrap/bootstrap.yaml.tpl index d29f939dc42..ab6a3516510 100644 --- a/internal/xds/bootstrap/bootstrap.yaml.tpl +++ b/internal/xds/bootstrap/bootstrap.yaml.tpl @@ -8,6 +8,26 @@ admin: socket_address: address: {{ .AdminServer.Address }} port_value: {{ .AdminServer.Port }} +{{- if .StatsMatcher }} +stats_config: + stats_matcher: + inclusion_list: + patterns: + {{- range $_, $item := .StatsMatcher.Exacts }} + - exact: {{$item}} + {{- end}} + {{- range $_, $item := .StatsMatcher.Prefixs }} + - prefix: {{$item}} + {{- end}} + {{- range $_, $item := .StatsMatcher.Suffixs }} + - suffix: {{$item}} + {{- end}} + {{- range $_, $item := .StatsMatcher.RegularExpressions }} + - safe_regex: + google_re2: {} + regex: {{js $item}} + {{- end}} +{{- end }} dynamic_resources: ads_config: api_type: DELTA_GRPC @@ -112,8 +132,10 @@ static_resources: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: {{ .XdsServer.Address }} @@ -122,7 +144,10 @@ static_resources: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -144,11 +169,3 @@ static_resources: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 -layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 diff --git a/internal/xds/bootstrap/bootstrap_test.go b/internal/xds/bootstrap/bootstrap_test.go index 33a7a817887..6cb0ed31469 100644 --- a/internal/xds/bootstrap/bootstrap_test.go +++ b/internal/xds/bootstrap/bootstrap_test.go @@ -12,31 +12,40 @@ import ( "testing" "github.com/stretchr/testify/assert" + "k8s.io/utils/ptr" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) func TestGetRenderedBootstrapConfig(t *testing.T) { cases := []struct { name string - proxyMetrics *egcfgv1a1.ProxyMetrics + proxyMetrics *egv1a1.ProxyMetrics }{ { - name: "default", + name: "disable-prometheus", + proxyMetrics: &egv1a1.ProxyMetrics{ + Prometheus: &egv1a1.ProxyPrometheusProvider{ + Disable: true, + }, + }, }, { name: "enable-prometheus", - proxyMetrics: &egcfgv1a1.ProxyMetrics{ - Prometheus: &egcfgv1a1.PrometheusProvider{}, + proxyMetrics: &egv1a1.ProxyMetrics{ + Prometheus: &egv1a1.ProxyPrometheusProvider{}, }, }, { name: "otel-metrics", - proxyMetrics: &egcfgv1a1.ProxyMetrics{ - Sinks: []egcfgv1a1.MetricSink{ + proxyMetrics: &egv1a1.ProxyMetrics{ + Prometheus: &egv1a1.ProxyPrometheusProvider{ + Disable: true, + }, + Sinks: []egv1a1.ProxyMetricSink{ { - Type: egcfgv1a1.MetricSinkTypeOpenTelemetry, - OpenTelemetry: &egcfgv1a1.OpenTelemetrySink{ + Type: egv1a1.MetricSinkTypeOpenTelemetry, + OpenTelemetry: &egv1a1.ProxyOpenTelemetrySink{ Host: "otel-collector.monitoring.svc", Port: 4317, }, @@ -44,6 +53,33 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { }, }, }, + { + name: "custom-stats-matcher", + proxyMetrics: &egv1a1.ProxyMetrics{ + Matches: []egv1a1.StringMatch{ + { + Type: ptr.To(egv1a1.StringMatchExact), + Value: "http.foo.bar.cluster.upstream_rq", + }, + { + Type: ptr.To(egv1a1.StringMatchPrefix), + Value: "http", + }, + { + Type: ptr.To(egv1a1.StringMatchSuffix), + Value: "upstream_rq", + }, + { + Type: ptr.To(egv1a1.StringMatchRegularExpression), + Value: "virtual.*", + }, + { + Type: ptr.To(egv1a1.StringMatchPrefix), + Value: "cluster", + }, + }, + }, + }, } for _, tc := range cases { diff --git a/internal/xds/bootstrap/testdata/custom-stats-matcher.yaml b/internal/xds/bootstrap/testdata/custom-stats-matcher.yaml new file mode 100644 index 00000000000..c8376470146 --- /dev/null +++ b/internal/xds/bootstrap/testdata/custom-stats-matcher.yaml @@ -0,0 +1,127 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 +stats_config: + stats_matcher: + inclusion_list: + patterns: + - exact: http.foo.bar.cluster.upstream_rq + - prefix: http + - prefix: cluster + - suffix: upstream_rq + - safe_regex: + google_re2: {} + regex: virtual.* +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + virtual_hosts: + - name: prometheus_stats + domains: + - "*" + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: prometheus_stats + connect_timeout: 0.250s + type: STATIC + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: prometheus_stats + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 diff --git a/internal/xds/bootstrap/testdata/default.yaml b/internal/xds/bootstrap/testdata/default.yaml deleted file mode 100644 index 48c5b1cbdde..00000000000 --- a/internal/xds/bootstrap/testdata/default.yaml +++ /dev/null @@ -1,97 +0,0 @@ -admin: - access_log: - - name: envoy.access_loggers.file - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/null - address: - socket_address: - address: 127.0.0.1 - port_value: 19000 -dynamic_resources: - ads_config: - api_type: DELTA_GRPC - transport_api_version: V3 - grpc_services: - - envoy_grpc: - cluster_name: xds_cluster - set_node_on_first_message_only: true - lds_config: - ads: {} - resource_api_version: V3 - cds_config: - ads: {} - resource_api_version: V3 -static_resources: - listeners: - - name: envoy-gateway-proxy-ready-0.0.0.0-19001 - address: - socket_address: - address: 0.0.0.0 - port_value: 19001 - protocol: TCP - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: eg-ready-http - route_config: - name: local_route - http_filters: - - name: envoy.filters.http.health_check - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck - pass_through_mode: false - headers: - - name: ":path" - string_match: - exact: /ready - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - connect_timeout: 10s - load_assignment: - cluster_name: xds_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-gateway - port_value: 18000 - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" - explicit_http_config: - http2_protocol_options: {} - name: xds_cluster - type: STRICT_DNS - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - common_tls_context: - tls_params: - tls_maximum_protocol_version: TLSv1_3 - tls_certificate_sds_secret_configs: - - name: xds_certificate - sds_config: - path_config_source: - path: "/sds/xds-certificate.json" - resource_api_version: V3 - validation_context_sds_secret_config: - name: xds_trusted_ca - sds_config: - path_config_source: - path: "/sds/xds-trusted-ca.json" - resource_api_version: V3 -layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 diff --git a/internal/xds/bootstrap/testdata/disable-prometheus.yaml b/internal/xds/bootstrap/testdata/disable-prometheus.yaml new file mode 100644 index 00000000000..4cfe3d7d2b0 --- /dev/null +++ b/internal/xds/bootstrap/testdata/disable-prometheus.yaml @@ -0,0 +1,94 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 19000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 diff --git a/internal/xds/bootstrap/testdata/enable-prometheus.yaml b/internal/xds/bootstrap/testdata/enable-prometheus.yaml index 457f116c715..667729dadf1 100644 --- a/internal/xds/bootstrap/testdata/enable-prometheus.yaml +++ b/internal/xds/bootstrap/testdata/enable-prometheus.yaml @@ -77,8 +77,10 @@ static_resources: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -87,7 +89,10 @@ static_resources: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -109,11 +114,3 @@ static_resources: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 -layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 diff --git a/internal/xds/bootstrap/testdata/otel-metrics.yaml b/internal/xds/bootstrap/testdata/otel-metrics.yaml index 5a07f9030ea..c4e4a6b750b 100644 --- a/internal/xds/bootstrap/testdata/otel-metrics.yaml +++ b/internal/xds/bootstrap/testdata/otel-metrics.yaml @@ -80,8 +80,10 @@ static_resources: load_assignment: cluster_name: xds_cluster endpoints: - - lb_endpoints: - - endpoint: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: address: socket_address: address: envoy-gateway @@ -90,7 +92,10 @@ static_resources: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" explicit_http_config: - http2_protocol_options: {} + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s name: xds_cluster type: STRICT_DNS transport_socket: @@ -112,11 +117,3 @@ static_resources: path_config_source: path: "/sds/xds-trusted-ca.json" resource_api_version: V3 -layered_runtime: - layers: - - name: runtime-0 - rtds_layer: - rtds_config: - ads: {} - resource_api_version: V3 - name: runtime-0 diff --git a/internal/xds/bootstrap/util.go b/internal/xds/bootstrap/util.go new file mode 100644 index 00000000000..ca0ffbfa3e8 --- /dev/null +++ b/internal/xds/bootstrap/util.go @@ -0,0 +1,24 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package bootstrap + +import ( + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + yamlutils "github.com/envoyproxy/gateway/internal/utils/yaml" +) + +// ApplyBootstrapConfig applies the bootstrap config to the default bootstrap config and return the result config. +func ApplyBootstrapConfig(boostrapConfig *egv1a1.ProxyBootstrap, defaultBootstrap string) (string, error) { + bootstrapType := boostrapConfig.Type + if bootstrapType != nil && *bootstrapType == egv1a1.BootstrapTypeMerge { + mergedBootstrap, err := yamlutils.MergeYAML(defaultBootstrap, boostrapConfig.Value) + if err != nil { + return "", err + } + return mergedBootstrap, nil + } + return boostrapConfig.Value, nil +} diff --git a/internal/xds/extensions/extensions.gen.go b/internal/xds/extensions/extensions.gen.go index b3376cae310..ec91f89f958 100644 --- a/internal/xds/extensions/extensions.gen.go +++ b/internal/xds/extensions/extensions.gen.go @@ -9,6 +9,7 @@ package extensions import ( _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/config/v3alpha" + _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/checksum/v3alpha" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/dynamo/v3" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/golang/v3alpha" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/language/v3alpha" @@ -20,6 +21,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/generic_proxy/matcher/v3" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/generic_proxy/router/v3" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/generic_proxy/v3" + _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/golang/v3alpha" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/kafka_broker/v3" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/kafka_mesh/v3alpha" _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/mysql_proxy/v3" @@ -53,6 +55,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" _ "github.com/envoyproxy/go-control-plane/envoy/config/tap/v3" _ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/config/upstream/local_address_selector/v3" _ "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3" _ "github.com/envoyproxy/go-control-plane/envoy/data/cluster/v3" _ "github.com/envoyproxy/go-control-plane/envoy/data/core/v3" @@ -84,12 +87,14 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/dependency/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/matcher/action/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/set_filter_state/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/adaptive_concurrency/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/admission_control/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/alternate_protocols_cache/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_lambda/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_request_signing/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/bandwidth_limit/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/basic_auth/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/buffer/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cache/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cdn_loop/v3" @@ -97,6 +102,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/connect_grpc_bridge/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/credential_injector/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/custom_response/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/decompressor/v3" @@ -107,6 +113,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/file_system_buffer/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/geoip/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_field_extraction/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_bridge/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_json_transcoder/v3" @@ -117,6 +124,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_to_metadata/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ip_tagging/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/json_to_metadata/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/kill_request/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3" @@ -128,6 +136,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/set_filter_state/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/set_metadata/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/stateful_session/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/tap/v3" @@ -151,6 +160,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ratelimit/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/set_filter_state/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_cluster/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" @@ -162,10 +172,14 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/zookeeper_proxy/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/dns_filter/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/session/dynamic_forward_proxy/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/session/http_capsule/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/cel/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/metadata/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/req_without_query/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/geoip_providers/common/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/geoip_providers/maxmind/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/health_check/event_sinks/file/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/health_checkers/redis/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/health_checkers/thrift/v3" @@ -180,6 +194,8 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/original_ip_detection/xff/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/stateful_session/cookie/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/stateful_session/header/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/injected_credentials/generic/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/injected_credentials/oauth2/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/allow_listed_routes/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/previous_routes/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/safe_cross_scheme/v3" @@ -226,6 +242,8 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/graphite_statsd/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/open_telemetry/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/wasm/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/opentelemetry/resource_detectors/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/opentelemetry/samplers/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/alts/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/http_11_proxy/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/internal_upstream/v3" @@ -241,6 +259,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/generic/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/http/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/tcp/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/udp/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/tcp/generic/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/tcp/v3" diff --git a/internal/xds/server/runner/runner.go b/internal/xds/server/runner/runner.go index e76224a739c..bb18976932f 100644 --- a/internal/xds/server/runner/runner.go +++ b/internal/xds/server/runner/runner.go @@ -14,6 +14,9 @@ import ( "net" "os" "strconv" + "time" + + "google.golang.org/grpc/keepalive" clusterv3 "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3" discoveryv3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" @@ -26,7 +29,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/message" "github.com/envoyproxy/gateway/internal/xds/bootstrap" @@ -68,14 +71,17 @@ func (r *Runner) Name() string { } // Start starts the xds-server runner -func (r *Runner) Start(ctx context.Context) error { +func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) // Set up the gRPC server and register the xDS handler. // Create SnapshotCache before start subscribeAndTranslate, // prevent panics in case cache is nil. cfg := r.tlsConfig(xdsTLSCertFilename, xdsTLSKeyFilename, xdsTLSCaFilename) - r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(cfg))) + r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(cfg)), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + MinTime: 15 * time.Second, + PermitWithoutStream: true, + })) r.cache = cache.NewSnapshotCache(false, r.Logger) registerServer(serverv3.NewServer(ctx, r.cache, r.cache), r.grpc) @@ -86,7 +92,7 @@ func (r *Runner) Start(ctx context.Context) error { // Start message Subscription. go r.subscribeAndTranslate(ctx) r.Logger.Info("started") - return nil + return } func (r *Runner) serveXdsServer(ctx context.Context) { @@ -96,18 +102,20 @@ func (r *Runner) serveXdsServer(ctx context.Context) { r.Logger.Error(err, "failed to listen on address", "address", addr) return } - err = r.grpc.Serve(l) - if err != nil { + + go func() { + <-ctx.Done() + r.Logger.Info("grpc server shutting down") + // We don't use GracefulStop here because envoy + // has long-lived hanging xDS requests. There's no + // mechanism to make those pending requests fail, + // so we forcibly terminate the TCP sessions. + r.grpc.Stop() + }() + + if err = r.grpc.Serve(l); err != nil { r.Logger.Error(err, "failed to start grpc based xds server") } - - <-ctx.Done() - r.Logger.Info("grpc server shutting down") - // We don't use GracefulStop here because envoy - // has long-lived hanging xDS requests. There's no - // mechanism to make those pending requests fail, - // so we forcibly terminate the TCP sessions. - r.grpc.Stop() } // registerServer registers the given xDS protocol Server with the gRPC @@ -125,8 +133,8 @@ func registerServer(srv serverv3.Server, g *grpc.Server) { func (r *Runner) subscribeAndTranslate(ctx context.Context) { // Subscribe to resources - message.HandleSubscription(r.Xds.Subscribe(ctx), - func(update message.Update[string, *xdstypes.ResourceVersionTable]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentXdsServerRunner), Message: "xds"}, r.Xds.Subscribe(ctx), + func(update message.Update[string, *xdstypes.ResourceVersionTable], errChan chan error) { key := update.Key val := update.Value @@ -137,6 +145,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { } else if val != nil && val.XdsResources != nil { if r.cache == nil { r.Logger.Error(err, "failed to init snapshot cache") + errChan <- err } else { // Update snapshot cache err = r.cache.GenerateNewSnapshot(key, val.XdsResources) @@ -144,6 +153,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { } if err != nil { r.Logger.Error(err, "failed to generate a snapshot") + errChan <- err } }, ) diff --git a/internal/xds/server/runner/runner_test.go b/internal/xds/server/runner/runner_test.go index c1425a3ffab..6f574e2f9f2 100644 --- a/internal/xds/server/runner/runner_test.go +++ b/internal/xds/server/runner/runner_test.go @@ -22,7 +22,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/logging" "github.com/envoyproxy/gateway/internal/xds/bootstrap" diff --git a/internal/xds/translator/accesslog.go b/internal/xds/translator/accesslog.go index 0ae4fc6f27a..a74315a255e 100644 --- a/internal/xds/translator/accesslog.go +++ b/internal/xds/translator/accesslog.go @@ -6,6 +6,7 @@ package translator import ( + "errors" "sort" accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" @@ -18,6 +19,7 @@ import ( "golang.org/x/exp/maps" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/structpb" + "k8s.io/utils/ptr" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/xds/types" @@ -26,9 +28,21 @@ import ( const ( // EnvoyTextLogFormat is the default log format for Envoy. // See https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#default-format-string - EnvoyTextLogFormat = "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" " + - "%RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% " + - "\"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n" + EnvoyTextLogFormat = "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\"," + + "\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\"," + + "\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\"," + + "\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\"," + + "\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\"," + + "\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\"," + + "\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\"," + + "\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\"," + + "\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\"," + + "\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\"," + + "\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\"," + + "\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\"," + + "\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\"," + + "\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\"," + + "\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" otelLogName = "otel_envoy_accesslog" otelAccessLog = "envoy.access_loggers.open_telemetry" @@ -219,23 +233,29 @@ func convertToKeyValueList(attributes map[string]string, additionalLabels bool) return keyValueList } -func processClusterForAccessLog(tCtx *types.ResourceVersionTable, al *ir.AccessLog) { +func processClusterForAccessLog(tCtx *types.ResourceVersionTable, al *ir.AccessLog) error { if al == nil { - return + return nil } for _, otel := range al.OpenTelemetry { clusterName := buildClusterName("accesslog", otel.Host, otel.Port) - if existingCluster := findXdsCluster(tCtx, clusterName); existingCluster == nil { - destinations := []*ir.RouteDestination{ir.NewRouteDest(otel.Host, otel.Port)} - addXdsCluster(tCtx, addXdsClusterArgs{ - name: clusterName, - destinations: destinations, - tSocket: nil, - protocol: HTTP2, - endpoint: DefaultEndpointType, - }) + ds := &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Protocol: ir.GRPC, + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(otel.Host, otel.Port)}, + } + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: clusterName, + settings: []*ir.DestinationSetting{ds}, + tSocket: nil, + endpointType: EndpointTypeDNS, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err } + } + + return nil } diff --git a/internal/xds/translator/authentication.go b/internal/xds/translator/authentication.go deleted file mode 100644 index 7f662859d32..00000000000 --- a/internal/xds/translator/authentication.go +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -package translator - -import ( - "errors" - "fmt" - "net" - "net/url" - "strconv" - "strings" - - corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - jwtauthnv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" - hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/envoyproxy/go-control-plane/pkg/resource/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - - "github.com/envoyproxy/gateway/api/v1alpha1" - "github.com/envoyproxy/gateway/internal/ir" - "github.com/envoyproxy/gateway/internal/xds/types" -) - -const ( - jwtAuthenFilter = "envoy.filters.http.jwt_authn" - envoyTrustBundle = "/etc/ssl/certs/ca-certificates.crt" -) - -// patchHCMWithJwtAuthnFilter builds and appends the Jwt Filter to the HTTP -// Connection Manager if applicable, and it does not already exist. -func patchHCMWithJwtAuthnFilter(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { - if mgr == nil { - return errors.New("hcm is nil") - } - - if irListener == nil { - return errors.New("ir listener is nil") - } - - if !listenerContainsJwtAuthn(irListener) { - return nil - } - - // Return early if filter already exists. - for _, httpFilter := range mgr.HttpFilters { - if httpFilter.Name == jwtAuthenFilter { - return nil - } - } - - jwtFilter, err := buildHCMJwtFilter(irListener) - if err != nil { - return err - } - - // Ensure the authn filter is the first and the terminal filter is the last in the chain. - mgr.HttpFilters = append([]*hcmv3.HttpFilter{jwtFilter}, mgr.HttpFilters...) - - return nil -} - -// buildHCMJwtFilter returns a JWT authn HTTP filter from the provided IR listener. -func buildHCMJwtFilter(irListener *ir.HTTPListener) (*hcmv3.HttpFilter, error) { - jwtAuthnProto, err := buildJwtAuthn(irListener) - if err != nil { - return nil, err - } - - if err := jwtAuthnProto.ValidateAll(); err != nil { - return nil, err - } - - jwtAuthnAny, err := anypb.New(jwtAuthnProto) - if err != nil { - return nil, err - } - - return &hcmv3.HttpFilter{ - Name: jwtAuthenFilter, - ConfigType: &hcmv3.HttpFilter_TypedConfig{ - TypedConfig: jwtAuthnAny, - }, - }, nil -} - -// buildJwtAuthn returns a JwtAuthentication based on the provided IR HTTPListener. -func buildJwtAuthn(irListener *ir.HTTPListener) (*jwtauthnv3.JwtAuthentication, error) { - jwtProviders := make(map[string]*jwtauthnv3.JwtProvider) - reqMap := make(map[string]*jwtauthnv3.JwtRequirement) - - for _, route := range irListener.Routes { - if route != nil && routeContainsJwtAuthn(route) { - var reqs []*jwtauthnv3.JwtRequirement - for i := range route.RequestAuthentication.JWT.Providers { - irProvider := route.RequestAuthentication.JWT.Providers[i] - // Create the cluster for the remote jwks, if it doesn't exist. - jwksCluster, err := newJwksCluster(&irProvider) - if err != nil { - return nil, err - } - - remote := &jwtauthnv3.JwtProvider_RemoteJwks{ - RemoteJwks: &jwtauthnv3.RemoteJwks{ - HttpUri: &corev3.HttpUri{ - Uri: irProvider.RemoteJWKS.URI, - HttpUpstreamType: &corev3.HttpUri_Cluster{ - Cluster: jwksCluster.name, - }, - Timeout: &durationpb.Duration{Seconds: 5}, - }, - CacheDuration: &durationpb.Duration{Seconds: 5 * 60}, - }, - } - - claimToHeaders := []*jwtauthnv3.JwtClaimToHeader{} - for _, claimToHeader := range irProvider.ClaimToHeaders { - claimToHeader := &jwtauthnv3.JwtClaimToHeader{HeaderName: claimToHeader.Header, ClaimName: claimToHeader.Claim} - claimToHeaders = append(claimToHeaders, claimToHeader) - } - jwtProvider := &jwtauthnv3.JwtProvider{ - Issuer: irProvider.Issuer, - Audiences: irProvider.Audiences, - JwksSourceSpecifier: remote, - PayloadInMetadata: irProvider.Issuer, - ClaimToHeaders: claimToHeaders, - } - - providerKey := fmt.Sprintf("%s-%s", route.Name, irProvider.Name) - jwtProviders[providerKey] = jwtProvider - reqs = append(reqs, &jwtauthnv3.JwtRequirement{ - RequiresType: &jwtauthnv3.JwtRequirement_ProviderName{ - ProviderName: providerKey, - }, - }) - } - if len(reqs) == 1 { - reqMap[route.Name] = reqs[0] - } else { - orListReqs := &jwtauthnv3.JwtRequirement{ - RequiresType: &jwtauthnv3.JwtRequirement_RequiresAny{ - RequiresAny: &jwtauthnv3.JwtRequirementOrList{ - Requirements: reqs, - }, - }, - } - reqMap[route.Name] = orListReqs - } - } - } - - return &jwtauthnv3.JwtAuthentication{ - RequirementMap: reqMap, - Providers: jwtProviders, - }, nil -} - -// buildXdsUpstreamTLSSocket returns an xDS TransportSocket that uses envoyTrustBundle -// as the CA to authenticate server certificates. -func buildXdsUpstreamTLSSocket() (*corev3.TransportSocket, error) { - tlsCtxProto := &tlsv3.UpstreamTlsContext{ - CommonTlsContext: &tlsv3.CommonTlsContext{ - ValidationContextType: &tlsv3.CommonTlsContext_ValidationContext{ - ValidationContext: &tlsv3.CertificateValidationContext{ - TrustedCa: &corev3.DataSource{ - Specifier: &corev3.DataSource_Filename{ - Filename: envoyTrustBundle, - }, - }, - }, - }, - }, - } - - tlsCtxAny, err := anypb.New(tlsCtxProto) - if err != nil { - return nil, err - } - - return &corev3.TransportSocket{ - Name: wellknown.TransportSocketTls, - ConfigType: &corev3.TransportSocket_TypedConfig{ - TypedConfig: tlsCtxAny, - }, - }, nil -} - -// patchRouteWithJwtConfig patches the provided route with a JWT PerRouteConfig, if the -// route doesn't contain it. The listener is used to create the PerRouteConfig JWT -// requirement. -func patchRouteWithJwtConfig(route *routev3.Route, irRoute *ir.HTTPRoute, listener *listenerv3.Listener) error { //nolint:unparam - if route == nil { - return errors.New("xds route is nil") - } - if irRoute == nil { - return errors.New("ir route is nil") - } - if listener == nil { - return errors.New("listener is nil") - } - - filterCfg := route.GetTypedPerFilterConfig() - if _, ok := filterCfg[jwtAuthenFilter]; !ok { - if !routeContainsJwtAuthn(irRoute) { - return nil - } - - routeCfgProto, err := buildJwtPerRouteConfig(irRoute, listener) - if err != nil { - return fmt.Errorf("failed to build per route config for ir route %s", irRoute.Name) - } - - routeCfgAny, err := anypb.New(routeCfgProto) - if err != nil { - return err - } - - if filterCfg == nil { - route.TypedPerFilterConfig = make(map[string]*anypb.Any) - } - - route.TypedPerFilterConfig[jwtAuthenFilter] = routeCfgAny - } - - return nil -} - -// buildJwtPerRouteConfig returns a JWT PerRouteConfig based on the provided IR route and HCM. -func buildJwtPerRouteConfig(irRoute *ir.HTTPRoute, listener *listenerv3.Listener) (*jwtauthnv3.PerRouteConfig, error) { - if irRoute == nil { - return nil, errors.New("ir route is nil") - } - if irRoute == nil { - return nil, errors.New("ir route does not contain jwt authn") - } - if listener == nil { - return nil, errors.New("listener is nil") - } - - filterCh := listener.GetDefaultFilterChain() - if filterCh == nil { - return nil, fmt.Errorf("listener %s does not contain the default filterchain", listener.Name) - } - - for _, filter := range filterCh.Filters { - if filter.Name == wellknown.HTTPConnectionManager { - // Unmarshal the filter to a jwt authn config and validate it. - hcmProto := new(hcmv3.HttpConnectionManager) - hcmAny := filter.GetTypedConfig() - if err := hcmAny.UnmarshalTo(hcmProto); err != nil { - return nil, err - } - if err := hcmProto.ValidateAll(); err != nil { - return nil, err - } - // - req, err := getJwtRequirement(hcmProto.GetHttpFilters(), irRoute.Name) - if err != nil { - return nil, err - } - - return &jwtauthnv3.PerRouteConfig{ - RequirementSpecifier: req, - }, nil - } - } - - return nil, errors.New("failed to find HTTP connection manager filter") -} - -// getJwtRequirement iterates through the provided filters, returning a JWT requirement -// name if one exists. -func getJwtRequirement(filters []*hcmv3.HttpFilter, name string) (*jwtauthnv3.PerRouteConfig_RequirementName, error) { - if len(filters) == 0 { - return nil, errors.New("no hcmv3 http filters") - } - - for _, filter := range filters { - if filter.Name == jwtAuthenFilter { - // Unmarshal the filter to a jwt authn config and validate it. - jwtAuthnProto := new(jwtauthnv3.JwtAuthentication) - jwtAuthnAny := filter.GetTypedConfig() - if err := jwtAuthnAny.UnmarshalTo(jwtAuthnProto); err != nil { - return nil, err - } - if err := jwtAuthnProto.ValidateAll(); err != nil { - return nil, err - } - // Return the requirement name if it's found. - if _, found := jwtAuthnProto.RequirementMap[name]; found { - return &jwtauthnv3.PerRouteConfig_RequirementName{RequirementName: name}, nil - } - return nil, fmt.Errorf("failed to find jwt requirement %s", name) - } - } - - return nil, errors.New("failed to find jwt authn filter") -} - -type jwksCluster struct { - name string - hostname string - port uint32 - isStatic bool -} - -// createJwksClusters creates JWKS clusters from the provided routes, if needed. -func createJwksClusters(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { - if tCtx == nil || - tCtx.XdsResources == nil || - tCtx.XdsResources[resource.ClusterType] == nil || - len(routes) == 0 { - return nil - } - - for _, route := range routes { - if routeContainsJwtAuthn(route) { - for i := range route.RequestAuthentication.JWT.Providers { - provider := route.RequestAuthentication.JWT.Providers[i] - jwks, err := newJwksCluster(&provider) - ep := DefaultEndpointType - if jwks.isStatic { - ep = Static - } - if err != nil { - return err - } - if existingCluster := findXdsCluster(tCtx, jwks.name); existingCluster == nil { - routeDestinations := []*ir.RouteDestination{ir.NewRouteDest(jwks.hostname, jwks.port)} - tSocket, err := buildXdsUpstreamTLSSocket() - if err != nil { - return err - } - addXdsCluster(tCtx, addXdsClusterArgs{ - name: jwks.name, - destinations: routeDestinations, - tSocket: tSocket, - protocol: DefaultProtocol, - endpoint: ep, - }) - } - } - } - } - - return nil -} - -// newJwksCluster returns a jwksCluster from the provided provider. -func newJwksCluster(provider *v1alpha1.JwtAuthenticationFilterProvider) (*jwksCluster, error) { - static := false - if provider == nil { - return nil, errors.New("nil provider") - } - - u, err := url.Parse(provider.RemoteJWKS.URI) - if err != nil { - return nil, err - } - - var strPort string - switch u.Scheme { - case "https": - strPort = "443" - default: - return nil, fmt.Errorf("unsupported JWKS URI scheme %s", u.Scheme) - } - - if u.Port() != "" { - strPort = u.Port() - } - - name := fmt.Sprintf("%s_%s", strings.ReplaceAll(u.Hostname(), ".", "_"), strPort) - - port, err := strconv.Atoi(strPort) - if err != nil { - return nil, err - } - - if ip := net.ParseIP(u.Hostname()); ip != nil { - if v4 := ip.To4(); v4 != nil { - static = true - } - } - - return &jwksCluster{ - name: name, - hostname: u.Hostname(), - port: uint32(port), - isStatic: static, - }, nil -} - -// listenerContainsJwtAuthn returns true if JWT authentication exists for the -// provided listener. -func listenerContainsJwtAuthn(irListener *ir.HTTPListener) bool { - if irListener == nil { - return false - } - - for _, route := range irListener.Routes { - if routeContainsJwtAuthn(route) { - return true - } - } - - return false -} - -// routeContainsJwtAuthn returns true if JWT authentication exists for the -// provided route. -func routeContainsJwtAuthn(irRoute *ir.HTTPRoute) bool { - if irRoute == nil { - return false - } - - if irRoute != nil && - irRoute.RequestAuthentication != nil && - irRoute.RequestAuthentication.JWT != nil && - irRoute.RequestAuthentication.JWT.Providers != nil && - len(irRoute.RequestAuthentication.JWT.Providers) > 0 { - return true - } - - return false -} diff --git a/internal/xds/translator/basicauth.go b/internal/xds/translator/basicauth.go new file mode 100644 index 00000000000..a7af38551c7 --- /dev/null +++ b/internal/xds/translator/basicauth.go @@ -0,0 +1,189 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "errors" + "fmt" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + basicauthv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/basic_auth/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/tetratelabs/multierror" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/types" +) + +const ( + basicAuthFilter = "envoy.filters.http.basic_auth" +) + +func init() { + registerHTTPFilter(&basicAuth{}) +} + +type basicAuth struct { +} + +var _ httpFilter = &basicAuth{} + +// patchHCM builds and appends the basic_auth Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an basic_auth filter for each route that contains an BasicAuth config. +func (*basicAuth) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + var errs error + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + for _, route := range irListener.Routes { + if !routeContainsBasicAuth(route) { + continue + } + + filter, err := buildHCMBasicAuthFilter(route) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + // skip if the filter already exists + for _, existingFilter := range mgr.HttpFilters { + if filter.Name == existingFilter.Name { + continue + } + } + + mgr.HttpFilters = append(mgr.HttpFilters, filter) + } + + return nil +} + +// buildHCMBasicAuthFilter returns a basic_auth HTTP filter from the provided IR HTTPRoute. +func buildHCMBasicAuthFilter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) { + basicAuthProto := basicAuthConfig(route) + + if err := basicAuthProto.ValidateAll(); err != nil { + return nil, err + } + + basicAuthAny, err := anypb.New(basicAuthProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: basicAuthFilterName(route), + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: basicAuthAny, + }, + }, nil +} + +func basicAuthFilterName(route *ir.HTTPRoute) string { + return perRouteFilterName(basicAuthFilter, route.Name) +} + +func basicAuthConfig(route *ir.HTTPRoute) *basicauthv3.BasicAuth { + return &basicauthv3.BasicAuth{ + Users: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: route.BasicAuth.Users, + }, + }, + } +} + +// routeContainsBasicAuth returns true if BasicAuth exists for the provided route. +func routeContainsBasicAuth(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.BasicAuth != nil { + return true + } + + return false +} + +func (*basicAuth) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error { + return nil +} + +// patchRouteCfg patches the provided route configuration with the basicAuth filter +// if applicable. +// Note: this method disables all the basicAuth filters by default. The filter will +// be enabled per-route in the typePerFilterConfig of the route. +func (*basicAuth) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error { + if routeCfg == nil { + return errors.New("route configuration is nil") + } + if irListener == nil { + return errors.New("ir listener is nil") + } + + var errs error + for _, route := range irListener.Routes { + if !routeContainsBasicAuth(route) { + continue + } + + filterName := basicAuthFilterName(route) + filterCfg := routeCfg.TypedPerFilterConfig + + if _, ok := filterCfg[filterName]; ok { + // This should not happen since this is the only place where the basicAuth + // filter is added in a route. + errs = multierror.Append(errs, fmt.Errorf( + "route config already contains basicAuth config: %+v", route)) + continue + } + + // Disable all the filters by default. The filter will be enabled + // per-route in the typePerFilterConfig of the route. + routeCfgAny, err := anypb.New(&routev3.FilterConfig{Disabled: true}) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + if filterCfg == nil { + routeCfg.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + routeCfg.TypedPerFilterConfig[filterName] = routeCfgAny + } + return errs +} + +// patchRoute patches the provided route with the basicAuth config if applicable. +// Note: this method enables the corresponding basicAuth filter for the provided route. +func (*basicAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.BasicAuth == nil { + return nil + } + if err := enableFilterOnRoute(basicAuthFilter, route, irRoute); err != nil { + return err + } + return nil +} diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go index 08883e70f25..d7aa3d21aff 100644 --- a/internal/xds/translator/cluster.go +++ b/internal/xds/translator/cluster.go @@ -12,8 +12,11 @@ import ( clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + proxyprotocolv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3" + rawbufferv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3" httpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" @@ -27,12 +30,26 @@ const ( tcpClusterPerConnectionBufferLimitBytes = 32768 ) -func buildXdsCluster(routeName string, tSocket *corev3.TransportSocket, protocol ProtocolType, endpointType EndpointType) *clusterv3.Cluster { - clusterName := routeName +type xdsClusterArgs struct { + name string + settings []*ir.DestinationSetting + tSocket *corev3.TransportSocket + endpointType EndpointType + loadBalancer *ir.LoadBalancer + proxyProtocol *ir.ProxyProtocol +} + +type EndpointType int + +const ( + EndpointTypeDNS EndpointType = iota + EndpointTypeStatic +) + +func buildXdsCluster(args *xdsClusterArgs) *clusterv3.Cluster { cluster := &clusterv3.Cluster{ - Name: clusterName, + Name: args.name, ConnectTimeout: durationpb.New(10 * time.Second), - LbPolicy: clusterv3.Cluster_ROUND_ROBIN, DnsLookupFamily: clusterv3.Cluster_V4_ONLY, CommonLbConfig: &clusterv3.Cluster_CommonLbConfig{ LocalityConfigSpecifier: &clusterv3.Cluster_CommonLbConfig_LocalityWeightedLbConfig_{ @@ -41,14 +58,17 @@ func buildXdsCluster(routeName string, tSocket *corev3.TransportSocket, protocol PerConnectionBufferLimitBytes: wrapperspb.UInt32(tcpClusterPerConnectionBufferLimitBytes), } - if tSocket != nil { - cluster.TransportSocket = tSocket + // Set Proxy Protocol + if args.proxyProtocol != nil { + cluster.TransportSocket = buildProxyProtocolSocket(args.proxyProtocol, args.tSocket) + } else if args.tSocket != nil { + cluster.TransportSocket = args.tSocket } - if endpointType == Static { + if args.endpointType == EndpointTypeStatic { cluster.ClusterDiscoveryType = &clusterv3.Cluster_Type{Type: clusterv3.Cluster_EDS} cluster.EdsClusterConfig = &clusterv3.Cluster_EdsClusterConfig{ - ServiceName: clusterName, + ServiceName: args.name, EdsConfig: &corev3.ConfigSource{ ResourceApiVersion: resource.DefaultAPIVersion, ConfigSourceSpecifier: &corev3.ConfigSource_Ads{ @@ -62,50 +82,108 @@ func buildXdsCluster(routeName string, tSocket *corev3.TransportSocket, protocol cluster.RespectDnsTtl = true } - if protocol == HTTP2 { + isHTTP2 := false + for _, ds := range args.settings { + if ds.Protocol == ir.GRPC || + ds.Protocol == ir.HTTP2 { + isHTTP2 = true + break + } + } + if isHTTP2 { cluster.TypedExtensionProtocolOptions = buildTypedExtensionProtocolOptions() } + // Set Load Balancer policy + //nolint:gocritic + if args.loadBalancer == nil { + cluster.LbPolicy = clusterv3.Cluster_LEAST_REQUEST + } else if args.loadBalancer.LeastRequest != nil { + cluster.LbPolicy = clusterv3.Cluster_LEAST_REQUEST + if args.loadBalancer.LeastRequest.SlowStart != nil { + if args.loadBalancer.LeastRequest.SlowStart.Window != nil { + cluster.LbConfig = &clusterv3.Cluster_LeastRequestLbConfig_{ + LeastRequestLbConfig: &clusterv3.Cluster_LeastRequestLbConfig{ + SlowStartConfig: &clusterv3.Cluster_SlowStartConfig{ + SlowStartWindow: durationpb.New(args.loadBalancer.LeastRequest.SlowStart.Window.Duration), + }, + }, + } + } + } + } else if args.loadBalancer.RoundRobin != nil { + cluster.LbPolicy = clusterv3.Cluster_ROUND_ROBIN + if args.loadBalancer.RoundRobin.SlowStart != nil { + if args.loadBalancer.RoundRobin.SlowStart.Window != nil { + cluster.LbConfig = &clusterv3.Cluster_RoundRobinLbConfig_{ + RoundRobinLbConfig: &clusterv3.Cluster_RoundRobinLbConfig{ + SlowStartConfig: &clusterv3.Cluster_SlowStartConfig{ + SlowStartWindow: durationpb.New(args.loadBalancer.RoundRobin.SlowStart.Window.Duration), + }, + }, + } + } + } + } else if args.loadBalancer.Random != nil { + cluster.LbPolicy = clusterv3.Cluster_RANDOM + } else if args.loadBalancer.ConsistentHash != nil { + cluster.LbPolicy = clusterv3.Cluster_MAGLEV + } + return cluster } -func buildXdsClusterLoadAssignment(clusterName string, destinations []*ir.RouteDestination) *endpointv3.ClusterLoadAssignment { - endpoints := make([]*endpointv3.LbEndpoint, 0, len(destinations)) - for _, destination := range destinations { - lbEndpoint := &endpointv3.LbEndpoint{ - HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ - Endpoint: &endpointv3.Endpoint{ - Address: &corev3.Address{ - Address: &corev3.Address_SocketAddress{ - SocketAddress: &corev3.SocketAddress{ - Protocol: corev3.SocketAddress_TCP, - Address: destination.Host, - PortSpecifier: &corev3.SocketAddress_PortValue{ - PortValue: destination.Port, +func buildXdsClusterLoadAssignment(clusterName string, destSettings []*ir.DestinationSetting) *endpointv3.ClusterLoadAssignment { + localities := make([]*endpointv3.LocalityLbEndpoints, 0, len(destSettings)) + for i, ds := range destSettings { + + endpoints := make([]*endpointv3.LbEndpoint, 0, len(ds.Endpoints)) + + for _, irEp := range ds.Endpoints { + lbEndpoint := &endpointv3.LbEndpoint{ + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Protocol: corev3.SocketAddress_TCP, + Address: irEp.Host, + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: irEp.Port, + }, }, }, }, }, }, - }, + } + // Set default weight of 1 for all endpoints. + lbEndpoint.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: 1} + endpoints = append(endpoints, lbEndpoint) } - if destination.Weight != nil { - lbEndpoint.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: *destination.Weight} + + // Envoy requires a distinct region to be set for each LocalityLbEndpoints. + // If we don't do this, Envoy will merge all LocalityLbEndpoints into one. + // We use the name of the backendRef as a pseudo region name. + locality := &endpointv3.LocalityLbEndpoints{ + Locality: &corev3.Locality{ + Region: fmt.Sprintf("%s/backend/%d", clusterName, i), + }, + LbEndpoints: endpoints, + Priority: 0, } - endpoints = append(endpoints, lbEndpoint) - } - localities := make([]*endpointv3.LocalityLbEndpoints, 0, 1) - locality := &endpointv3.LocalityLbEndpoints{ - Locality: &corev3.Locality{}, - LbEndpoints: endpoints, - Priority: 0, - // Each locality gets the same weight 1. There is a single locality - // per priority, so the weight value does not really matter, but some - // load balancers need the value to be set. - LoadBalancingWeight: &wrapperspb.UInt32Value{Value: 1}} - localities = append(localities, locality) + // Set locality weight + var weight uint32 + if ds.Weight != nil { + weight = *ds.Weight + } else { + weight = 1 + } + locality.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: weight} + localities = append(localities, locality) + } return &endpointv3.ClusterLoadAssignment{ClusterName: clusterName, Endpoints: localities} } @@ -133,3 +211,53 @@ func buildTypedExtensionProtocolOptions() map[string]*anypb.Any { func buildClusterName(prefix string, host string, port uint32) string { return fmt.Sprintf("%s|%s|%d", prefix, host, port) } + +// buildProxyProtocolSocket builds the ProxyProtocol transport socket. +func buildProxyProtocolSocket(proxyProtocol *ir.ProxyProtocol, tSocket *corev3.TransportSocket) *corev3.TransportSocket { + if proxyProtocol == nil { + return nil + } + + ppCtx := &proxyprotocolv3.ProxyProtocolUpstreamTransport{} + + switch proxyProtocol.Version { + case ir.ProxyProtocolVersionV1: + ppCtx.Config = &corev3.ProxyProtocolConfig{ + Version: corev3.ProxyProtocolConfig_V1, + } + case ir.ProxyProtocolVersionV2: + ppCtx.Config = &corev3.ProxyProtocolConfig{ + Version: corev3.ProxyProtocolConfig_V2, + } + } + + // If existing transport socket does not exist wrap around raw buffer + if tSocket == nil { + rawCtx := &rawbufferv3.RawBuffer{} + rawCtxAny, err := anypb.New(rawCtx) + if err != nil { + return nil + } + rawSocket := &corev3.TransportSocket{ + Name: wellknown.TransportSocketRawBuffer, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: rawCtxAny, + }, + } + ppCtx.TransportSocket = rawSocket + } else { + ppCtx.TransportSocket = tSocket + } + + ppCtxAny, err := anypb.New(ppCtx) + if err != nil { + return nil + } + + return &corev3.TransportSocket{ + Name: "envoy.transport_sockets.upstream_proxy_protocol", + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: ppCtxAny, + }, + } +} diff --git a/internal/xds/translator/cluster_test.go b/internal/xds/translator/cluster_test.go index 37e31fc7013..f4b71c59b44 100644 --- a/internal/xds/translator/cluster_test.go +++ b/internal/xds/translator/cluster_test.go @@ -28,21 +28,27 @@ const ( func TestBuildXdsCluster(t *testing.T) { bootstrapXdsCluster := getXdsClusterObjFromBootstrap(t) - dynamicXdsCluster := buildXdsCluster(bootstrapXdsCluster.Name, bootstrapXdsCluster.TransportSocket, HTTP2, DefaultEndpointType) + args := &xdsClusterArgs{ + name: bootstrapXdsCluster.Name, + tSocket: bootstrapXdsCluster.TransportSocket, + endpointType: EndpointTypeDNS, + } + dynamicXdsCluster := buildXdsCluster(args) require.Equal(t, bootstrapXdsCluster.Name, dynamicXdsCluster.Name) require.Equal(t, bootstrapXdsCluster.ClusterDiscoveryType, dynamicXdsCluster.ClusterDiscoveryType) require.Equal(t, bootstrapXdsCluster.TransportSocket, dynamicXdsCluster.TransportSocket) assert.True(t, proto.Equal(bootstrapXdsCluster.TransportSocket, dynamicXdsCluster.TransportSocket)) assert.True(t, proto.Equal(bootstrapXdsCluster.ConnectTimeout, dynamicXdsCluster.ConnectTimeout)) - assert.True(t, proto.Equal(bootstrapXdsCluster.TypedExtensionProtocolOptions[extensionOptionsKey], dynamicXdsCluster.TypedExtensionProtocolOptions[extensionOptionsKey])) } func TestBuildXdsClusterLoadAssignment(t *testing.T) { bootstrapXdsCluster := getXdsClusterObjFromBootstrap(t) - destinations := []*ir.RouteDestination{{Host: envoyGatewayXdsServerHost, Port: bootstrap.DefaultXdsServerPort}} - - dynamicXdsClusterLoadAssignment := buildXdsClusterLoadAssignment(bootstrapXdsCluster.Name, destinations) + ds := &ir.DestinationSetting{ + Endpoints: []*ir.DestinationEndpoint{{Host: envoyGatewayXdsServerHost, Port: bootstrap.DefaultXdsServerPort}}, + } + settings := []*ir.DestinationSetting{ds} + dynamicXdsClusterLoadAssignment := buildXdsClusterLoadAssignment(bootstrapXdsCluster.Name, settings) assert.True(t, proto.Equal(bootstrapXdsCluster.LoadAssignment.Endpoints[0].LbEndpoints[0], dynamicXdsClusterLoadAssignment.Endpoints[0].LbEndpoints[0])) } diff --git a/internal/xds/translator/cors.go b/internal/xds/translator/cors.go new file mode 100644 index 00000000000..a7fe606d03e --- /dev/null +++ b/internal/xds/translator/cors.go @@ -0,0 +1,174 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "errors" + "fmt" + "strconv" + "strings" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + corsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/golang/protobuf/ptypes/wrappers" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/types" +) + +func init() { + registerHTTPFilter(&cors{}) +} + +type cors struct { +} + +var _ httpFilter = &cors{} + +// patchHCM builds and appends the CORS Filter to the HTTP Connection Manager if +// applicable. +func (*cors) patchHCM( + mgr *hcmv3.HttpConnectionManager, + irListener *ir.HTTPListener) error { + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + if !listenerContainsCORS(irListener) { + return nil + } + + // Return early if filter already exists. + for _, httpFilter := range mgr.HttpFilters { + if httpFilter.Name == wellknown.CORS { + return nil + } + } + + corsFilter, err := buildHCMCORSFilter() + if err != nil { + return err + } + + // Ensure the CORS filter is the first one in the filter chain. + mgr.HttpFilters = append([]*hcmv3.HttpFilter{corsFilter}, mgr.HttpFilters...) + + return nil +} + +// buildHCMCORSFilter returns a CORS filter from the provided IR listener. +func buildHCMCORSFilter() (*hcmv3.HttpFilter, error) { + corsProto := &corsv3.Cors{} + + corsAny, err := anypb.New(corsProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: wellknown.CORS, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: corsAny, + }, + }, nil +} + +// listenerContainsCORS returns true if the provided listener has CORS +// policies attached to its routes. +func listenerContainsCORS(irListener *ir.HTTPListener) bool { + if irListener == nil { + return false + } + + for _, route := range irListener.Routes { + if route.CORS != nil { + return true + } + } + + return false +} + +// patchRoute patches the provided route with the CORS config if applicable. +func (*cors) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.CORS == nil { + return nil + } + + filterCfg := route.GetTypedPerFilterConfig() + if _, ok := filterCfg[wellknown.CORS]; ok { + // This should not happen since this is the only place where the CORS + // filter is added in a route. + return fmt.Errorf("route already contains cors config: %+v", route) + } + + var ( + allowOrigins []*matcherv3.StringMatcher + allowMethods string + allowHeaders string + exposeHeaders string + maxAge string + allowCredentials *wrappers.BoolValue + ) + + //nolint:gocritic + + for _, origin := range irRoute.CORS.AllowOrigins { + allowOrigins = append(allowOrigins, buildXdsStringMatcher(origin)) + } + + allowMethods = strings.Join(irRoute.CORS.AllowMethods, ", ") + allowHeaders = strings.Join(irRoute.CORS.AllowHeaders, ", ") + exposeHeaders = strings.Join(irRoute.CORS.ExposeHeaders, ", ") + if irRoute.CORS.MaxAge != nil { + maxAge = strconv.Itoa(int(irRoute.CORS.MaxAge.Seconds())) + } + allowCredentials = &wrappers.BoolValue{Value: irRoute.CORS.AllowCredentials} + + routeCfgProto := &corsv3.CorsPolicy{ + AllowOriginStringMatch: allowOrigins, + AllowMethods: allowMethods, + AllowHeaders: allowHeaders, + ExposeHeaders: exposeHeaders, + MaxAge: maxAge, + AllowCredentials: allowCredentials, + } + + routeCfgAny, err := anypb.New(routeCfgProto) + if err != nil { + return err + } + + if filterCfg == nil { + route.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + route.TypedPerFilterConfig[wellknown.CORS] = routeCfgAny + + return nil +} + +func (*cors) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error { + return nil +} + +func (c *cors) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error { + return nil +} diff --git a/internal/xds/translator/extension.go b/internal/xds/translator/extension.go index 0c925a75149..b8eae0c5f7a 100644 --- a/internal/xds/translator/extension.go +++ b/internal/xds/translator/extension.go @@ -21,7 +21,7 @@ import ( resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" extensionTypes "github.com/envoyproxy/gateway/internal/extension/types" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/xds/types" @@ -108,7 +108,7 @@ func processExtensionPostListenerHook(tCtx *types.ResourceVersionTable, xdsListe } else if modifiedListener != nil { // Use the resource table to update the listener with the modified version returned by the extension // We're assuming that Listener names are unique. - tCtx.AddOrReplaceXdsResource(resourcev3.ListenerType, modifiedListener, func(existing resourceTypes.Resource, new resourceTypes.Resource) bool { + if err := tCtx.AddOrReplaceXdsResource(resourcev3.ListenerType, modifiedListener, func(existing resourceTypes.Resource, new resourceTypes.Resource) bool { oldListener := existing.(*listenerv3.Listener) newListener := new.(*listenerv3.Listener) if newListener == nil || oldListener == nil { @@ -118,7 +118,9 @@ func processExtensionPostListenerHook(tCtx *types.ResourceVersionTable, xdsListe return true } return false - }) + }); err != nil { + return err + } } } return nil diff --git a/internal/xds/translator/httpfilters.go b/internal/xds/translator/httpfilters.go new file mode 100644 index 00000000000..f16bb5c09ac --- /dev/null +++ b/internal/xds/translator/httpfilters.go @@ -0,0 +1,232 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "sort" + "strings" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + + "github.com/envoyproxy/gateway/internal/xds/filters" + "github.com/envoyproxy/gateway/internal/xds/types" + + "github.com/envoyproxy/gateway/internal/ir" +) + +var httpFilters []httpFilter + +// registerHTTPFilter registers the provided HTTP filter. +func registerHTTPFilter(filter httpFilter) { + httpFilters = append(httpFilters, filter) +} + +// httpFilter is the interface for all the HTTP filters. +// +// There are two ways to support per-route configuration for an HTTP filter: +// - For the filters with native per-route configuration support: +// - patchHCM: EG adds the filter to the HCM filter chain only once. +// - patchRoute: EG adds the filter's native per-route configuration to each +// route's typedFilterConfig. +// +// - For the filters without native per-route configuration support: +// - patchHCM: EG adds a filter for each route in the HCM filter chain, the +// filter name is prefixed with the filter's type name, for example, +// "envoy.filters.http.oauth2", and suffixed with the route name. Each filter +// is configured with the route's per-route configuration. +// - patchRouteConfig: EG disables all the filters of this type in the +// typedFilterConfig of the route config. +// - PatchRouteWithPerRouteConfig: EG enables the corresponding filter for each +// route in the typedFilterConfig of that route. +// +// The filter types that haven't native per-route support: oauth2, basic authn +// Note: The filter types that have native per-route configuration support should +// always se their own native per-route configuration. +type httpFilter interface { + // patchHCM patches the HttpConnectionManager with the filter. + patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error + + // patchRouteConfig patches the provided RouteConfiguration with a filter's + // RouteConfiguration level configuration. + patchRouteConfig(rc *routev3.RouteConfiguration, irListener *ir.HTTPListener) error + + // patchRoute patches the provide Route with a filter's Route level configuration. + patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error + + // patchResources adds all the other needed resources referenced by this + // filter to the resource version table. + // for example: + // - a jwt filter needs to add the cluster for the jwks. + // - an oidc filter needs to add the cluster for token endpoint and the secret + // for the oauth2 client secret and the hmac secret. + patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error +} + +type OrderedHTTPFilter struct { + filter *hcmv3.HttpFilter + order int +} + +type OrderedHTTPFilters []*OrderedHTTPFilter + +// newOrderedHTTPFilter gives each HTTP filter a rational order. +// This is needed because the order of the filters is important. +// For example, the cors filter should be put at the first to avoid unnecessary +// processing of other filters for unauthorized cross-region access. +// The router filter must be the last one since it's a terminal filter. +// +// Important: please modify this method and set the order for the new filter +// when adding a new filter in the HCM filter chain. +// If the order is not explicitly specified in this method, a filter will be set +// a default order 50. +func newOrderedHTTPFilter(filter *hcmv3.HttpFilter) *OrderedHTTPFilter { + order := 50 + + // Set a rational order for all the filters. + switch { + case filter.Name == wellknown.CORS: + order = 1 + case filter.Name == basicAuthFilter: + order = 2 + case isOAuth2Filter(filter): + order = 3 + case filter.Name == jwtAuthn: + order = 4 + case filter.Name == wellknown.HTTPRateLimit: + order = 5 + case filter.Name == wellknown.Router: + order = 100 + } + + return &OrderedHTTPFilter{ + filter: filter, + order: order, + } +} + +// sort.Interface implementation. + +func (o OrderedHTTPFilters) Len() int { + return len(o) +} + +func (o OrderedHTTPFilters) Less(i, j int) bool { + return o[i].order < o[j].order +} + +func (o OrderedHTTPFilters) Swap(i, j int) { + o[i], o[j] = o[j], o[i] +} + +// sortHTTPFilters sorts the HTTP filters in the correct order. +// This is needed because the order of the filters is important. +// For example, the cors filter should be put at the first to avoid unnecessary +// processing of other filters for unauthorized cross-region access. +// The router filter must be the last one since it's a terminal filter. +func sortHTTPFilters(filters []*hcmv3.HttpFilter) []*hcmv3.HttpFilter { + orderedFilters := make(OrderedHTTPFilters, len(filters)) + for i := 0; i < len(filters); i++ { + orderedFilters[i] = newOrderedHTTPFilter(filters[i]) + } + sort.Sort(orderedFilters) + + for i := 0; i < len(filters); i++ { + filters[i] = orderedFilters[i].filter + } + return filters +} + +// patchHCMWithFilters builds and appends HTTP Filters to the HTTP connection +// manager. +// Important: don't forget to set the order for newly added filters in the +// newOrderedHTTPFilter method. +func (t *Translator) patchHCMWithFilters( + mgr *hcmv3.HttpConnectionManager, + irListener *ir.HTTPListener) error { + // The order of filter patching is not relevant here. + // All the filters will be sorted in correct order after the patching is done. + // + // Important: don't forget to set the order for new filters in the + // newOrderedHTTPFilter method. + for _, filter := range httpFilters { + if err := filter.patchHCM(mgr, irListener); err != nil { + return err + } + } + + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + t.patchHCMWithRateLimit(mgr, irListener) + + // Add the router filter + mgr.HttpFilters = append(mgr.HttpFilters, filters.HTTPRouter) + + // Sort the filters in the correct order. + mgr.HttpFilters = sortHTTPFilters(mgr.HttpFilters) + return nil +} + +// patchRouteCfgWithPerRouteConfig appends per-route filter configurations to the +// provided listener's RouteConfiguration. +// This method is used to disable the filters without native per-route support. +// The disabled filters will be enabled by route in the patchRouteWithPerRouteConfig +// method. +func patchRouteCfgWithPerRouteConfig( + routeCfg *routev3.RouteConfiguration, + irListener *ir.HTTPListener) error { + // Only supports the oauth2 filter for now, other filters will be added later. + for _, filter := range httpFilters { + if err := filter.patchRouteConfig(routeCfg, irListener); err != nil { + return err + } + } + return nil +} + +// patchRouteWithPerRouteConfig appends per-route filter configuration to the +// provided route. +func patchRouteWithPerRouteConfig( + route *routev3.Route, + irRoute *ir.HTTPRoute) error { + + for _, filter := range httpFilters { + if err := filter.patchRoute(route, irRoute); err != nil { + return err + } + } + + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + if err := + patchRouteWithRateLimit(route.GetRoute(), irRoute); err != nil { + return nil + } + + return nil +} + +// isOAuth2Filter returns true if the provided filter is an OAuth2 filter. +func isOAuth2Filter(filter *hcmv3.HttpFilter) bool { + // Multiple oauth2 filters are added to the HCM filter chain, one for each + // route. The oauth2 filter name is prefixed with "envoy.filters.http.oauth2". + return strings.HasPrefix(filter.Name, oauth2Filter) +} + +// patchResources adds all the other needed resources referenced by this +// filter to the resource version table. +// for example: +// - a jwt filter needs to add the cluster for the jwks. +// - an oidc filter needs to add the secret for the oauth2 client secret. +func patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { + for _, filter := range httpFilters { + if err := filter.patchResources(tCtx, routes); err != nil { + return err + } + } + return nil +} diff --git a/internal/xds/translator/httpfilters_test.go b/internal/xds/translator/httpfilters_test.go new file mode 100644 index 00000000000..d1cce4f5ea1 --- /dev/null +++ b/internal/xds/translator/httpfilters_test.go @@ -0,0 +1,49 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "testing" + + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/stretchr/testify/assert" +) + +func Test_sortHTTPFilters(t *testing.T) { + tests := []struct { + name string + filters []*hcmv3.HttpFilter + want []*hcmv3.HttpFilter + }{ + { + name: "sort filters", + filters: []*hcmv3.HttpFilter{ + httpFilterForTest(wellknown.Router), + httpFilterForTest(wellknown.CORS), + httpFilterForTest(jwtAuthn), + httpFilterForTest(wellknown.HTTPRateLimit), + }, + want: []*hcmv3.HttpFilter{ + httpFilterForTest(wellknown.CORS), + httpFilterForTest(jwtAuthn), + httpFilterForTest(wellknown.HTTPRateLimit), + httpFilterForTest(wellknown.Router), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, sortHTTPFilters(tt.filters), "sortHTTPFilters(%v)", tt.filters) + }) + } +} + +func httpFilterForTest(name string) *hcmv3.HttpFilter { + return &hcmv3.HttpFilter{ + Name: name, + } +} diff --git a/internal/xds/translator/jsonpatch.go b/internal/xds/translator/jsonpatch.go index f0c3929c2b1..c9364c769dc 100644 --- a/internal/xds/translator/jsonpatch.go +++ b/internal/xds/translator/jsonpatch.go @@ -7,6 +7,7 @@ package translator import ( "fmt" + "strings" clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" @@ -37,6 +38,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* } for _, e := range envoyPatchPolicies { + e := e for _, p := range e.JSONPatches { var ( listener *listenerv3.Listener @@ -68,56 +70,55 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) + msg := unmarshalErrorMessage(err, p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - if err = temp.Validate(); err != nil { + if err = tCtx.AddXdsResource(resourcev3.ListenerType, temp); err != nil { msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - tCtx.AddXdsResource(resourcev3.ListenerType, temp) case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) + msg := unmarshalErrorMessage(err, p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - if err = temp.Validate(); err != nil { + if err = tCtx.AddXdsResource(resourcev3.RouteType, temp); err != nil { msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - tCtx.AddXdsResource(resourcev3.RouteType, temp) + case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) + msg := unmarshalErrorMessage(err, p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - if err = temp.Validate(); err != nil { + if err = tCtx.AddXdsResource(resourcev3.ClusterType, temp); err != nil { msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - tCtx.AddXdsResource(resourcev3.ClusterType, temp) + case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) + msg := unmarshalErrorMessage(err, p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - if err = temp.Validate(); err != nil { + if err = tCtx.AddXdsResource(resourcev3.EndpointType, temp); err != nil { msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } - tCtx.AddXdsResource(resourcev3.EndpointType, temp) + } // Skip further processing @@ -215,7 +216,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) + msg := unmarshalErrorMessage(err, string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -232,7 +233,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) + msg := unmarshalErrorMessage(err, string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -249,7 +250,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) + msg := unmarshalErrorMessage(err, string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -266,7 +267,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) + msg := unmarshalErrorMessage(err, string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -292,3 +293,9 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* } return errs } + +var unescaper = strings.NewReplacer(" ", " ") + +func unmarshalErrorMessage(err error, xdsResource any) string { + return fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", xdsResource, unescaper.Replace(err.Error())) +} diff --git a/internal/xds/translator/jwt.go b/internal/xds/translator/jwt.go new file mode 100644 index 00000000000..2978c548505 --- /dev/null +++ b/internal/xds/translator/jwt.go @@ -0,0 +1,331 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "errors" + "fmt" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + jwtauthnv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/tetratelabs/multierror" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "k8s.io/utils/ptr" + + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/types" +) + +const ( + jwtAuthn = "envoy.filters.http.jwt_authn" + envoyTrustBundle = "/etc/ssl/certs/ca-certificates.crt" +) + +func init() { + registerHTTPFilter(&jwt{}) +} + +type jwt struct { +} + +var _ httpFilter = &jwt{} + +// patchHCM builds and appends the JWT Filter to the HTTP Connection Manager if +// applicable, and it does not already exist. +func (*jwt) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + if !listenerContainsJWTAuthn(irListener) { + return nil + } + + // Return early if filter already exists. + for _, httpFilter := range mgr.HttpFilters { + if httpFilter.Name == jwtAuthn { + return nil + } + } + + jwtFilter, err := buildHCMJWTFilter(irListener) + if err != nil { + return err + } + + // Ensure the authn filter is the first and the terminal filter is the last in the chain. + mgr.HttpFilters = append([]*hcmv3.HttpFilter{jwtFilter}, mgr.HttpFilters...) + + return nil +} + +// buildHCMJWTFilter returns a JWT authn HTTP filter from the provided IR listener. +func buildHCMJWTFilter(irListener *ir.HTTPListener) (*hcmv3.HttpFilter, error) { + jwtAuthnProto, err := buildJWTAuthn(irListener) + if err != nil { + return nil, err + } + + if err := jwtAuthnProto.ValidateAll(); err != nil { + return nil, err + } + + jwtAuthnAny, err := anypb.New(jwtAuthnProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: jwtAuthn, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: jwtAuthnAny, + }, + }, nil +} + +// buildJWTAuthn returns a JwtAuthentication based on the provided IR HTTPListener. +func buildJWTAuthn(irListener *ir.HTTPListener) (*jwtauthnv3.JwtAuthentication, error) { + jwtProviders := make(map[string]*jwtauthnv3.JwtProvider) + reqMap := make(map[string]*jwtauthnv3.JwtRequirement) + + for _, route := range irListener.Routes { + if route == nil || !routeContainsJWTAuthn(route) { + continue + } + + var reqs []*jwtauthnv3.JwtRequirement + for i := range route.JWT.Providers { + irProvider := route.JWT.Providers[i] + // Create the cluster for the remote jwks, if it doesn't exist. + jwksCluster, err := url2Cluster(irProvider.RemoteJWKS.URI) + if err != nil { + return nil, err + } + + remote := &jwtauthnv3.JwtProvider_RemoteJwks{ + RemoteJwks: &jwtauthnv3.RemoteJwks{ + HttpUri: &corev3.HttpUri{ + Uri: irProvider.RemoteJWKS.URI, + HttpUpstreamType: &corev3.HttpUri_Cluster{ + Cluster: jwksCluster.name, + }, + Timeout: &durationpb.Duration{Seconds: 5}, + }, + CacheDuration: &durationpb.Duration{Seconds: 5 * 60}, + AsyncFetch: &jwtauthnv3.JwksAsyncFetch{}, + RetryPolicy: &corev3.RetryPolicy{}, + }, + } + + claimToHeaders := []*jwtauthnv3.JwtClaimToHeader{} + for _, claimToHeader := range irProvider.ClaimToHeaders { + claimToHeader := &jwtauthnv3.JwtClaimToHeader{ + HeaderName: claimToHeader.Header, + ClaimName: claimToHeader.Claim} + claimToHeaders = append(claimToHeaders, claimToHeader) + } + jwtProvider := &jwtauthnv3.JwtProvider{ + Issuer: irProvider.Issuer, + Audiences: irProvider.Audiences, + JwksSourceSpecifier: remote, + PayloadInMetadata: irProvider.Issuer, + ClaimToHeaders: claimToHeaders, + Forward: true, + } + + if irProvider.ExtractFrom != nil { + jwtProvider.FromCookies = irProvider.ExtractFrom.Cookies + } + + providerKey := fmt.Sprintf("%s/%s", route.Name, irProvider.Name) + jwtProviders[providerKey] = jwtProvider + reqs = append(reqs, &jwtauthnv3.JwtRequirement{ + RequiresType: &jwtauthnv3.JwtRequirement_ProviderName{ + ProviderName: providerKey, + }, + }) + } + if len(reqs) == 1 { + reqMap[route.Name] = reqs[0] + } else { + orListReqs := &jwtauthnv3.JwtRequirement{ + RequiresType: &jwtauthnv3.JwtRequirement_RequiresAny{ + RequiresAny: &jwtauthnv3.JwtRequirementOrList{ + Requirements: reqs, + }, + }, + } + reqMap[route.Name] = orListReqs + } + } + + return &jwtauthnv3.JwtAuthentication{ + RequirementMap: reqMap, + Providers: jwtProviders, + }, nil +} + +// buildXdsUpstreamTLSSocket returns an xDS TransportSocket that uses envoyTrustBundle +// as the CA to authenticate server certificates. +func buildXdsUpstreamTLSSocket() (*corev3.TransportSocket, error) { + tlsCtxProto := &tlsv3.UpstreamTlsContext{ + CommonTlsContext: &tlsv3.CommonTlsContext{ + ValidationContextType: &tlsv3.CommonTlsContext_ValidationContext{ + ValidationContext: &tlsv3.CertificateValidationContext{ + TrustedCa: &corev3.DataSource{ + Specifier: &corev3.DataSource_Filename{ + Filename: envoyTrustBundle, + }, + }, + }, + }, + }, + } + + tlsCtxAny, err := anypb.New(tlsCtxProto) + if err != nil { + return nil, err + } + + return &corev3.TransportSocket{ + Name: wellknown.TransportSocketTls, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: tlsCtxAny, + }, + }, nil +} + +// patchRoute patches the provided route with a JWT PerRouteConfig, if the route +// doesn't contain it. +func (*jwt) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + + filterCfg := route.GetTypedPerFilterConfig() + if _, ok := filterCfg[jwtAuthn]; !ok { + if !routeContainsJWTAuthn(irRoute) { + return nil + } + + routeCfgProto := &jwtauthnv3.PerRouteConfig{ + RequirementSpecifier: &jwtauthnv3.PerRouteConfig_RequirementName{RequirementName: irRoute.Name}} + + routeCfgAny, err := anypb.New(routeCfgProto) + if err != nil { + return err + } + + if filterCfg == nil { + route.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + route.TypedPerFilterConfig[jwtAuthn] = routeCfgAny + } + + return nil +} + +// patchResources creates JWKS clusters from the provided routes, if needed. +func (*jwt) patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { + if tCtx == nil || tCtx.XdsResources == nil { + return errors.New("xds resource table is nil") + } + + var errs error + for _, route := range routes { + if !routeContainsJWTAuthn(route) { + continue + } + + for i := range route.JWT.Providers { + var ( + jwks *urlCluster + ds *ir.DestinationSetting + tSocket *corev3.TransportSocket + err error + ) + + provider := route.JWT.Providers[i] + jwks, err = url2Cluster(provider.RemoteJWKS.URI) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + ds = &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(jwks.hostname, jwks.port)}, + } + + tSocket, err = buildXdsUpstreamTLSSocket() + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + if err = addXdsCluster(tCtx, &xdsClusterArgs{ + name: jwks.name, + settings: []*ir.DestinationSetting{ds}, + tSocket: tSocket, + endpointType: jwks.endpointType, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = multierror.Append(errs, err) + } + } + } + + return errs +} + +// listenerContainsJWTAuthn returns true if JWT authentication exists for the +// provided listener. +func listenerContainsJWTAuthn(irListener *ir.HTTPListener) bool { + if irListener == nil { + return false + } + + for _, route := range irListener.Routes { + if routeContainsJWTAuthn(route) { + return true + } + } + + return false +} + +// routeContainsJWTAuthn returns true if JWT authentication exists for the +// provided route. +func routeContainsJWTAuthn(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.JWT != nil && + irRoute.JWT.Providers != nil && + len(irRoute.JWT.Providers) > 0 { + return true + } + + return false +} + +func (*jwt) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error { + return nil +} diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go index 887436c5e15..d4c5ad06e96 100644 --- a/internal/xds/translator/listener.go +++ b/internal/xds/translator/listener.go @@ -53,11 +53,15 @@ func http2ProtocolOptions() *corev3.Http2ProtocolOptions { } } -func buildXdsTCPListener(name, address string, port uint32, accesslog *ir.AccessLog) *listenerv3.Listener { +// buildXdsTCPListener creates a xds Listener resource +// TODO: Improve function parameters +func buildXdsTCPListener(name, address string, port uint32, keepalive *ir.TCPKeepalive, accesslog *ir.AccessLog) *listenerv3.Listener { + socketOptions := buildTCPSocketOptions(keepalive) al := buildXdsAccessLog(accesslog, true) return &listenerv3.Listener{ Name: name, AccessLog: al, + SocketOptions: socketOptions, PerConnectionBufferLimitBytes: wrapperspb.UInt32(tcpListenerPerConnectionBufferLimitBytes), Address: &corev3.Address{ Address: &corev3.Address_SocketAddress{ @@ -116,6 +120,9 @@ func (t *Translator) addXdsHTTPFilterChain(xdsListener *listenerv3.Listener, irL Tracing: hcmTracing, } + // Add the proxy protocol filter if needed + patchProxyProtocolFilter(xdsListener, irListener) + if irListener.IsHTTP2 { mgr.HttpFilters = append(mgr.HttpFilters, xdsfilters.GRPCWeb) // always enable grpc stats filter @@ -130,17 +137,12 @@ func (t *Translator) addXdsHTTPFilterChain(xdsListener *listenerv3.Listener, irL } } - // TODO: Make this a generic interface for all API Gateway features. - // https://github.com/envoyproxy/gateway/issues/882 - t.patchHCMWithRateLimit(mgr, irListener) - - // Add the jwt authn filter, if needed. - if err := patchHCMWithJwtAuthnFilter(mgr, irListener); err != nil { + // Add HTTP filters to the HCM, the filters have already been sorted in the + // correct order in the patchHCMWithFilters function. + if err := t.patchHCMWithFilters(mgr, irListener); err != nil { return err } - // Make sure the router filter is the last one. - mgr.HttpFilters = append(mgr.HttpFilters, xdsfilters.HTTPRouter) mgrAny, err := protocov.ToAnyWithError(mgr) if err != nil { return err diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go new file mode 100644 index 00000000000..e2cadc9a958 --- /dev/null +++ b/internal/xds/translator/oidc.go @@ -0,0 +1,428 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "crypto/rand" + "errors" + "fmt" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + oauth2v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/oauth2/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "github.com/golang/protobuf/ptypes/duration" + "github.com/tetratelabs/multierror" + "google.golang.org/protobuf/types/known/anypb" + "k8s.io/utils/ptr" + + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/types" +) + +const ( + oauth2Filter = "envoy.filters.http.oauth2" + defaultTokenEndpointTimeout = 10 + redirectURL = "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" + redirectPathMatcher = "/oauth2/callback" + defaultSignoutPath = "/signout" +) + +func init() { + registerHTTPFilter(&oidc{}) +} + +type oidc struct { +} + +var _ httpFilter = &oidc{} + +// patchHCM builds and appends the oauth2 Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an oauth2 filter for each route that contains an OIDC config. +func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + var errs error + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + for _, route := range irListener.Routes { + if !routeContainsOIDC(route) { + continue + } + + filter, err := buildHCMOAuth2Filter(route) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + // skip if the filter already exists + for _, existingFilter := range mgr.HttpFilters { + if filter.Name == existingFilter.Name { + continue + } + } + + mgr.HttpFilters = append(mgr.HttpFilters, filter) + } + + return nil +} + +// buildHCMOAuth2Filter returns an OAuth2 HTTP filter from the provided IR HTTPRoute. +func buildHCMOAuth2Filter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) { + oauth2Proto, err := oauth2Config(route) + if err != nil { + return nil, err + } + + if err := oauth2Proto.ValidateAll(); err != nil { + return nil, err + } + + OAuth2Any, err := anypb.New(oauth2Proto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: oauth2FilterName(route), + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: OAuth2Any, + }, + }, nil +} + +func oauth2FilterName(route *ir.HTTPRoute) string { + return fmt.Sprintf("%s_%s", oauth2Filter, route.Name) +} + +func oauth2Config(route *ir.HTTPRoute) (*oauth2v3.OAuth2, error) { + cluster, err := url2Cluster(route.OIDC.Provider.TokenEndpoint) + if err != nil { + return nil, err + } + if cluster.endpointType == EndpointTypeStatic { + return nil, fmt.Errorf( + "static IP cluster is not allowed: %s", + route.OIDC.Provider.TokenEndpoint) + } + + oauth2 := &oauth2v3.OAuth2{ + Config: &oauth2v3.OAuth2Config{ + TokenEndpoint: &corev3.HttpUri{ + Uri: route.OIDC.Provider.TokenEndpoint, + HttpUpstreamType: &corev3.HttpUri_Cluster{ + Cluster: cluster.name, + }, + Timeout: &duration.Duration{ + Seconds: defaultTokenEndpointTimeout, + }, + }, + AuthorizationEndpoint: route.OIDC.Provider.AuthorizationEndpoint, + RedirectUri: redirectURL, + RedirectPathMatcher: &matcherv3.PathMatcher{ + Rule: &matcherv3.PathMatcher_Path{ + Path: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: redirectPathMatcher, + }, + }, + }, + }, + SignoutPath: &matcherv3.PathMatcher{ + Rule: &matcherv3.PathMatcher_Path{ + Path: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: defaultSignoutPath, + }, + }, + }, + }, + ForwardBearerToken: true, + Credentials: &oauth2v3.OAuth2Credentials{ + ClientId: route.OIDC.ClientID, + TokenSecret: &tlsv3.SdsSecretConfig{ + Name: oauth2ClientSecretName(route), + SdsConfig: makeConfigSource(), + }, + TokenFormation: &oauth2v3.OAuth2Credentials_HmacSecret{ + HmacSecret: &tlsv3.SdsSecretConfig{ + Name: oauth2HMACSecretName(route), + SdsConfig: makeConfigSource(), + }, + }, + }, + // every OIDC provider supports basic auth + AuthType: oauth2v3.OAuth2Config_BASIC_AUTH, + AuthScopes: route.OIDC.Scopes, + }, + } + return oauth2, nil +} + +// routeContainsOIDC returns true if OIDC exists for the provided route. +func routeContainsOIDC(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.OIDC != nil { + return true + } + + return false +} + +func (*oidc) patchResources(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute) error { + if err := createOAuth2TokenEndpointClusters(tCtx, routes); err != nil { + return err + } + if err := createOAuth2Secrets(tCtx, routes); err != nil { + return err + } + return nil +} + +// createOAuth2TokenEndpointClusters creates token endpoint clusters from the +// provided routes, if needed. +func createOAuth2TokenEndpointClusters(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute) error { + if tCtx == nil || tCtx.XdsResources == nil { + return errors.New("xds resource table is nil") + } + + var errs error + for _, route := range routes { + if !routeContainsOIDC(route) { + continue + } + + var ( + cluster *urlCluster + ds *ir.DestinationSetting + tSocket *corev3.TransportSocket + err error + ) + + cluster, err = url2Cluster(route.OIDC.Provider.TokenEndpoint) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + // EG does not support static IP clusters for token endpoint clusters. + // This validation could be removed since it's already validated in the + // Gateway API translator. + if cluster.endpointType == EndpointTypeStatic { + errs = multierror.Append(errs, fmt.Errorf( + "static IP cluster is not allowed: %s", + route.OIDC.Provider.TokenEndpoint)) + continue + } + + tlsContext := &tlsv3.UpstreamTlsContext{ + Sni: cluster.hostname, + } + + tlsContextAny, err := anypb.New(tlsContext) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + tSocket = &corev3.TransportSocket{ + Name: "envoy.transport_sockets.tls", + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: tlsContextAny, + }, + } + + ds = &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint( + cluster.hostname, + cluster.port), + }, + } + + if err = addXdsCluster(tCtx, &xdsClusterArgs{ + name: cluster.name, + settings: []*ir.DestinationSetting{ds}, + tSocket: tSocket, + endpointType: cluster.endpointType, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = multierror.Append(errs, err) + } + } + + return errs +} + +// createOAuth2Secrets creates OAuth2 client and HMAC secrets from the provided +// routes, if needed. +func createOAuth2Secrets(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { + var errs error + + for _, route := range routes { + if !routeContainsOIDC(route) { + continue + } + + // a separate secret is created for each route, even they share the same + // oauth2 client ID and secret. + clientSecret := buildOAuth2ClientSecret(route) + if err := addXdsSecret(tCtx, clientSecret); err != nil { + errs = multierror.Append(errs, err) + } + + hmacSecret, err := buildOAuth2HMACSecret(route) + if err != nil { + errs = multierror.Append(errs, err) + } + if err := addXdsSecret(tCtx, hmacSecret); err != nil { + errs = multierror.Append(errs, err) + } + } + + return errs +} + +func buildOAuth2ClientSecret(route *ir.HTTPRoute) *tlsv3.Secret { + clientSecret := &tlsv3.Secret{ + Name: oauth2ClientSecretName(route), + Type: &tlsv3.Secret_GenericSecret{ + GenericSecret: &tlsv3.GenericSecret{ + Secret: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: route.OIDC.ClientSecret, + }, + }, + }, + }, + } + + return clientSecret +} + +func buildOAuth2HMACSecret(route *ir.HTTPRoute) (*tlsv3.Secret, error) { + hmac, err := generateHMACSecretKey() + if err != nil { + return nil, fmt.Errorf("failed to generate hmack secret key: %w", err) + } + hmacSecret := &tlsv3.Secret{ + Name: oauth2HMACSecretName(route), + Type: &tlsv3.Secret_GenericSecret{ + GenericSecret: &tlsv3.GenericSecret{ + Secret: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: hmac, + }, + }, + }, + }, + } + + return hmacSecret, nil +} + +func oauth2ClientSecretName(route *ir.HTTPRoute) string { + return fmt.Sprintf("%s/oauth2/client_secret", route.Name) +} + +func oauth2HMACSecretName(route *ir.HTTPRoute) string { + return fmt.Sprintf("%s/oauth2/hmac_secret", route.Name) +} + +func generateHMACSecretKey() ([]byte, error) { + // Set the desired length of the secret key in bytes + keyLength := 32 + + // Create a byte slice to hold the random bytes + key := make([]byte, keyLength) + + // Read random bytes from the cryptographically secure random number generator + _, err := rand.Read(key) + if err != nil { + return nil, err + } + + return key, nil +} + +// patchRouteCfg patches the provided route configuration with the oauth2 filter +// if applicable. +// Note: this method disables all the oauth2 filters by default. The filter will +// be enabled per-route in the typePerFilterConfig of the route. +func (*oidc) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error { + if routeCfg == nil { + return errors.New("route configuration is nil") + } + if irListener == nil { + return errors.New("ir listener is nil") + } + + var errs error + for _, route := range irListener.Routes { + if !routeContainsOIDC(route) { + continue + } + + filterName := oauth2FilterName(route) + filterCfg := routeCfg.TypedPerFilterConfig + + if _, ok := filterCfg[filterName]; ok { + // This should not happen since this is the only place where the oauth2 + // filter is added in a route. + errs = multierror.Append(errs, fmt.Errorf( + "route config already contains oauth2 config: %+v", route)) + continue + } + + // Disable all the filters by default. The filter will be enabled + // per-route in the typePerFilterConfig of the route. + routeCfgAny, err := anypb.New(&routev3.FilterConfig{Disabled: true}) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + if filterCfg == nil { + routeCfg.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + routeCfg.TypedPerFilterConfig[filterName] = routeCfgAny + } + return errs +} + +// patchRoute patches the provided route with the oauth2 config if applicable. +// Note: this method enables the corresponding oauth2 filter for the provided route. +func (*oidc) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.OIDC == nil { + return nil + } + + if err := enableFilterOnRoute(oauth2Filter, route, irRoute); err != nil { + return err + } + return nil +} diff --git a/internal/xds/translator/proxy_protocol.go b/internal/xds/translator/proxy_protocol.go new file mode 100644 index 00000000000..9c0dcb2bd16 --- /dev/null +++ b/internal/xds/translator/proxy_protocol.go @@ -0,0 +1,54 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + proxyprotocolv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/envoyproxy/gateway/internal/ir" +) + +// patchProxyProtocolFilter builds and appends the Proxy Protocol Filter to the +// HTTP Listener's Listener Filters if applicable. +func patchProxyProtocolFilter(xdsListener *listenerv3.Listener, irListener *ir.HTTPListener) { + // Return early if unset + if xdsListener == nil || irListener == nil || !irListener.EnableProxyProtocol { + return + } + + // Return early if filter already exists. + for _, filter := range xdsListener.ListenerFilters { + if filter.Name == wellknown.ProxyProtocol { + return + } + } + + proxyProtocolFilter := buildProxyProtocolFilter() + + if proxyProtocolFilter != nil { + xdsListener.ListenerFilters = append(xdsListener.ListenerFilters, proxyProtocolFilter) + } +} + +// buildProxypProtocolFilter returns a Proxy Protocol listener filter from the provided IR listener. +func buildProxyProtocolFilter() *listenerv3.ListenerFilter { + pp := &proxyprotocolv3.ProxyProtocol{} + + ppAny, err := anypb.New(pp) + if err != nil { + return nil + } + + return &listenerv3.ListenerFilter{ + Name: wellknown.ProxyProtocol, + ConfigType: &listenerv3.ListenerFilter_TypedConfig{ + TypedConfig: ppAny, + }, + } +} diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go index 1ce773aab79..55e4a09d2b5 100644 --- a/internal/xds/translator/ratelimit.go +++ b/internal/xds/translator/ratelimit.go @@ -7,6 +7,7 @@ package translator import ( "bytes" + "errors" "net/url" "strconv" "strings" @@ -21,8 +22,10 @@ import ( rlsconfv3 "github.com/envoyproxy/go-control-plane/ratelimit/config/ratelimit/v3" "github.com/envoyproxy/ratelimit/src/config" "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" goyaml "gopkg.in/yaml.v3" // nolint: depguard + "k8s.io/utils/ptr" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/xds/types" @@ -64,7 +67,7 @@ func (t *Translator) patchHCMWithRateLimit(mgr *hcmv3.HttpConnectionManager, irL } } - rateLimitFilter := buildRateLimitFilter(irListener) + rateLimitFilter := t.buildRateLimitFilter(irListener) // Make sure the router filter is the terminal filter in the chain. mgr.HttpFilters = append([]*hcmv3.HttpFilter{rateLimitFilter}, mgr.HttpFilters...) } @@ -84,7 +87,8 @@ func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool { return false } -func buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.HttpFilter { +func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.HttpFilter { + rateLimitFilterProto := &ratelimitfilterv3.RateLimit{ Domain: getRateLimitDomain(irListener), RateLimitService: &ratelimitv3.RateLimitServiceConfig{ @@ -99,6 +103,13 @@ func buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.HttpFilter { }, EnableXRatelimitHeaders: ratelimitfilterv3.RateLimit_XRateLimitHeadersRFCVersion(xRateLimitHeadersRfcVersion), } + if t.GlobalRateLimit.Timeout > 0 { + rateLimitFilterProto.Timeout = durationpb.New(t.GlobalRateLimit.Timeout) + } + + if t.GlobalRateLimit.FailClosed { + rateLimitFilterProto.FailureModeDeny = t.GlobalRateLimit.FailClosed + } rateLimitFilterAny, err := anypb.New(rateLimitFilterProto) if err != nil { @@ -117,7 +128,7 @@ func buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.HttpFilter { // patchRouteWithRateLimit builds rate limit actions and appends to the route. func patchRouteWithRateLimit(xdsRouteAction *routev3.RouteAction, irRoute *ir.HTTPRoute) error { //nolint:unparam // Return early if no rate limit config exists. - if irRoute.RateLimit == nil || irRoute.RateLimit.Global == nil { + if irRoute.RateLimit == nil || irRoute.RateLimit.Global == nil || xdsRouteAction == nil { return nil } @@ -417,24 +428,28 @@ func (t *Translator) createRateLimitServiceCluster(tCtx *types.ResourceVersionTa return nil } clusterName := getRateLimitServiceClusterName() - if rlCluster := findXdsCluster(tCtx, clusterName); rlCluster == nil { - // Create cluster if it does not exist - host, port := t.getRateLimitServiceGrpcHostPort() - routeDestinations := []*ir.RouteDestination{ir.NewRouteDest(host, uint32(port))} - - tSocket, err := buildRateLimitTLSocket() - if err != nil { - return err - } + // Create cluster if it does not exist + host, port := t.getRateLimitServiceGrpcHostPort() + ds := &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Protocol: ir.GRPC, + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(host, uint32(port))}, + } - addXdsCluster(tCtx, addXdsClusterArgs{ - name: clusterName, - destinations: routeDestinations, - tSocket: tSocket, - protocol: HTTP2, - endpoint: DefaultEndpointType, - }) + tSocket, err := buildRateLimitTLSocket() + if err != nil { + return err } + + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: clusterName, + settings: []*ir.DestinationSetting{ds}, + tSocket: tSocket, + endpointType: EndpointTypeDNS, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + return nil } diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go index 492d17cc3e8..b5dd1d61a12 100644 --- a/internal/xds/translator/route.go +++ b/internal/xds/translator/route.go @@ -6,19 +6,18 @@ package translator import ( - "fmt" "strings" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/envoyproxy/gateway/internal/ir" ) -func buildXdsRoute(httpRoute *ir.HTTPRoute, listener *listenerv3.Listener) *routev3.Route { +func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) { router := &routev3.Route{ Name: httpRoute.Name, Match: buildXdsRouteMatch(httpRoute.PathMatch, httpRoute.HeaderMatches, httpRoute.QueryParamMatches), @@ -44,9 +43,9 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute, listener *listenerv3.Listener) *rout case httpRoute.Redirect != nil: router.Action = &routev3.Route_Redirect{Redirect: buildXdsRedirectAction(httpRoute.Redirect)} case httpRoute.URLRewrite != nil: - routeAction := buildXdsURLRewriteAction(httpRoute.Name, httpRoute.URLRewrite) - if len(httpRoute.Mirrors) > 0 { - routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Name, httpRoute.Mirrors) + routeAction := buildXdsURLRewriteAction(httpRoute.Destination.Name, httpRoute.URLRewrite, httpRoute.PathMatch) + if httpRoute.Mirrors != nil { + routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Mirrors) } router.Action = &routev3.Route_Route{Route: routeAction} @@ -54,31 +53,35 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute, listener *listenerv3.Listener) *rout if httpRoute.BackendWeights.Invalid != 0 { // If there are invalid backends then a weighted cluster is required for the route routeAction := buildXdsWeightedRouteAction(httpRoute) - if len(httpRoute.Mirrors) > 0 { - routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Name, httpRoute.Mirrors) + if httpRoute.Mirrors != nil { + routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Mirrors) } router.Action = &routev3.Route_Route{Route: routeAction} } else { - routeAction := buildXdsRouteAction(httpRoute.Name) - if len(httpRoute.Mirrors) > 0 { - routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Name, httpRoute.Mirrors) + routeAction := buildXdsRouteAction(httpRoute.Destination.Name) + if httpRoute.Mirrors != nil { + routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Mirrors) } router.Action = &routev3.Route_Route{Route: routeAction} } } - // TODO: Convert this into a generic interface for API Gateway features. - // https://github.com/envoyproxy/gateway/issues/882 - if err := patchRouteWithRateLimit(router.GetRoute(), httpRoute); err != nil { - return nil + // Hash Policy + if router.GetRoute() != nil { + router.GetRoute().HashPolicy = buildHashPolicy(httpRoute) } - // Add the jwt per route config to the route, if needed. - if err := patchRouteWithJwtConfig(router, httpRoute, listener); err != nil { - return nil + // Timeouts + if router.GetRoute() != nil && httpRoute.Timeout != nil { + router.GetRoute().Timeout = durationpb.New(httpRoute.Timeout.Duration) + } + + // Add per route filter configs to the route, if needed. + if err := patchRouteWithPerRouteConfig(router, httpRoute); err != nil { + return nil, err } - return router + return router, nil } func buildXdsRouteMatch(pathMatch *ir.StringMatch, headerMatches []*ir.StringMatch, queryParamMatches []*ir.StringMatch) *routev3.RouteMatch { @@ -98,14 +101,15 @@ func buildXdsRouteMatch(pathMatch *ir.StringMatch, headerMatches []*ir.StringMat Path: *pathMatch.Exact, } } else if pathMatch.Prefix != nil { - // when the prefix ends with "/", use RouteMatch_Prefix - if strings.HasSuffix(*pathMatch.Prefix, "/") { + if *pathMatch.Prefix == "/" { outMatch.PathSpecifier = &routev3.RouteMatch_Prefix{ - Prefix: *pathMatch.Prefix, + Prefix: "/", } } else { + // Remove trailing / + trimmedPrefix := strings.TrimSuffix(*pathMatch.Prefix, "/") outMatch.PathSpecifier = &routev3.RouteMatch_PathSeparatedPrefix{ - PathSeparatedPrefix: *pathMatch.Prefix, + PathSeparatedPrefix: trimmedPrefix, } } } else if pathMatch.SafeRegex != nil { @@ -180,10 +184,10 @@ func buildXdsStringMatcher(irMatch *ir.StringMatch) *matcherv3.StringMatcher { return stringMatcher } -func buildXdsRouteAction(routeName string) *routev3.RouteAction { +func buildXdsRouteAction(destName string) *routev3.RouteAction { return &routev3.RouteAction{ ClusterSpecifier: &routev3.RouteAction_Cluster{ - Cluster: routeName, + Cluster: destName, }, } } @@ -194,11 +198,16 @@ func buildXdsWeightedRouteAction(httpRoute *ir.HTTPRoute) *routev3.RouteAction { Name: "invalid-backend-cluster", Weight: &wrapperspb.UInt32Value{Value: httpRoute.BackendWeights.Invalid}, }, - { - Name: httpRoute.Name, + } + + if httpRoute.BackendWeights.Valid > 0 { + validCluster := &routev3.WeightedCluster_ClusterWeight{ + Name: httpRoute.Destination.Name, Weight: &wrapperspb.UInt32Value{Value: httpRoute.BackendWeights.Valid}, - }, + } + clusters = append(clusters, validCluster) } + return &routev3.RouteAction{ // Intentionally route to a non-existent cluster and return a 500 error when it is not found ClusterNotFoundResponseCode: routev3.RouteAction_INTERNAL_SERVER_ERROR, @@ -244,10 +253,10 @@ func buildXdsRedirectAction(redirection *ir.Redirect) *routev3.RedirectAction { return routeAction } -func buildXdsURLRewriteAction(routeName string, urlRewrite *ir.URLRewrite) *routev3.RouteAction { +func buildXdsURLRewriteAction(destName string, urlRewrite *ir.URLRewrite, pathMatch *ir.StringMatch) *routev3.RouteAction { routeAction := &routev3.RouteAction{ ClusterSpecifier: &routev3.RouteAction_Cluster{ - Cluster: routeName, + Cluster: destName, }, } @@ -260,7 +269,21 @@ func buildXdsURLRewriteAction(routeName string, urlRewrite *ir.URLRewrite) *rout Substitution: *urlRewrite.Path.FullReplace, } } else if urlRewrite.Path.PrefixMatchReplace != nil { - routeAction.PrefixRewrite = *urlRewrite.Path.PrefixMatchReplace + // Circumvent the case of "//" when the replace string is "/" + // An empty replace string does not seem to solve the issue so we are using + // a regex match and replace instead + // Remove this workaround once https://github.com/envoyproxy/envoy/issues/26055 is fixed + if pathMatch != nil && pathMatch.Prefix != nil && + (*urlRewrite.Path.PrefixMatchReplace == "" || *urlRewrite.Path.PrefixMatchReplace == "/") { + routeAction.RegexRewrite = &matcherv3.RegexMatchAndSubstitute{ + Pattern: &matcherv3.RegexMatcher{ + Regex: "^" + *pathMatch.Prefix + `\/*`, + }, + Substitution: "/", + } + } else { + routeAction.PrefixRewrite = *urlRewrite.Path.PrefixMatchReplace + } } } @@ -289,12 +312,12 @@ func buildXdsDirectResponseAction(res *ir.DirectResponse) *routev3.DirectRespons return routeAction } -func buildXdsRequestMirrorPolicies(routeName string, mirrors []*ir.RouteDestination) []*routev3.RouteAction_RequestMirrorPolicy { - mirrorPolicies := []*routev3.RouteAction_RequestMirrorPolicy{} +func buildXdsRequestMirrorPolicies(mirrorDestinations []*ir.RouteDestination) []*routev3.RouteAction_RequestMirrorPolicy { + var mirrorPolicies []*routev3.RouteAction_RequestMirrorPolicy - for i := range mirrors { + for _, mirrorDest := range mirrorDestinations { mirrorPolicies = append(mirrorPolicies, &routev3.RouteAction_RequestMirrorPolicy{ - Cluster: fmt.Sprintf("%s-mirror-%d", routeName, i), + Cluster: mirrorDest.Name, }) } @@ -321,3 +344,23 @@ func buildXdsAddedHeaders(headersToAdd []ir.AddHeader) []*corev3.HeaderValueOpti return headerValueOptions } + +func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy { + // Return early + if httpRoute == nil || httpRoute.LoadBalancer == nil || httpRoute.LoadBalancer.ConsistentHash == nil { + return nil + } + + if httpRoute.LoadBalancer.ConsistentHash.SourceIP != nil && *httpRoute.LoadBalancer.ConsistentHash.SourceIP { + hashPolicy := &routev3.RouteAction_HashPolicy{ + PolicySpecifier: &routev3.RouteAction_HashPolicy_ConnectionProperties_{ + ConnectionProperties: &routev3.RouteAction_HashPolicy_ConnectionProperties{ + SourceIp: true, + }, + }, + } + return []*routev3.RouteAction_HashPolicy{hashPolicy} + } + + return nil +} diff --git a/internal/xds/translator/runner/runner.go b/internal/xds/translator/runner/runner.go index defbc7fc516..bae2a9fe939 100644 --- a/internal/xds/translator/runner/runner.go +++ b/internal/xds/translator/runner/runner.go @@ -10,7 +10,7 @@ import ( ktypes "k8s.io/apimachinery/pkg/types" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" extension "github.com/envoyproxy/gateway/internal/extension/types" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" @@ -21,10 +21,10 @@ import ( type Config struct { config.Server - XdsIR *message.XdsIR - Xds *message.Xds - EnvoyPatchPolicyStatuses *message.EnvoyPatchPolicyStatuses - ExtensionManager extension.Manager + XdsIR *message.XdsIR + Xds *message.Xds + ExtensionManager extension.Manager + ProviderResources *message.ProviderResources } type Runner struct { @@ -40,17 +40,17 @@ func (r *Runner) Name() string { } // Start starts the xds-translator runner -func (r *Runner) Start(ctx context.Context) error { +func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) go r.subscribeAndTranslate(ctx) r.Logger.Info("started") - return nil + return } func (r *Runner) subscribeAndTranslate(ctx context.Context) { // Subscribe to resources - message.HandleSubscription(r.XdsIR.Subscribe(ctx), - func(update message.Update[string, *ir.Xds]) { + message.HandleSubscription(message.Metadata{Runner: string(v1alpha1.LogComponentXdsTranslatorRunner), Message: "xds-ir"}, r.XdsIR.Subscribe(ctx), + func(update message.Update[string, *ir.Xds], errChan chan error) { r.Logger.Info("received an update") key := update.Key val := update.Value @@ -70,10 +70,25 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { if r.EnvoyGateway.RateLimit != nil { t.GlobalRateLimit = &translator.GlobalRateLimitSettings{ ServiceURL: ratelimit.GetServiceURL(r.Namespace, r.DNSDomain), + FailClosed: r.EnvoyGateway.RateLimit.FailClosed, + } + if r.EnvoyGateway.RateLimit.Timeout != nil { + t.GlobalRateLimit.Timeout = r.EnvoyGateway.RateLimit.Timeout.Duration } } result, err := t.Translate(val) + if err != nil { + r.Logger.Error(err, "failed to translate xds ir") + errChan <- err + } + + // xDS translation is done in a best-effort manner, so the result + // may contain partial resources even if there are errors. + if result == nil { + r.Logger.Info("no xds resources to publish") + return + } // Publish EnvoyPatchPolicyStatus for _, e := range result.EnvoyPatchPolicyStatuses { @@ -81,17 +96,13 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { Name: e.Name, Namespace: e.Namespace, } - r.EnvoyPatchPolicyStatuses.Store(key, e.Status) + r.ProviderResources.EnvoyPatchPolicyStatuses.Store(key, e.Status) } // Discard the EnvoyPatchPolicyStatuses to reduce memory footprint result.EnvoyPatchPolicyStatuses = nil - if err != nil { - r.Logger.Error(err, "failed to translate xds ir") - } else { - // Publish - r.Xds.Store(key, result) - } + // Publish + r.Xds.Store(key, result) } }, ) diff --git a/internal/xds/translator/runner/runner_test.go b/internal/xds/translator/runner/runner_test.go index b6dc54f3670..ab8a2c65e78 100644 --- a/internal/xds/translator/runner/runner_test.go +++ b/internal/xds/translator/runner/runner_test.go @@ -7,13 +7,18 @@ package runner import ( "context" + "fmt" "testing" "time" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/stretchr/testify/require" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/extension/types" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/message" ) @@ -53,10 +58,17 @@ func TestRunner(t *testing.T) { PathMatch: &ir.StringMatch{ Exact: &path, }, - Destinations: []*ir.RouteDestination{ - { - Host: "10.11.12.13", - Port: 8080, + Destination: &ir.RouteDestination{ + Name: "test-dest", + Settings: []*ir.DestinationSetting{ + { + Endpoints: []*ir.DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, }, }, }, @@ -86,3 +98,86 @@ func TestRunner(t *testing.T) { }, time.Second*5, time.Millisecond*50) } + +func TestRunner_withExtensionManager(t *testing.T) { + // Setup + xdsIR := new(message.XdsIR) + xds := new(message.Xds) + cfg, err := config.New() + require.NoError(t, err) + r := New(&Config{ + Server: *cfg, + XdsIR: xdsIR, + Xds: xds, + ExtensionManager: &extManagerMock{}, + }) + + ctx := context.Background() + // Start + err = r.Start(ctx) + require.NoError(t, err) + + // xDS is nil at start + require.Equal(t, map[string]*ir.Xds{}, xdsIR.LoadAll()) + + // test translation + path := "example" + res := ir.Xds{ + HTTP: []*ir.HTTPListener{ + { + Name: "test", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*ir.HTTPRoute{ + { + Name: "test-route", + PathMatch: &ir.StringMatch{ + Exact: &path, + }, + Destination: &ir.RouteDestination{ + Name: "test-dest", + Settings: []*ir.DestinationSetting{ + { + Endpoints: []*ir.DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + xdsIR.Store("test", &res) + require.Eventually(t, func() bool { + out := xds.LoadAll() + // xDS translation is done in a best-effort manner, so event the extension + // manager returns an error, the xDS resources should still be created. + return len(out) == 1 + }, time.Second*5, time.Millisecond*50) +} + +type extManagerMock struct { + types.Manager +} + +func (m *extManagerMock) GetPostXDSHookClient(xdsHookType v1alpha1.XDSTranslatorHook) types.XDSHookClient { + if xdsHookType == v1alpha1.XDSHTTPListener { + return &xdsHookClientMock{} + } + + return nil +} + +type xdsHookClientMock struct { + types.XDSHookClient +} + +func (c *xdsHookClientMock) PostHTTPListenerModifyHook(*listenerv3.Listener) (*listenerv3.Listener, error) { + return nil, fmt.Errorf("assuming a network error during the call") +} diff --git a/internal/xds/translator/tcpkeepalive.go b/internal/xds/translator/tcpkeepalive.go new file mode 100644 index 00000000000..9d4c71d6d30 --- /dev/null +++ b/internal/xds/translator/tcpkeepalive.go @@ -0,0 +1,66 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + + "github.com/envoyproxy/gateway/internal/ir" +) + +// buildTCPSocketOptions converts listener downstream settings to xds socketOptions +func buildTCPSocketOptions(keepAlive *ir.TCPKeepalive) []*corev3.SocketOption { + if keepAlive == nil { + return nil + } + + socketOptions := make([]*corev3.SocketOption, 0) + // Enable Keep Alives + socketOption := &corev3.SocketOption{ + Description: "socket option to enable tcp keep alive", + Level: 0x1, // syscall.SOL_SOCKET has a different value for Darwin, resulting in `go test` failing + Name: 0x9, // syscall.SO_KEEPALIVE has a different value for Darwin, resulting in `go test` failing + Value: &corev3.SocketOption_IntValue{IntValue: 1}, // Enable + } + + socketOptions = append(socketOptions, socketOption) + + if keepAlive.Probes != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive probes", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x6, // Darwin lacks syscall.TCP_KEEPCNT, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.Probes)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + if keepAlive.IdleTime != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive idle time", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x4, // Darwin lacks syscall.TCP_KEEPIDLE, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.IdleTime)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + if keepAlive.Interval != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive interval", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x5, // Darwin lacks syscall.TCP_KEEPINTVL, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.Interval)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + return socketOptions + +} diff --git a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-filter.yaml b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-filter.yaml index bd922ee7caa..0164a4ddee4 100644 --- a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-filter.yaml +++ b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-filter.yaml @@ -6,11 +6,15 @@ http: - "*" routes: - name: "first-route" + hostname: "*" pathMatch: prefix: "/" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 extensionRefs: - object: apiVersion: foo.example.io/v1alpha1 diff --git a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-listener-error.yaml b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-listener-error.yaml index bce1731dbb8..22b31a0ad8b 100644 --- a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-listener-error.yaml +++ b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-listener-error.yaml @@ -6,8 +6,12 @@ http: - "*" routes: - name: "first-route" + hostname: "*" pathMatch: prefix: "/" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-route-error.yaml b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-route-error.yaml index cc4589b7d8c..7db70007a9e 100644 --- a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-route-error.yaml +++ b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-route-error.yaml @@ -6,11 +6,15 @@ http: - "*" routes: - name: "extension-post-xdsroute-hook-error" + hostname: "*" pathMatch: prefix: "/" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "extension-post-xdsroute-hook-error-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 extensionRefs: - object: apiVersion: foo.example.io/v1alpha1 diff --git a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-virtualhost-error.yaml b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-virtualhost-error.yaml index ab84477fb3e..2da86a336dc 100644 --- a/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-virtualhost-error.yaml +++ b/internal/xds/translator/testdata/in/extension-xds-ir/http-route-extension-virtualhost-error.yaml @@ -6,8 +6,12 @@ http: - "*" routes: - name: "first-route" + hostname: "*" pathMatch: prefix: "/" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/extension-xds-ir/http-route.yaml b/internal/xds/translator/testdata/in/extension-xds-ir/http-route.yaml index 449f0571f36..43aec9d1ea7 100644 --- a/internal/xds/translator/testdata/in/extension-xds-ir/http-route.yaml +++ b/internal/xds/translator/testdata/in/extension-xds-ir/http-route.yaml @@ -6,6 +6,7 @@ http: - "*" routes: - name: "first-route" + hostname: "*" headerMatches: - name: user stringMatch: @@ -16,6 +17,9 @@ http: queryParamMatches: - name: "debug" exact: "yes" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml index 461405e3132..c742dfc7387 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml @@ -16,6 +16,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml index 126423fac1f..a024113cb6b 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-remote-address-match.yaml @@ -18,6 +18,9 @@ routes: distinct: true pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml index de53be9737f..49f625800d5 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml @@ -13,6 +13,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml index 0cfe65b6224..34883f1d33b 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/masked-remote-address-match.yaml @@ -17,6 +17,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml index 14ff04aad25..cd65dde7274 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml @@ -18,6 +18,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml index 761e936ac7b..dff823cfcc5 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml @@ -28,6 +28,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml index 9cfe4156caf..3c807f4d466 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml @@ -22,6 +22,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml index 018b78a2190..d45401e5c74 100644 --- a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml +++ b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml @@ -16,6 +16,9 @@ routes: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml new file mode 100644 index 00000000000..53883a39a2f --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml @@ -0,0 +1,40 @@ +name: "accesslog" +accesslog: + text: + - path: "/dev/stdout" + format: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + json: + - path: "/dev/stdout" + json: + start_time: "%START_TIME%" + method: "%REQ(:METHOD)%" + path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" + protocol: "%PROTOCOL%" + response_code: "%RESPONSE_CODE%" + openTelemetry: + - text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + attributes: + "response_code": "%RESPONSE_CODE%" + resources: + "cluster_name": "cluster1" + host: "" + port: 4317 +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "direct-route" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml index 3fe85c2f389..4aa0ac3b216 100644 --- a/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml @@ -29,9 +29,13 @@ http: - "*" routes: - name: "direct-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 directResponse: body: "Unknown custom filter type: UnsupportedType" statusCode: 500 diff --git a/internal/xds/translator/testdata/in/xds-ir/authn-multi-route-multi-provider.yaml b/internal/xds/translator/testdata/in/xds-ir/authn-multi-route-multi-provider.yaml deleted file mode 100644 index 9a993f7a403..00000000000 --- a/internal/xds/translator/testdata/in/xds-ir/authn-multi-route-multi-provider.yaml +++ /dev/null @@ -1,62 +0,0 @@ -http: -- name: "first-listener" - address: "0.0.0.0" - port: 10080 - hostnames: - - "*" - routes: - - name: "first-route-www.test.com" - pathMatch: - exact: "foo/bar" - requestAuthentication: - jwt: - providers: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJWKS: - uri: https://localhost/jwt/public-key/jwks.json - claimToHeaders: - - header: one-route-example-key1 - claim: claim.neteased.key - - name: example2 - issuer: https://www.two.example.com - audiences: - - one.foo.com - - two.foo.com - remoteJWKS: - uri: https://192.168.1.250:8080/jwt/public-key/jwks.json - claimToHeaders: - - header: one-route-example2-key1 - claim: claim.neteased.key - - header: one-route-example2-key2 - claim: name - destinations: - - host: "1.2.3.4" - port: 50000 - - name: "second-route-www.test.com" - pathMatch: - exact: "foo/baz" - requestAuthentication: - jwt: - providers: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJWKS: - uri: https://localhost/jwt/public-key/jwks.json - claimToHeaders: - - header: second-route-example-key1 - claim: claim.neteased.key - - name: example2 - issuer: https://www.two.example.com - audiences: - - one.foo.com - - two.foo.com - remoteJWKS: - uri: https://192.168.1.250:8080/jwt/public-key/jwks.json - destinations: - - host: "5.6.7.8" - port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/authn-multi-route-single-provider.yaml b/internal/xds/translator/testdata/in/xds-ir/authn-multi-route-single-provider.yaml deleted file mode 100644 index 498006bfc09..00000000000 --- a/internal/xds/translator/testdata/in/xds-ir/authn-multi-route-single-provider.yaml +++ /dev/null @@ -1,43 +0,0 @@ -accesslog: - text: - - path: "/dev/stdout" -http: -- name: "first-listener" - address: "0.0.0.0" - port: 10080 - hostnames: - - "*" - routes: - - name: "first-route" - pathMatch: - exact: "foo/bar" - requestAuthentication: - jwt: - providers: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJWKS: - uri: https://localhost/jwt/public-key/jwks.json - claimToHeaders: - - header: first-route-key - claim: claim.neteased.key - destinations: - - host: "1.2.3.4" - port: 50000 - - name: "second-route" - pathMatch: - exact: "foo/baz" - requestAuthentication: - jwt: - providers: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJWKS: - uri: https://localhost/jwt/public-key/jwks.json - destinations: - - host: "5.6.7.8" - port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/authn-ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/authn-ratelimit.yaml deleted file mode 100644 index 9317567dd90..00000000000 --- a/internal/xds/translator/testdata/in/xds-ir/authn-ratelimit.yaml +++ /dev/null @@ -1,58 +0,0 @@ -http: -- name: "first-listener" - address: "0.0.0.0" - port: 10080 - hostnames: - - "*" - routes: - - name: "first-route" - rateLimit: - global: - rules: - - headerMatches: - - name: "x-user-id" - exact: "one" - limit: - requests: 5 - unit: second - pathMatch: - exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 - requestAuthentication: - jwt: - providers: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJWKS: - uri: https://192.168.1.250/jwt/public-key/jwks.json - - name: "second-route" - rateLimit: - global: - rules: - - headerMatches: - - name: "x-user-id" - distinct: true - limit: - requests: 5 - unit: second - pathMatch: - exact: "example" - destinations: - - host: "1.2.3.4" - port: 50000 - - name: "third-route" - rateLimit: - global: - rules: - - limit: - requests: 5 - unit: second - pathMatch: - exact: "test" - destinations: - - host: "1.2.3.4" - port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/authn-single-route-single-match.yaml b/internal/xds/translator/testdata/in/xds-ir/authn-single-route-single-match.yaml deleted file mode 100644 index 4b2bcac6b16..00000000000 --- a/internal/xds/translator/testdata/in/xds-ir/authn-single-route-single-match.yaml +++ /dev/null @@ -1,22 +0,0 @@ -http: -- name: "first-listener" - address: "0.0.0.0" - port: 10080 - hostnames: - - "*" - routes: - - name: "first-route" - pathMatch: - exact: "foo/bar" - requestAuthentication: - jwt: - providers: - - name: example - issuer: https://www.example.com - audiences: - - foo.com - remoteJWKS: - uri: https://localhost/jwt/public-key/jwks.json - destinations: - - host: "1.2.3.4" - port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/basic-auth.yaml b/internal/xds/translator/testdata/in/xds-ir/basic-auth.yaml new file mode 100644 index 00000000000..2e05dcb6c4c --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/basic-auth.yaml @@ -0,0 +1,19 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + basicAuth: + users: "dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=" diff --git a/internal/xds/translator/testdata/in/xds-ir/cors.yaml b/internal/xds/translator/testdata/in/xds-ir/cors.yaml new file mode 100644 index 00000000000..d7af9c71192 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/cors.yaml @@ -0,0 +1,36 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + cors: + allowOrigins: + - name: example.com + stringMatch: + safeRegex: "*.example.com" + - name: foo.bar.com + stringMatch: + exact: foo.bar.com + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" + allowCredentials: true + maxAge: 1000s diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-direct-response.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-direct-response.yaml index 12ff0b778e6..cb7af714fa7 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-direct-response.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-direct-response.yaml @@ -7,9 +7,13 @@ http: - "*" routes: - name: "direct-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 directResponse: body: "Unknown custom filter type: UnsupportedType" statusCode: 500 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-dns-cluster.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-dns-cluster.yaml new file mode 100644 index 00000000000..990d883f8d8 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-dns-cluster.yaml @@ -0,0 +1,23 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + prefix: / + queryParamMatches: + - exact: "yes" + name: debug + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "foo.bar" + port: 50000 + - host: "bar.foo" + port: 50001 + addressType: FQDN diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-invalid.yaml new file mode 100644 index 00000000000..9d16c7298fe --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-invalid.yaml @@ -0,0 +1,25 @@ +http: +- name: "first-listener" + address: "" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-mirror.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-mirror.yaml index 936b690bf05..ca7558a6cd9 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-mirror.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-mirror.yaml @@ -7,9 +7,15 @@ http: - "*" routes: - name: "mirror-route" - destinations: - - host: "1.2.3.4" - port: 50000 - mirrors: - - host: "2.3.4.5" - port: 60000 + hostname: "*" + destination: + name: "route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + mirror: + name: "mirror-route-dest" + settings: + - endpoints: + - host: "2.3.4.5" diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-multiple-matches.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-multiple-matches.yaml new file mode 100644 index 00000000000..a92c856faff --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-multiple-matches.yaml @@ -0,0 +1,83 @@ +http: +- name: first-listener + address: 0.0.0.0 + port: 10080 + hostnames: + - "*" + routes: + - destination: + name: "first-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: example.com + name: envoy-gateway/httproute-2/rule/0/match/0/example.com + pathMatch: + prefix: /v1/example + queryParamMatches: + - exact: "yes" + name: debug + - destination: + name: "second-route-dest" + settings: + - endpoints: + - host: 8.8.8.8 + port: 8080 + hostname: example.com + name: envoy-gateway/httproute-3/rule/0/match/0/example.com + pathMatch: + prefix: /v1/example + - destination: + name: "third-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + headerMatches: + - exact: one + name: version + hostname: example.net + name: envoy-gateway/httproute-4/rule/0/match/0/example.net + pathMatch: + prefix: /v1/status + - destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: 8.8.8.8 + port: 8080 + hostname: example.net + name: envoy-gateway/httproute-5/rule/0/match/0/example.net + pathMatch: + prefix: /v1/status + - destination: + name: "fifth-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: '*.com' + name: envoy-gateway/httproute-1/rule/0/match/0/*.com + pathMatch: + prefix: /foo + - destination: + name: "sixth-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: '*.net' + name: envoy-gateway/httproute-1/rule/0/match/0/*.net + pathMatch: + prefix: /foo + - destination: + name: "seventh-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: "*" + name: envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + prefix: / diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-multiple-mirrors.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-multiple-mirrors.yaml new file mode 100644 index 00000000000..7eaabf815f1 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-multiple-mirrors.yaml @@ -0,0 +1,25 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "mirror-route" + hostname: "*" + destination: + name: "route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + mirrors: + - name: "mirror-route-dest" + settings: + - endpoints: + - host: "2.3.4.5" + - name: "mirror-route-dest1" + settings: + - endpoints: + - host: "3.4.5.6" diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-partial-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-partial-invalid.yaml new file mode 100644 index 00000000000..887408511b7 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-partial-invalid.yaml @@ -0,0 +1,43 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "valid-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "valid-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "invalid-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "invalid-route-dest" + settings: + - endpoints: + - host: "" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-redirect.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-redirect.yaml index 96e87f7c621..9081d4fc22a 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-redirect.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-redirect.yaml @@ -7,9 +7,13 @@ http: - "*" routes: - name: "redirect-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "redirect-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 redirect: scheme: https statusCode: 302 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-regex.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-regex.yaml index f82f747d3f4..8eb85fa1fb6 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-regex.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-regex.yaml @@ -6,6 +6,7 @@ http: - "*" routes: - name: "regex-route" + hostname: "*" pathMatch: safeRegex: "/v1/.*" headerMatches: @@ -16,6 +17,9 @@ http: - name: re_query stringMatch: safeRegex: ".*" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "regex-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-request-headers.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-request-headers.yaml index a75f97fa9b9..9f2bf5c5419 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-request-headers.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-request-headers.yaml @@ -7,9 +7,13 @@ http: - "*" routes: - name: "request-header-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "request-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 addRequestHeaders: - name: "some-header" value: "some-value" diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml index 3cafd43593c..b6bc34b5af5 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml @@ -7,9 +7,13 @@ http: - "*" routes: - name: "response-header-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "response-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 addResponseHeaders: - name: "some-header" value: "some-value" diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml index 01b888594ff..67d9d868723 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml @@ -7,9 +7,13 @@ http: - "*" routes: - name: "response-header-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "response-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 addResponseHeaders: - name: "some-header" value: "some-value" diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml index f9fc78e32f3..10222ef07df 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml @@ -7,9 +7,13 @@ http: - "*" routes: - name: "response-header-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "response-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 removeResponseHeaders: - "some-header5" - "some-header6" diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml index 32474645692..893674ba08e 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml @@ -9,12 +9,16 @@ http: - name: "rewrite-route" pathMatch: prefix: "/origin/" + hostname: gateway.envoyproxy.io headerMatches: - name: ":authority" exact: gateway.envoyproxy.io - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "rewrite-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 urlRewrite: path: prefixMatchReplace: / diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml index 21a05fcbefd..b47fd370d01 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml @@ -9,12 +9,13 @@ http: - name: "rewrite-route" pathMatch: prefix: "/origin" - headerMatches: - - name: ":authority" - exact: gateway.envoyproxy.io - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: gateway.envoyproxy.io + destination: + name: "rewrite-route" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 urlRewrite: path: fullReplace: /rewrite diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml index 1c6af60e7fd..49818d6c849 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml @@ -9,12 +9,16 @@ http: - name: "rewrite-route" pathMatch: prefix: "/origin" + hostname: gateway.envoyproxy.io headerMatches: - name: ":authority" exact: gateway.envoyproxy.io - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "rewrite-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 urlRewrite: hostname: "3.3.3.3" path: diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml index 8ce2437be26..c39ea68d167 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml @@ -9,12 +9,16 @@ http: - name: "rewrite-route" pathMatch: prefix: "/origin" + hostname: gateway.envoyproxy.io headerMatches: - name: ":authority" exact: gateway.envoyproxy.io - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "rewrite-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 urlRewrite: path: prefixMatchReplace: /rewrite diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml new file mode 100644 index 00000000000..49392abb0e3 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml @@ -0,0 +1,20 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + timeout: 5s + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml index ed038fb0874..38227bc91ab 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml @@ -6,16 +6,23 @@ http: - "*" routes: - name: "first-route" - destinations: - - host: "1.1.1.1" - port: 50001 - weight: 20 - - host: "2.2.2.2" - port: 50002 - weight: 40 - - host: "3.3.3.3" - port: 50003 - weight: 20 - - host: "4.4.4.4" - port: 50004 - weight: 20 + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.1.1.1" + port: 50001 + weight: 20 + - endpoints: + - host: "2.2.2.2" + port: 50002 + weight: 40 + - endpoints: + - host: "3.3.3.3" + port: 50003 + weight: 20 + - endpoints: + - host: "4.4.4.4" + port: 50004 + weight: 20 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml index fde9aa76907..5fa0bc33bb2 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml @@ -6,9 +6,13 @@ http: - "*" routes: - name: "first-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 backendWeights: invalid: 1 valid: 1 diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route.yaml index 449f0571f36..43aec9d1ea7 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route.yaml @@ -6,6 +6,7 @@ http: - "*" routes: - name: "first-route" + hostname: "*" headerMatches: - name: user stringMatch: @@ -16,6 +17,9 @@ http: queryParamMatches: - name: "debug" exact: "yes" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/http2-route.yaml b/internal/xds/translator/testdata/in/xds-ir/http2-route.yaml index ff2282adb59..2a61570f0ec 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http2-route.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http2-route.yaml @@ -7,6 +7,7 @@ http: isHTTP2: true routes: - name: "first-route" + hostname: "*" pathMatch: name: "test" exact: "foo/bar" @@ -17,6 +18,10 @@ http: queryParamMatches: - name: "debug" exact: "yes" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + protocol: GRPC diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-listener.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-listener.yaml new file mode 100644 index 00000000000..166edbee6b8 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-listener.yaml @@ -0,0 +1,71 @@ +jsonPatches: +- type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "first-listener" + operation: + op: "add" + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eg-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 +- type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + name: "first-listener" + operation: + op: "add" + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} +- type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 +- type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" + name: "first-route" + operation: + op: "replace" + path: "/endpoints/0/load_balancing_weight" + value: "50" +http: +- name: "first-listener" + address: "" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml index 10bc99336bd..1cb5d100915 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml @@ -28,10 +28,14 @@ http: - "*" routes: - name: "first-route" + hostname: "*" headerMatches: - name: user stringMatch: exact: "jason" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid.yaml new file mode 100644 index 00000000000..4c04333d85a --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid.yaml @@ -0,0 +1,75 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "first-listener" + operation: + op: "add" + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eg-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + name: "first-listener" + operation: + op: "add" + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 + - type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" + name: "first-route" + operation: + op: "replace" + path: "/endpoints/0/load_balancing_weight" + value: "50" +http: +- name: "first-listener" + address: "" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml index 8e96f03ee48..2ae43310e76 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml @@ -28,10 +28,14 @@ http: - "*" routes: - name: "first-route" + hostname: "*" headerMatches: - name: user stringMatch: exact: "jason" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml index 4ce37b015bc..3e09512c6ed 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml @@ -49,7 +49,7 @@ envoyPatchPolicies: address: ratelimit.svc.cluster.local port_value: 8081 - type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" - name: "first-route" + name: "first-route-dest" operation: op: "replace" path: "/endpoints/0/load_balancing_weight" @@ -62,10 +62,14 @@ http: - "*" routes: - name: "first-route" + hostname: "*" headerMatches: - name: user stringMatch: exact: "jason" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-custom-extractor.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-custom-extractor.yaml new file mode 100644 index 00000000000..f8a56289c65 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-custom-extractor.yaml @@ -0,0 +1,28 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + extractFrom: + cookies: + - session_access_token + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-multi-route-multi-provider.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-multi-route-multi-provider.yaml new file mode 100644 index 00000000000..6abee5f0575 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-multi-route-multi-provider.yaml @@ -0,0 +1,68 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route-www.test.com" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key1 + claim: claim.neteased.key + - name: example2 + issuer: https://www.two.example.com + audiences: + - one.foo.com + - two.foo.com + remoteJWKS: + uri: https://192.168.1.250:8080/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example2-key1 + claim: claim.neteased.key + - header: one-route-example2-key2 + claim: name + destination: + name: "first-route-www.test.com-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route-www.test.com" + hostname: "*" + pathMatch: + exact: "foo/baz" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + claimToHeaders: + - header: second-route-example-key1 + claim: claim.neteased.key + - name: example2 + issuer: https://www.two.example.com + audiences: + - one.foo.com + - two.foo.com + remoteJWKS: + uri: https://192.168.1.250:8080/jwt/public-key/jwks.json + destination: + name: "second-route-www.test.com-dest" + settings: + - endpoints: + - host: "5.6.7.8" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-multi-route-single-provider.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-multi-route-single-provider.yaml new file mode 100644 index 00000000000..d9699c052f9 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-multi-route-single-provider.yaml @@ -0,0 +1,49 @@ +accesslog: + text: + - path: "/dev/stdout" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + claimToHeaders: + - header: first-route-key + claim: claim.neteased.key + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + pathMatch: + exact: "foo/baz" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "5.6.7.8" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml new file mode 100644 index 00000000000..f4b24fe3525 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-ratelimit.yaml @@ -0,0 +1,69 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://192.168.1.250/jwt/public-key/jwks.json + - name: "second-route" + hostname: "*" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + distinct: true + limit: + requests: 5 + unit: second + pathMatch: + exact: "example" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + rateLimit: + global: + rules: + - limit: + requests: 5 + unit: second + pathMatch: + exact: "test" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-single-route-single-match.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-single-route-single-match.yaml new file mode 100644 index 00000000000..cb7b0a20946 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-single-route-single-match.yaml @@ -0,0 +1,25 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/listener-proxy-protocol.yaml b/internal/xds/translator/testdata/in/xds-ir/listener-proxy-protocol.yaml new file mode 100644 index 00000000000..02fedd52603 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/listener-proxy-protocol.yaml @@ -0,0 +1,25 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + tls: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + enableProxyProtocol: true + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml b/internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml new file mode 100644 index 00000000000..35905935a44 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml @@ -0,0 +1,60 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + tcpKeepalive: {} + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "second-listener" + address: "0.0.0.0" + port: 10081 + hostnames: + - "foo.net" + tcpKeepalive: + probes: 7 + interval: 200 + idleTime: 50 + routes: + - name: "second-route" + hostname: "*" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +tcp: +- name: "third-listener" + address: "0.0.0.0" + port: 10082 + tcpKeepalive: {} + tls: + passthrough: + snis: + - bar.com + destination: + name: "tls-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "fourth-listener" + address: "0.0.0.0" + tcpKeepalive: + probes: 10 + port: 10083 + destination: + name: "tcp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml b/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml new file mode 100644 index 00000000000..91ba5b18971 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/load-balancer.yaml @@ -0,0 +1,72 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + loadBalancer: + roundRobin: {} + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + loadBalancer: + random: {} + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + loadBalancer: + leastRequest: {} + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fourth-route" + hostname: "*" + loadBalancer: + consistentHash: + sourceIP: true + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fifth-route" + hostname: "*" + loadBalancer: + leastRequest: + slowStart: + window: 60s + destination: + name: "fifth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "sixth-route" + hostname: "*" + loadBalancer: + roundRobin: + slowStart: + window: 300s + destination: + name: "sixth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/metrics-virtual-host.yaml b/internal/xds/translator/testdata/in/xds-ir/metrics-virtual-host.yaml new file mode 100644 index 00000000000..b1441eee3e0 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/metrics-virtual-host.yaml @@ -0,0 +1,18 @@ +name: "metrics" +metrics: + enableVirtualHostStats: true +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/mixed-tls-jwt-authn.yaml b/internal/xds/translator/testdata/in/xds-ir/mixed-tls-jwt-authn.yaml new file mode 100644 index 00000000000..158f3240e22 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/mixed-tls-jwt-authn.yaml @@ -0,0 +1,32 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + tls: + - name: first-listener + # byte slice representation of "cert-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + requestAuthentication: + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml b/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml index 320dccaacf3..40af29afaed 100644 --- a/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml @@ -12,9 +12,13 @@ http: privateKey: [107, 101, 121, 45, 100, 97, 116, 97] routes: - name: "first-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "second-listener" address: "0.0.0.0" port: 10080 @@ -28,9 +32,13 @@ http: privateKey: [107, 101, 121, 45, 100, 97, 116, 97] routes: - name: "second-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "third-listener" address: "0.0.0.0" port: 10080 @@ -38,9 +46,13 @@ http: - "example.com" routes: - name: "third-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "fourth-listener" address: "0.0.0.0" port: 10080 @@ -48,9 +60,13 @@ http: - "example.net" routes: - name: "fourth-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 tcp: - name: "fifth-listener" address: "0.0.0.0" @@ -59,9 +75,12 @@ tcp: passthrough: snis: - bar.com - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "tcp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "sixth-listener" address: "0.0.0.0" port: 10080 @@ -69,6 +88,9 @@ tcp: passthrough: snis: - bar.net - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "tls-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml b/internal/xds/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml index 324de212838..fca8012cd10 100644 --- a/internal/xds/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml @@ -2,40 +2,55 @@ tcp: - name: "tcp-route-simple" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-simple-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 - name: "tcp-route-simple-1" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-simple-1-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 - name: "tcp-route-simple-2" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-simple-2-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 - name: "tcp-route-simple-3" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-simple-3-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 - name: "tcp-route-simple-4" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-simple-4-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/oidc.yaml b/internal/xds/translator/testdata/in/xds-ir/oidc.yaml new file mode 100644 index 00000000000..efbd8b9ea69 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/oidc.yaml @@ -0,0 +1,67 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + oidc: + clientID: client.oauth.foo.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + tokenEndpoint: https://oauth.foo.com/token + scopes: + - openid + - email + - profile + - name: "second-route" + hostname: "*" + pathMatch: + exact: "bar" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + oidc: + clientID: client.oauth.bar.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + tokenEndpoint: https://oauth.bar.com/token + scopes: + - openid + - email + - profile + - name: "third-route" + hostname: "*" + pathMatch: + exact: "test" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + oidc: + clientID: test.oauth.bar.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + tokenEndpoint: https://oauth.bar.com/token + scopes: + - openid + - email + - profile diff --git a/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml b/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml new file mode 100644 index 00000000000..cfe35f7fd0b --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml @@ -0,0 +1,17 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + proxyProtocol: + version: "V2" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml index c9c17210220..9a0671ad4ad 100644 --- a/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-custom-domain.yaml @@ -6,6 +6,7 @@ http: - "*" routes: - name: "first-route" + hostname: "*" rateLimit: global: rules: @@ -17,10 +18,14 @@ http: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "second-route" + hostname: "*" rateLimit: global: rules: @@ -32,10 +37,14 @@ http: unit: second pathMatch: exact: "example" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "third-route" + hostname: "*" rateLimit: global: rules: @@ -44,6 +53,9 @@ http: unit: second pathMatch: exact: "test" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml index ef7602d291d..7acb4a7cd0d 100644 --- a/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-sourceip.yaml @@ -6,6 +6,7 @@ http: - "*" routes: - name: "first-route" + hostname: "*" rateLimit: global: rules: @@ -17,10 +18,14 @@ http: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "second-route" + hostname: "*" rateLimit: global: rules: @@ -32,10 +37,14 @@ http: unit: second pathMatch: exact: "example" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "third-route" + hostname: "*" rateLimit: global: rules: @@ -44,10 +53,14 @@ http: unit: second pathMatch: exact: "test" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "fourth-route" + hostname: "*" rateLimit: global: rules: @@ -60,6 +73,9 @@ http: unit: second pathMatch: exact: "distinct" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml index c9c17210220..9a0671ad4ad 100644 --- a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml @@ -6,6 +6,7 @@ http: - "*" routes: - name: "first-route" + hostname: "*" rateLimit: global: rules: @@ -17,10 +18,14 @@ http: unit: second pathMatch: exact: "foo/bar" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "second-route" + hostname: "*" rateLimit: global: rules: @@ -32,10 +37,14 @@ http: unit: second pathMatch: exact: "example" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 - name: "third-route" + hostname: "*" rateLimit: global: rules: @@ -44,6 +53,9 @@ http: unit: second pathMatch: exact: "test" - destinations: - - host: "1.2.3.4" - port: 50000 + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/simple-tls.yaml b/internal/xds/translator/testdata/in/xds-ir/simple-tls.yaml index 38915b9046b..140a2b41c28 100644 --- a/internal/xds/translator/testdata/in/xds-ir/simple-tls.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/simple-tls.yaml @@ -15,6 +15,10 @@ http: privateKey: [107, 101, 121, 45, 100, 97, 116, 97] routes: - name: "first-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml index f4f0afdecad..05ac886693e 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-complex.yaml @@ -8,8 +8,11 @@ tcp: - foo.com - bar.com - example.com - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-complex-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-invalid-endpoint.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-invalid-endpoint.yaml new file mode 100644 index 00000000000..3885afa4fd9 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-invalid-endpoint.yaml @@ -0,0 +1,11 @@ +tcp: +- name: "tcp-route-simple" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-dest" + settings: + - endpoints: + - port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-invalid.yaml new file mode 100644 index 00000000000..d14b325d3dc --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-invalid.yaml @@ -0,0 +1,12 @@ +tcp: +- name: "tcp-route-invalid" + address: "" + port: 10080 + destination: + name: "tcp-route-invalid-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-simple.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-simple.yaml index b88d57dd399..f79cebf3e67 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tcp-route-simple.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-simple.yaml @@ -2,8 +2,11 @@ tcp: - name: "tcp-route-simple" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tcp-route-simple-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml index 281b171dc87..bcd02eb8712 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml @@ -7,8 +7,11 @@ tcp: - Name: envoy-gateway-tls-secret-1 PrivateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K ServerCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tls-terminate-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml b/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml index f983764b3f9..0bd5ac621a9 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml @@ -8,16 +8,22 @@ tcp: - foo.com - bar.com - example.com - destinations: - - host: "1.1.1.1" - port: 50001 - weight: 20 - - host: "2.2.2.2" - port: 50002 - weight: 40 - - host: "3.3.3.3" - port: 50003 - weight: 20 - - host: "4.4.4.4" - port: 50004 - weight: 20 + destination: + name: "tcp-route-weighted-backend-dest" + settings: + - endpoints: + - host: "1.1.1.1" + port: 50001 + weight: 20 + - endpoints: + - host: "2.2.2.2" + port: 50002 + weight: 40 + - endpoints: + - host: "3.3.3.3" + port: 50003 + weight: 20 + - endpoints: + - host: "4.4.4.4" + port: 50004 + weight: 20 diff --git a/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml b/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml index 40b409f59f4..1d33d64cc91 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tls-route-passthrough.yaml @@ -6,8 +6,11 @@ tcp: passthrough: snis: - foo.com - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "tls-passthrough-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/tracing-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/tracing-invalid.yaml new file mode 100644 index 00000000000..f56127bb6d7 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/tracing-invalid.yaml @@ -0,0 +1,40 @@ +name: "tracing" +tracing: + serviceName: "fake-name.fake-ns" + samplingRate: 90 + customTags: + "literal1": + type: Literal + literal: + value: "value1" + "env1": + type: Environment + environment: + name: "env1" + defaultValue: "-" + "req1": + type: RequestHeader + requestHeader: + name: "X-Request-Id" + defaultValue: "-" + provider: + host: "" + port: 4317 +http: + - name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/internal/xds/translator/testdata/in/xds-ir/tracing.yaml b/internal/xds/translator/testdata/in/xds-ir/tracing.yaml index d2cc017f9ce..1961a714477 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tracing.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tracing.yaml @@ -28,9 +28,13 @@ http: - "*" routes: - name: "direct-route" - destinations: - - host: "1.2.3.4" - port: 50000 + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 directResponse: body: "Unknown custom filter type: UnsupportedType" statusCode: 500 diff --git a/internal/xds/translator/testdata/in/xds-ir/udp-route-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/udp-route-invalid.yaml new file mode 100644 index 00000000000..39ab4e7f403 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/udp-route-invalid.yaml @@ -0,0 +1,10 @@ +udp: +- name: "udp-route" + port: 10080 + destination: + name: "udp-reoute-dest" + settings: + - endpoints: + - host: "" + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/in/xds-ir/udp-route.yaml b/internal/xds/translator/testdata/in/xds-ir/udp-route.yaml index 490b4aa2121..fa4ffafc138 100644 --- a/internal/xds/translator/testdata/in/xds-ir/udp-route.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/udp-route.yaml @@ -2,8 +2,11 @@ udp: - name: "udp-route" address: "0.0.0.0" port: 10080 - destinations: - - host: "1.2.3.4" - port: 50000 - - host: "5.6.7.8" - port: 50001 + destination: + name: "udp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.clusters.yaml b/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.clusters.yaml index db48ea8b154..fc88cebd59b 100644 --- a/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.clusters.yaml +++ b/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.clusters.yaml @@ -1,13 +1,14 @@ - commonLbConfig: localityWeightedLbConfig: {} - connectTimeout: 30s + connectTimeout: 10s dnsLookupFamily: V4_ONLY edsClusterConfig: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.endpoints.yaml b/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.endpoints.yaml index f5f137f9374..3b3f2d09076 100644 --- a/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.endpoints.yaml +++ b/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.routes.yaml b/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.routes.yaml index d0fe0090810..243339164de 100644 --- a/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.routes.yaml +++ b/internal/xds/translator/testdata/out/extension-xds-ir/http-route-extension-filter.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: extension-listener + name: extension-listener/* routes: - match: prefix: / @@ -28,7 +28,4 @@ key: mock-extension-was-here-extensionRef-apiversion value: foo.example.io/v1alpha1 route: - cluster: first-route - - directResponse: - status: 200 - name: mock-extension-inserted-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/extension-xds-ir/http-route.clusters.yaml b/internal/xds/translator/testdata/out/extension-xds-ir/http-route.clusters.yaml index db48ea8b154..fc88cebd59b 100644 --- a/internal/xds/translator/testdata/out/extension-xds-ir/http-route.clusters.yaml +++ b/internal/xds/translator/testdata/out/extension-xds-ir/http-route.clusters.yaml @@ -1,13 +1,14 @@ - commonLbConfig: localityWeightedLbConfig: {} - connectTimeout: 30s + connectTimeout: 10s dnsLookupFamily: V4_ONLY edsClusterConfig: edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/extension-xds-ir/http-route.endpoints.yaml b/internal/xds/translator/testdata/out/extension-xds-ir/http-route.endpoints.yaml index f5f137f9374..3b3f2d09076 100644 --- a/internal/xds/translator/testdata/out/extension-xds-ir/http-route.endpoints.yaml +++ b/internal/xds/translator/testdata/out/extension-xds-ir/http-route.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/extension-xds-ir/http-route.routes.yaml b/internal/xds/translator/testdata/out/extension-xds-ir/http-route.routes.yaml index 757bdd9e3e0..738eb0a2d2a 100644 --- a/internal/xds/translator/testdata/out/extension-xds-ir/http-route.routes.yaml +++ b/internal/xds/translator/testdata/out/extension-xds-ir/http-route.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: headers: @@ -20,4 +20,4 @@ exact: "yes" name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml index dc03cc47978..1ba3e1d7080 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: direct-route - name: direct-route + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -16,6 +17,7 @@ connectTimeout: 10s dnsLookupFamily: V4_ONLY dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST loadAssignment: clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 endpoints: @@ -25,8 +27,10 @@ socketAddress: address: otel-collector.default.svc.cluster.local portValue: 4317 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: accesslog|otel-collector.default.svc.cluster.local|4317/backend/0 name: accesslog|otel-collector.default.svc.cluster.local|4317 outlierDetection: {} perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog.endpoints.yaml index cddb5bf893b..20c80b3aaaa 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: direct-route +- clusterName: direct-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: direct-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog.routes.yaml index 72e9dfd4264..d4a7fa5ae20 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - directResponse: body: diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.clusters.yaml deleted file mode 100644 index 72f0104afea..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.clusters.yaml +++ /dev/null @@ -1,76 +0,0 @@ -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: first-route-www.test.com - name: first-route-www.test.com - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: second-route-www.test.com - name: second-route-www.test.com - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: localhost_443 - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: localhost - portValue: 443 - loadBalancingWeight: 1 - locality: {} - name: localhost_443 - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: STRICT_DNS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: "192_168_1_250_8080" - name: "192_168_1_250_8080" - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.endpoints.yaml deleted file mode 100644 index 228889274a9..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.endpoints.yaml +++ /dev/null @@ -1,30 +0,0 @@ -- clusterName: first-route-www.test.com - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 1.2.3.4 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} -- clusterName: second-route-www.test.com - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 5.6.7.8 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} -- clusterName: "192_168_1_250_8080" - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 192.168.1.250 - portValue: 8080 - loadBalancingWeight: 1 - locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.listeners.yaml deleted file mode 100644 index c0b91ba47c2..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.listeners.yaml +++ /dev/null @@ -1,105 +0,0 @@ -- address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.jwt_authn - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication - providers: - first-route-www.test.com-example: - audiences: - - foo.com - claimToHeaders: - - claimName: claim.neteased.key - headerName: one-route-example-key1 - issuer: https://www.example.com - payloadInMetadata: https://www.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: localhost_443 - timeout: 5s - uri: https://localhost/jwt/public-key/jwks.json - first-route-www.test.com-example2: - audiences: - - one.foo.com - - two.foo.com - claimToHeaders: - - claimName: claim.neteased.key - headerName: one-route-example2-key1 - - claimName: name - headerName: one-route-example2-key2 - issuer: https://www.two.example.com - payloadInMetadata: https://www.two.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: "192_168_1_250_8080" - timeout: 5s - uri: https://192.168.1.250:8080/jwt/public-key/jwks.json - second-route-www.test.com-example: - audiences: - - foo.com - claimToHeaders: - - claimName: claim.neteased.key - headerName: second-route-example-key1 - issuer: https://www.example.com - payloadInMetadata: https://www.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: localhost_443 - timeout: 5s - uri: https://localhost/jwt/public-key/jwks.json - second-route-www.test.com-example2: - audiences: - - one.foo.com - - two.foo.com - issuer: https://www.two.example.com - payloadInMetadata: https://www.two.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: "192_168_1_250_8080" - timeout: 5s - uri: https://192.168.1.250:8080/jwt/public-key/jwks.json - requirementMap: - first-route-www.test.com: - requiresAny: - requirements: - - providerName: first-route-www.test.com-example - - providerName: first-route-www.test.com-example2 - second-route-www.test.com: - requiresAny: - requirements: - - providerName: second-route-www.test.com-example - - providerName: second-route-www.test.com-example2 - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: first-listener - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: first-listener - perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.routes.yaml deleted file mode 100644 index d07629c32e2..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-multi-provider.routes.yaml +++ /dev/null @@ -1,25 +0,0 @@ -- ignorePortInHostMatching: true - name: first-listener - virtualHosts: - - domains: - - '*' - name: first-listener - routes: - - match: - path: foo/bar - name: first-route-www.test.com - route: - cluster: first-route-www.test.com - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: first-route-www.test.com - - match: - path: foo/baz - name: second-route-www.test.com - route: - cluster: second-route-www.test.com - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: second-route-www.test.com diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.clusters.yaml deleted file mode 100644 index 602dbfaa290..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.clusters.yaml +++ /dev/null @@ -1,55 +0,0 @@ -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: first-route - name: first-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: second-route - name: second-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: localhost_443 - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: localhost - portValue: 443 - loadBalancingWeight: 1 - locality: {} - name: localhost_443 - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.endpoints.yaml deleted file mode 100644 index 5a1312bf2ea..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.endpoints.yaml +++ /dev/null @@ -1,20 +0,0 @@ -- clusterName: first-route - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 1.2.3.4 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} -- clusterName: second-route - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 5.6.7.8 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.listeners.yaml deleted file mode 100644 index 48d7ded1c7c..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.listeners.yaml +++ /dev/null @@ -1,89 +0,0 @@ -- accessLog: - - filter: - responseFlagFilter: - flags: - - NR - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - accessLog: - - name: envoy.access_loggers.file - typedConfig: - '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - logFormat: - textFormatSource: - inlineString: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - path: /dev/stdout - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.jwt_authn - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication - providers: - first-route-example: - audiences: - - foo.com - claimToHeaders: - - claimName: claim.neteased.key - headerName: first-route-key - issuer: https://www.example.com - payloadInMetadata: https://www.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: localhost_443 - timeout: 5s - uri: https://localhost/jwt/public-key/jwks.json - second-route-example: - audiences: - - foo.com - issuer: https://www.example.com - payloadInMetadata: https://www.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: localhost_443 - timeout: 5s - uri: https://localhost/jwt/public-key/jwks.json - requirementMap: - first-route: - providerName: first-route-example - second-route: - providerName: second-route-example - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: first-listener - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: first-listener - perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.routes.yaml deleted file mode 100644 index b5181aa2110..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-multi-route-single-provider.routes.yaml +++ /dev/null @@ -1,25 +0,0 @@ -- ignorePortInHostMatching: true - name: first-listener - virtualHosts: - - domains: - - '*' - name: first-listener - routes: - - match: - path: foo/bar - name: first-route - route: - cluster: first-route - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: first-route - - match: - path: foo/baz - name: second-route - route: - cluster: second-route - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: second-route diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.clusters.yaml deleted file mode 100644 index 28127e4f122..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.clusters.yaml +++ /dev/null @@ -1,99 +0,0 @@ -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: first-route - name: first-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: second-route - name: second-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: third-route - name: third-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: ratelimit_cluster - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: envoy-ratelimit.envoy-gateway-system.svc.cluster.local - portValue: 8081 - loadBalancingWeight: 1 - locality: {} - name: ratelimit_cluster - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - tlsCertificates: - - certificateChain: - filename: /certs/tls.crt - privateKey: - filename: /certs/tls.key - validationContext: - trustedCa: - filename: /certs/ca.crt - type: STRICT_DNS - typedExtensionProtocolOptions: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicitHttpConfig: - http2ProtocolOptions: {} -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: "192_168_1_250_443" - name: "192_168_1_250_443" - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.endpoints.yaml deleted file mode 100644 index e046ea8f5d6..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.endpoints.yaml +++ /dev/null @@ -1,40 +0,0 @@ -- clusterName: first-route - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 1.2.3.4 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} -- clusterName: second-route - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 1.2.3.4 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} -- clusterName: third-route - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 1.2.3.4 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} -- clusterName: "192_168_1_250_443" - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 192.168.1.250 - portValue: 443 - loadBalancingWeight: 1 - locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.listeners.yaml deleted file mode 100644 index 2802148a13d..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.listeners.yaml +++ /dev/null @@ -1,61 +0,0 @@ -- address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.jwt_authn - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication - providers: - first-route-example: - audiences: - - foo.com - issuer: https://www.example.com - payloadInMetadata: https://www.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: "192_168_1_250_443" - timeout: 5s - uri: https://192.168.1.250/jwt/public-key/jwks.json - requirementMap: - first-route: - providerName: first-route-example - - name: envoy.filters.http.ratelimit - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit - domain: first-listener - enableXRatelimitHeaders: DRAFT_VERSION_03 - rateLimitService: - grpcService: - envoyGrpc: - clusterName: ratelimit_cluster - transportApiVersion: V3 - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: first-listener - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: first-listener - perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.routes.yaml deleted file mode 100644 index a6f302bc217..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-ratelimit.routes.yaml +++ /dev/null @@ -1,46 +0,0 @@ -- ignorePortInHostMatching: true - name: first-listener - virtualHosts: - - domains: - - '*' - name: first-listener - routes: - - match: - path: foo/bar - name: first-route - route: - cluster: first-route - rateLimits: - - actions: - - headerValueMatch: - descriptorKey: first-route-key-rule-0-match-0 - descriptorValue: first-route-value-rule-0-match-0 - expectMatch: true - headers: - - name: x-user-id - stringMatch: - exact: one - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: first-route - - match: - path: example - name: second-route - route: - cluster: second-route - rateLimits: - - actions: - - requestHeaders: - descriptorKey: second-route-key-rule-0-match-0 - headerName: x-user-id - - match: - path: test - name: third-route - route: - cluster: third-route - rateLimits: - - actions: - - genericKey: - descriptorKey: third-route-key-rule-0-match--1 - descriptorValue: third-route-value-rule-0-match--1 diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.clusters.yaml deleted file mode 100644 index daf70e0a2bd..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.clusters.yaml +++ /dev/null @@ -1,42 +0,0 @@ -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: first-route - name: first-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - dnsRefreshRate: 30s - loadAssignment: - clusterName: localhost_443 - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: localhost - portValue: 443 - loadBalancingWeight: 1 - locality: {} - name: localhost_443 - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - respectDnsTtl: true - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.endpoints.yaml deleted file mode 100644 index f5f137f9374..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.endpoints.yaml +++ /dev/null @@ -1,10 +0,0 @@ -- clusterName: first-route - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 1.2.3.4 - portValue: 50000 - loadBalancingWeight: 1 - locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.listeners.yaml deleted file mode 100644 index 0920e5878a1..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.listeners.yaml +++ /dev/null @@ -1,51 +0,0 @@ -- address: - socketAddress: - address: 0.0.0.0 - portValue: 10080 - defaultFilterChain: - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - http2ProtocolOptions: - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.jwt_authn - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication - providers: - first-route-example: - audiences: - - foo.com - issuer: https://www.example.com - payloadInMetadata: https://www.example.com - remoteJwks: - cacheDuration: 300s - httpUri: - cluster: localhost_443 - timeout: 5s - uri: https://localhost/jwt/public-key/jwks.json - requirementMap: - first-route: - providerName: first-route-example - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - resourceApiVersion: V3 - routeConfigName: first-listener - statPrefix: http - upgradeConfigs: - - upgradeType: websocket - useRemoteAddress: true - name: first-listener - perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.routes.yaml deleted file mode 100644 index 7e390ce6f8c..00000000000 --- a/internal/xds/translator/testdata/out/xds-ir/authn-single-route-single-match.routes.yaml +++ /dev/null @@ -1,16 +0,0 @@ -- ignorePortInHostMatching: true - name: first-listener - virtualHosts: - - domains: - - '*' - name: first-listener - routes: - - match: - path: foo/bar - name: first-route - route: - cluster: first-route - typedPerFilterConfig: - envoy.filters.http.jwt_authn: - '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig - requirementName: first-route diff --git a/internal/xds/translator/testdata/out/xds-ir/basic-auth.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/basic-auth.clusters.yaml new file mode 100755 index 00000000000..c8692b81602 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/basic-auth.clusters.yaml @@ -0,0 +1,14 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/basic-auth.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/basic-auth.endpoints.yaml new file mode 100755 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/basic-auth.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml new file mode 100755 index 00000000000..ab0592b760b --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml @@ -0,0 +1,38 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.basic_auth_first-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth + users: + inlineBytes: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo= + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml new file mode 100755 index 00000000000..dbaae69d06a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml @@ -0,0 +1,20 @@ +- ignorePortInHostMatching: true + name: first-listener + typedPerFilterConfig: + envoy.filters.http.basic_auth_first-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + disabled: true + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.basic_auth_first-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/cors.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/cors.clusters.yaml new file mode 100755 index 00000000000..c8692b81602 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/cors.clusters.yaml @@ -0,0 +1,14 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/cors.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/cors.endpoints.yaml new file mode 100755 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/cors.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/cors.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/cors.listeners.yaml new file mode 100755 index 00000000000..c48a6d76424 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/cors.listeners.yaml @@ -0,0 +1,36 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.cors + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/cors.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/cors.routes.yaml new file mode 100755 index 00000000000..fc0fdea378e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/cors.routes.yaml @@ -0,0 +1,24 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.cors: + '@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy + allowCredentials: true + allowHeaders: x-header-1, x-header-2 + allowMethods: GET, POST + allowOriginStringMatch: + - safeRegex: + regex: '*.example.com' + - exact: foo.bar.com + exposeHeaders: x-header-3, x-header-4 + maxAge: "1000" diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml index bbdc92c6f40..af1e50c6320 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: direct-route - name: direct-route + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml index cddb5bf893b..20c80b3aaaa 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: direct-route +- clusterName: direct-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: direct-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml index 72e9dfd4264..d4a7fa5ae20 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - directResponse: body: diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.clusters.yaml new file mode 100644 index 00000000000..4527713b1c2 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.clusters.yaml @@ -0,0 +1,30 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: foo.bar + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: bar.foo + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.endpoints.yaml new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.endpoints.yaml @@ -0,0 +1 @@ +[] diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.listeners.yaml new file mode 100644 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.routes.yaml new file mode 100644 index 00000000000..7b23a59dfee --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-dns-cluster.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: first-route + route: + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml index f02953347db..36dfc7b6165 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml @@ -6,21 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: mirror-route - name: mirror-route - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - type: EDS -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: mirror-route-mirror-0 - name: mirror-route-mirror-0 + serviceName: route-dest + lbPolicy: LEAST_REQUEST + name: route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml index 0b1b4671ef3..43cba2e7979 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: mirror-route +- clusterName: route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,15 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: mirror-route-mirror-0 - endpoints: - - lbEndpoints: - - endpoint: - address: - socketAddress: - address: 2.3.4.5 - portValue: 60000 - loadBalancingWeight: 1 - locality: {} + locality: + region: route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml index 8c75652a21d..c5f6e70b254 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml @@ -3,12 +3,10 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / name: mirror-route route: - cluster: mirror-route - requestMirrorPolicies: - - cluster: mirror-route-mirror-0 + cluster: route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.clusters.yaml new file mode 100755 index 00000000000..daf7072f011 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.clusters.yaml @@ -0,0 +1,98 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fifth-route-dest + lbPolicy: LEAST_REQUEST + name: fifth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: sixth-route-dest + lbPolicy: LEAST_REQUEST + name: sixth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: seventh-route-dest + lbPolicy: LEAST_REQUEST + name: seventh-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.endpoints.yaml new file mode 100755 index 00000000000..3787880a314 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.endpoints.yaml @@ -0,0 +1,84 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 8.8.8.8 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 8.8.8.8 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: fifth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fifth-route-dest/backend/0 +- clusterName: sixth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: sixth-route-dest/backend/0 +- clusterName: seventh-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: seventh-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.listeners.yaml new file mode 100755 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.routes.yaml new file mode 100755 index 00000000000..2db7970b38a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-matches.routes.yaml @@ -0,0 +1,66 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - example.com + name: first-listener/example_com + routes: + - match: + pathSeparatedPrefix: /v1/example + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: envoy-gateway/httproute-2/rule/0/match/0/example.com + route: + cluster: first-route-dest + - match: + pathSeparatedPrefix: /v1/example + name: envoy-gateway/httproute-3/rule/0/match/0/example.com + route: + cluster: second-route-dest + - domains: + - example.net + name: first-listener/example_net + routes: + - match: + headers: + - name: version + stringMatch: + exact: one + pathSeparatedPrefix: /v1/status + name: envoy-gateway/httproute-4/rule/0/match/0/example.net + route: + cluster: third-route-dest + - match: + pathSeparatedPrefix: /v1/status + name: envoy-gateway/httproute-5/rule/0/match/0/example.net + route: + cluster: fourth-route-dest + - domains: + - '*.com' + name: first-listener/*_com + routes: + - match: + pathSeparatedPrefix: /foo + name: envoy-gateway/httproute-1/rule/0/match/0/*.com + route: + cluster: fifth-route-dest + - domains: + - '*.net' + name: first-listener/*_net + routes: + - match: + pathSeparatedPrefix: /foo + name: envoy-gateway/httproute-1/rule/0/match/0/*.net + route: + cluster: sixth-route-dest + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: envoy-gateway/httproute-1/rule/0/match/0/* + route: + cluster: seventh-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.clusters.yaml new file mode 100644 index 00000000000..0296e36dc4e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.clusters.yaml @@ -0,0 +1,42 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: route-dest + lbPolicy: LEAST_REQUEST + name: route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: mirror-route-dest + lbPolicy: LEAST_REQUEST + name: mirror-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: mirror-route-dest1 + lbPolicy: LEAST_REQUEST + name: mirror-route-dest1 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.endpoints.yaml new file mode 100644 index 00000000000..f741af39cd4 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: route-dest/backend/0 +- clusterName: mirror-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 2.3.4.5 + portValue: 0 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: mirror-route-dest/backend/0 +- clusterName: mirror-route-dest1 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 3.4.5.6 + portValue: 0 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: mirror-route-dest1/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.listeners.yaml new file mode 100644 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.routes.yaml new file mode 100644 index 00000000000..1429b4da64e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-multiple-mirrors.routes.yaml @@ -0,0 +1,15 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: mirror-route + route: + cluster: route-dest + requestMirrorPolicies: + - cluster: mirror-route-dest + - cluster: mirror-route-dest1 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.clusters.yaml new file mode 100755 index 00000000000..d8b8e4d21ff --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.clusters.yaml @@ -0,0 +1,14 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: valid-route-dest + lbPolicy: LEAST_REQUEST + name: valid-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.endpoints.yaml new file mode 100755 index 00000000000..4fdb0384f3c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: valid-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: valid-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.listeners.yaml new file mode 100755 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.routes.yaml new file mode 100755 index 00000000000..ac7c41d9846 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-partial-invalid.routes.yaml @@ -0,0 +1,39 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + - name: test + stringMatch: + suffix: end + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: valid-route + route: + cluster: valid-route-dest + - match: + headers: + - name: user + stringMatch: + exact: jason + - name: test + stringMatch: + suffix: end + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: invalid-route + route: + cluster: invalid-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml index a00488cafe9..a19246c6c14 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: redirect-route - name: redirect-route + serviceName: redirect-route-dest + lbPolicy: LEAST_REQUEST + name: redirect-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml index 42c13c716ff..7543b702fb9 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: redirect-route +- clusterName: redirect-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: redirect-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml index 5e280fc6e89..7cac95f1009 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml index 830695dae3c..a21398c504f 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: regex-route - name: regex-route + serviceName: regex-route-dest + lbPolicy: LEAST_REQUEST + name: regex-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml index 6879216e7b4..b36ee450059 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: regex-route +- clusterName: regex-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: regex-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-regex.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-regex.routes.yaml index 886b355e8d1..a3c115b6da9 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-regex.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-regex.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: headers: @@ -20,4 +20,4 @@ regex: /v1/.* name: regex-route route: - cluster: regex-route + cluster: regex-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml index bc31afb46e5..7e253f6b5be 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: request-header-route - name: request-header-route + serviceName: request-header-route-dest + lbPolicy: LEAST_REQUEST + name: request-header-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml index a092af26592..846a89074e1 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: request-header-route +- clusterName: request-header-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: request-header-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml index a6c2a2edb02..2d8f26ab8da 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / @@ -33,4 +33,4 @@ - some-header5 - some-header6 route: - cluster: request-header-route + cluster: request-header-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml index 07cef74ca03..3c6050b2116 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: response-header-route - name: response-header-route + serviceName: response-header-route-dest + lbPolicy: LEAST_REQUEST + name: response-header-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml index 975e532f687..d21491f6216 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: response-header-route +- clusterName: response-header-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: response-header-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml index 4b7584fb5eb..af0333a9bfb 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / @@ -30,4 +30,4 @@ key: empty-header keepEmptyValue: true route: - cluster: response-header-route + cluster: response-header-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml index 07cef74ca03..3c6050b2116 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: response-header-route - name: response-header-route + serviceName: response-header-route-dest + lbPolicy: LEAST_REQUEST + name: response-header-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml index 975e532f687..d21491f6216 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: response-header-route +- clusterName: response-header-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: response-header-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml index 65c3f226fb3..f94d0e9d92d 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / @@ -33,4 +33,4 @@ - some-header5 - some-header6 route: - cluster: response-header-route + cluster: response-header-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml index 07cef74ca03..3c6050b2116 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: response-header-route - name: response-header-route + serviceName: response-header-route-dest + lbPolicy: LEAST_REQUEST + name: response-header-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml index 975e532f687..d21491f6216 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: response-header-route +- clusterName: response-header-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: response-header-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml index 67e1247624d..4052f973e28 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / @@ -12,4 +12,4 @@ - some-header5 - some-header6 route: - cluster: response-header-route + cluster: response-header-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml index dfc0f368100..1a641679539 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: rewrite-route - name: rewrite-route + serviceName: rewrite-route-dest + lbPolicy: LEAST_REQUEST + name: rewrite-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml index 06548bca3f8..256dda09089 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: rewrite-route +- clusterName: rewrite-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: rewrite-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml index 26090b70e82..2bf01099ad2 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml @@ -2,16 +2,19 @@ name: first-listener virtualHosts: - domains: - - '*' - name: first-listener + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io routes: - match: headers: - name: :authority stringMatch: exact: gateway.envoyproxy.io - prefix: /origin/ + pathSeparatedPrefix: /origin name: rewrite-route route: - cluster: rewrite-route - prefixRewrite: / + cluster: rewrite-route-dest + regexRewrite: + pattern: + regex: ^/origin/\/* + substitution: / diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml index dfc0f368100..6aeeab50483 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml @@ -7,6 +7,7 @@ ads: {} resourceApiVersion: V3 serviceName: rewrite-route + lbPolicy: LEAST_REQUEST name: rewrite-route outlierDetection: {} perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml index 06548bca3f8..ca1ef21c989 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: rewrite-route/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml index 4f2765d7108..f1f30bbbffc 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml @@ -2,14 +2,10 @@ name: first-listener virtualHosts: - domains: - - '*' - name: first-listener + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io routes: - match: - headers: - - name: :authority - stringMatch: - exact: gateway.envoyproxy.io pathSeparatedPrefix: /origin name: rewrite-route route: diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml index dfc0f368100..1a641679539 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: rewrite-route - name: rewrite-route + serviceName: rewrite-route-dest + lbPolicy: LEAST_REQUEST + name: rewrite-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml index 06548bca3f8..256dda09089 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: rewrite-route +- clusterName: rewrite-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: rewrite-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml index b29a06ff668..7de20ab0305 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml @@ -2,8 +2,8 @@ name: first-listener virtualHosts: - domains: - - '*' - name: first-listener + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io routes: - match: headers: @@ -14,6 +14,6 @@ name: rewrite-route route: appendXForwardedHost: true - cluster: rewrite-route + cluster: rewrite-route-dest hostRewriteLiteral: 3.3.3.3 prefixRewrite: /rewrite diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml index dfc0f368100..1a641679539 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: rewrite-route - name: rewrite-route + serviceName: rewrite-route-dest + lbPolicy: LEAST_REQUEST + name: rewrite-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml index 06548bca3f8..256dda09089 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: rewrite-route +- clusterName: rewrite-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: rewrite-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml index 1dd8b233d92..a5f47ebab54 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml @@ -2,8 +2,8 @@ name: first-listener virtualHosts: - domains: - - '*' - name: first-listener + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io routes: - match: headers: @@ -13,5 +13,5 @@ pathSeparatedPrefix: /origin name: rewrite-route route: - cluster: rewrite-route + cluster: rewrite-route-dest prefixRewrite: /rewrite diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml new file mode 100644 index 00000000000..c8692b81602 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml @@ -0,0 +1,14 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml new file mode 100644 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.listeners.yaml new file mode 100644 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml new file mode 100644 index 00000000000..52a166770bc --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml @@ -0,0 +1,17 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + timeout: 5s diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml index 2c908f7ef69..c8692b81602 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml index 66fec63b37b..f5633bd158e 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,24 +6,37 @@ socketAddress: address: 1.1.1.1 portValue: 50001 - loadBalancingWeight: 20 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: first-route-dest/backend/0 + - lbEndpoints: - endpoint: address: socketAddress: address: 2.2.2.2 portValue: 50002 - loadBalancingWeight: 40 + loadBalancingWeight: 1 + loadBalancingWeight: 40 + locality: + region: first-route-dest/backend/1 + - lbEndpoints: - endpoint: address: socketAddress: address: 3.3.3.3 portValue: 50003 - loadBalancingWeight: 20 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: first-route-dest/backend/2 + - lbEndpoints: - endpoint: address: socketAddress: address: 4.4.4.4 portValue: 50004 - loadBalancingWeight: 20 - loadBalancingWeight: 1 - locality: {} + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: first-route-dest/backend/3 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml index d7666a2b56a..2734c7cc42a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml @@ -3,10 +3,10 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml index 2c908f7ef69..c8692b81602 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml index f5f137f9374..3b3f2d09076 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml index 28d563ec72c..5b56c04dbae 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / @@ -14,5 +14,5 @@ clusters: - name: invalid-backend-cluster weight: 1 - - name: first-route + - name: first-route-dest weight: 1 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route.clusters.yaml index 2c908f7ef69..c8692b81602 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route.endpoints.yaml index f5f137f9374..3b3f2d09076 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route.routes.yaml index 757bdd9e3e0..738eb0a2d2a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: headers: @@ -20,4 +20,4 @@ exact: "yes" name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/http2-route.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http2-route.clusters.yaml index 761d2265f04..b64f0e3521c 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http2-route.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http2-route.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http2-route.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http2-route.endpoints.yaml index f5f137f9374..3b3f2d09076 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http2-route.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http2-route.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http2-route.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http2-route.routes.yaml index 9954740c9bf..59831c3176d 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http2-route.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http2-route.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: headers: @@ -17,4 +17,4 @@ exact: "yes" name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml index 2c908f7ef69..c8692b81602 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml index f5f137f9374..3b3f2d09076 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml index 47fecd8f845..9ff692421b9 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml @@ -3,7 +3,8 @@ status: conditions: - lastTransitionTime: null - message: unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"never":{"existed":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}} + message: 'unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"never":{"existed":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}}, + err:proto: (line 1:1023): unknown field "this"' reason: Invalid status: "False" type: Programmed diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml index 60009c41c84..dc48be19646 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: headers: @@ -13,4 +13,4 @@ prefix: / name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml index 2c908f7ef69..c8692b81602 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml index f5f137f9374..3b3f2d09076 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml index 60009c41c84..dc48be19646 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: headers: @@ -13,4 +13,4 @@ prefix: / name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml index 5a0733fda8c..b171eb11f71 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml index ae7ea189da8..a9249b45d31 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 50 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.routes.yaml index 0482acadb08..fae73436c2f 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* rateLimits: - actions: - remoteAddress: {} @@ -16,4 +16,4 @@ prefix: / name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.clusters.yaml new file mode 100644 index 00000000000..7c8285ba0f6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.clusters.yaml @@ -0,0 +1,46 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: localhost_443/backend/0 + name: localhost_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.endpoints.yaml new file mode 100644 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.listeners.yaml new file mode 100644 index 00000000000..3bd76da8af0 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.listeners.yaml @@ -0,0 +1,56 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example: + audiences: + - foo.com + forward: true + fromCookies: + - session_access_token + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route: + providerName: first-route/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.routes.yaml new file mode 100644 index 00000000000..c73bec09093 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-custom-extractor.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.clusters.yaml new file mode 100755 index 00000000000..fd188612f27 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.clusters.yaml @@ -0,0 +1,82 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-www.test.com-dest + lbPolicy: LEAST_REQUEST + name: first-route-www.test.com-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-www.test.com-dest + lbPolicy: LEAST_REQUEST + name: second-route-www.test.com-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: localhost_443/backend/0 + name: localhost_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: "192_168_1_250_8080" + lbPolicy: LEAST_REQUEST + name: "192_168_1_250_8080" + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.endpoints.yaml new file mode 100755 index 00000000000..6731cfd3faf --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: first-route-www.test.com-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-www.test.com-dest/backend/0 +- clusterName: second-route-www.test.com-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-www.test.com-dest/backend/0 +- clusterName: "192_168_1_250_8080" + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 192.168.1.250 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: 192_168_1_250_8080/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.listeners.yaml new file mode 100755 index 00000000000..23990992ee7 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.listeners.yaml @@ -0,0 +1,117 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route-www.test.com/example: + audiences: + - foo.com + claimToHeaders: + - claimName: claim.neteased.key + headerName: one-route-example-key1 + forward: true + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + first-route-www.test.com/example2: + audiences: + - one.foo.com + - two.foo.com + claimToHeaders: + - claimName: claim.neteased.key + headerName: one-route-example2-key1 + - claimName: name + headerName: one-route-example2-key2 + forward: true + issuer: https://www.two.example.com + payloadInMetadata: https://www.two.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: "192_168_1_250_8080" + timeout: 5s + uri: https://192.168.1.250:8080/jwt/public-key/jwks.json + retryPolicy: {} + second-route-www.test.com/example: + audiences: + - foo.com + claimToHeaders: + - claimName: claim.neteased.key + headerName: second-route-example-key1 + forward: true + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + second-route-www.test.com/example2: + audiences: + - one.foo.com + - two.foo.com + forward: true + issuer: https://www.two.example.com + payloadInMetadata: https://www.two.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: "192_168_1_250_8080" + timeout: 5s + uri: https://192.168.1.250:8080/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route-www.test.com: + requiresAny: + requirements: + - providerName: first-route-www.test.com/example + - providerName: first-route-www.test.com/example2 + second-route-www.test.com: + requiresAny: + requirements: + - providerName: second-route-www.test.com/example + - providerName: second-route-www.test.com/example2 + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.routes.yaml new file mode 100755 index 00000000000..630157c4b40 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-multi-provider.routes.yaml @@ -0,0 +1,25 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route-www.test.com + route: + cluster: first-route-www.test.com-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route-www.test.com + - match: + path: foo/baz + name: second-route-www.test.com + route: + cluster: second-route-www.test.com-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: second-route-www.test.com diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.clusters.yaml new file mode 100644 index 00000000000..f18c018dd7c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.clusters.yaml @@ -0,0 +1,60 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: localhost_443/backend/0 + name: localhost_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.endpoints.yaml new file mode 100644 index 00000000000..39102da4001 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.endpoints.yaml @@ -0,0 +1,24 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.listeners.yaml new file mode 100644 index 00000000000..bc515a98bb8 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.listeners.yaml @@ -0,0 +1,95 @@ +- accessLog: + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example: + audiences: + - foo.com + claimToHeaders: + - claimName: claim.neteased.key + headerName: first-route-key + forward: true + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + second-route/example: + audiences: + - foo.com + forward: true + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route: + providerName: first-route/example + second-route: + providerName: second-route/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.routes.yaml new file mode 100644 index 00000000000..2078809a694 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-multi-route-single-provider.routes.yaml @@ -0,0 +1,25 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route + - match: + path: foo/baz + name: second-route + route: + cluster: second-route-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: second-route diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml new file mode 100644 index 00000000000..353a2a7f696 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml @@ -0,0 +1,106 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: "192_168_1_250_443" + lbPolicy: LEAST_REQUEST + name: "192_168_1_250_443" + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: ratelimit_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-ratelimit.envoy-gateway-system.svc.cluster.local + portValue: 8081 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: ratelimit_cluster/backend/0 + name: ratelimit_cluster + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificates: + - certificateChain: + filename: /certs/tls.crt + privateKey: + filename: /certs/tls.key + validationContext: + trustedCa: + filename: /certs/ca.crt + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.endpoints.yaml new file mode 100644 index 00000000000..73130b205cb --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.endpoints.yaml @@ -0,0 +1,48 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: "192_168_1_250_443" + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 192.168.1.250 + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: 192_168_1_250_443/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.listeners.yaml new file mode 100644 index 00000000000..797be82e982 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.listeners.yaml @@ -0,0 +1,64 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example: + audiences: + - foo.com + forward: true + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: "192_168_1_250_443" + timeout: 5s + uri: https://192.168.1.250/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route: + providerName: first-route/example + - name: envoy.filters.http.ratelimit + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + domain: first-listener + enableXRatelimitHeaders: DRAFT_VERSION_03 + rateLimitService: + grpcService: + envoyGrpc: + clusterName: ratelimit_cluster + transportApiVersion: V3 + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.routes.yaml new file mode 100644 index 00000000000..0223e989d39 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.routes.yaml @@ -0,0 +1,46 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + rateLimits: + - actions: + - headerValueMatch: + descriptorKey: first-route-key-rule-0-match-0 + descriptorValue: first-route-value-rule-0-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: one + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route + - match: + path: example + name: second-route + route: + cluster: second-route-dest + rateLimits: + - actions: + - requestHeaders: + descriptorKey: second-route-key-rule-0-match-0 + headerName: x-user-id + - match: + path: test + name: third-route + route: + cluster: third-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: third-route-key-rule-0-match--1 + descriptorValue: third-route-value-rule-0-match--1 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.clusters.yaml new file mode 100644 index 00000000000..7c8285ba0f6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.clusters.yaml @@ -0,0 +1,46 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: localhost_443/backend/0 + name: localhost_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.endpoints.yaml new file mode 100644 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.listeners.yaml new file mode 100644 index 00000000000..e268247f475 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.listeners.yaml @@ -0,0 +1,54 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example: + audiences: + - foo.com + forward: true + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route: + providerName: first-route/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.routes.yaml new file mode 100644 index 00000000000..c73bec09093 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-single-route-single-match.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.clusters.yaml new file mode 100644 index 00000000000..c8692b81602 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.clusters.yaml @@ -0,0 +1,14 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.endpoints.yaml new file mode 100644 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml new file mode 100644 index 00000000000..5d4a7fbce0f --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml @@ -0,0 +1,60 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + filterChains: + - filterChainMatch: + serverNames: + - foo.com + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: https + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + listenerFilters: + - name: envoy.filters.listener.proxy_protocol + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.routes.yaml new file mode 100644 index 00000000000..2734c7cc42a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.routes.yaml @@ -0,0 +1,12 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml new file mode 100755 index 00000000000..1bcfa8d1104 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml @@ -0,0 +1,56 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-route-dest + lbPolicy: LEAST_REQUEST + name: tls-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml new file mode 100755 index 00000000000..5b4fe89e58c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml @@ -0,0 +1,48 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: tls-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-route-dest/backend/0 +- clusterName: tcp-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml new file mode 100755 index 00000000000..09334586970 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml @@ -0,0 +1,135 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10081 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: second-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: second-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" + - description: socket option for keep alive probes + intValue: "7" + level: "6" + name: "6" + - description: socket option for keep alive idle time + intValue: "50" + level: "6" + name: "4" + - description: socket option for keep alive interval + intValue: "200" + level: "6" + name: "5" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10082 + filterChains: + - filterChainMatch: + serverNames: + - bar.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-route-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: third-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10083 + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-dest + statPrefix: tcp + name: fourth-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" + - description: socket option for keep alive probes + intValue: "10" + level: "6" + name: "6" diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml new file mode 100755 index 00000000000..d36b9824e31 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml @@ -0,0 +1,24 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest +- ignorePortInHostMatching: true + name: second-listener + virtualHosts: + - domains: + - '*' + name: second-listener/* + routes: + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml new file mode 100644 index 00000000000..60969f05429 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.clusters.yaml @@ -0,0 +1,88 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: RANDOM + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: MAGLEV + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fifth-route-dest + lbPolicy: LEAST_REQUEST + leastRequestLbConfig: + slowStartConfig: + slowStartWindow: 60s + name: fifth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: sixth-route-dest + name: sixth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + roundRobinLbConfig: + slowStartConfig: + slowStartWindow: 300s + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml new file mode 100644 index 00000000000..ee22b49d2b2 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml @@ -0,0 +1,72 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: fifth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fifth-route-dest/backend/0 +- clusterName: sixth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: sixth-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.listeners.yaml new file mode 100644 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml new file mode 100644 index 00000000000..d2a33c64849 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/load-balancer.routes.yaml @@ -0,0 +1,40 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + - match: + prefix: / + name: third-route + route: + cluster: third-route-dest + - match: + prefix: / + name: fourth-route + route: + cluster: fourth-route-dest + hashPolicy: + - connectionProperties: + sourceIp: true + - match: + prefix: / + name: fifth-route + route: + cluster: fifth-route-dest + - match: + prefix: / + name: sixth-route + route: + cluster: sixth-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.clusters.yaml new file mode 100644 index 00000000000..c8692b81602 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.clusters.yaml @@ -0,0 +1,14 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.endpoints.yaml new file mode 100644 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.listeners.yaml new file mode 100644 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.routes.yaml new file mode 100644 index 00000000000..0d2cb8567b8 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/metrics-virtual-host.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + virtualClusters: + - headers: + - name: :authority + stringMatch: + prefix: '*' + name: '*' diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.clusters.yaml new file mode 100755 index 00000000000..7a0c933174e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.clusters.yaml @@ -0,0 +1,45 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} + name: localhost_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.endpoints.yaml new file mode 100755 index 00000000000..0d68b430c20 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.endpoints.yaml @@ -0,0 +1,11 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml new file mode 100755 index 00000000000..096b2e7add4 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml @@ -0,0 +1,66 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example: + audiences: + - foo.com + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route: + providerName: first-route/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: https + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: first-listener + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.routes.yaml new file mode 100755 index 00000000000..c73bec09093 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.secrets.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.secrets.yaml new file mode 100755 index 00000000000..9155e5c882d --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.secrets.yaml @@ -0,0 +1,6 @@ +- name: first-listener + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml index 071336b1d28..6ac76261382 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -19,8 +20,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: second-route - name: second-route + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -32,8 +34,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: third-route - name: third-route + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -45,8 +48,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: fourth-route - name: fourth-route + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -58,8 +62,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: fifth-listener - name: fifth-listener + serviceName: tcp-route-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -71,8 +76,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: sixth-listener - name: sixth-listener + serviceName: tls-route-dest + lbPolicy: LEAST_REQUEST + name: tls-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml index f46fe1ee819..ceed007af6a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,9 +6,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: second-route + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest endpoints: - lbEndpoints: - endpoint: @@ -16,9 +18,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: third-route + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest endpoints: - lbEndpoints: - endpoint: @@ -26,9 +30,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: fourth-route + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest endpoints: - lbEndpoints: - endpoint: @@ -36,9 +42,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: fifth-listener + locality: + region: fourth-route-dest/backend/0 +- clusterName: tcp-route-dest endpoints: - lbEndpoints: - endpoint: @@ -46,9 +54,19 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: sixth-listener + locality: + region: tcp-route-dest/backend/0 +- clusterName: tls-route-dest endpoints: - - loadBalancingWeight: 1 - locality: {} + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml index 0adac0b6757..632271fe590 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml @@ -121,7 +121,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: fifth-listener + cluster: tcp-route-dest statPrefix: passthrough - filterChainMatch: serverNames: @@ -130,7 +130,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: sixth-listener + cluster: tls-route-dest statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml index 14e4380399a..cd8f24c1194 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml @@ -2,44 +2,44 @@ name: first-listener virtualHosts: - domains: - - foo.com - name: first-listener + - '*' + name: first-listener/* routes: - match: prefix: / name: first-route route: - cluster: first-route + cluster: first-route-dest - ignorePortInHostMatching: true name: second-listener virtualHosts: - domains: - - foo.net - name: second-listener + - '*' + name: second-listener/* routes: - match: prefix: / name: second-route route: - cluster: second-route + cluster: second-route-dest - ignorePortInHostMatching: true name: third-listener virtualHosts: - domains: - - example.com - name: third-listener + - '*' + name: third-listener/* routes: - match: prefix: / name: third-route route: - cluster: third-route + cluster: third-route-dest - domains: - - example.net - name: fourth-listener + - '*' + name: fourth-listener/* routes: - match: prefix: / name: fourth-route route: - cluster: fourth-route + cluster: fourth-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml index 25d14e345df..30c51562dd2 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-simple - name: tcp-route-simple + serviceName: tcp-route-simple-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -19,8 +20,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-simple-1 - name: tcp-route-simple-1 + serviceName: tcp-route-simple-1-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-1-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -32,8 +34,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-simple-2 - name: tcp-route-simple-2 + serviceName: tcp-route-simple-2-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-2-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -45,8 +48,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-simple-3 - name: tcp-route-simple-3 + serviceName: tcp-route-simple-3-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-3-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -58,8 +62,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-simple-4 - name: tcp-route-simple-4 + serviceName: tcp-route-simple-4-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-4-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml index dc24432f097..8d9b5f2277d 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: tcp-route-simple +- clusterName: tcp-route-simple-dest endpoints: - lbEndpoints: - endpoint: @@ -6,14 +6,17 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: tcp-route-simple-1 + locality: + region: tcp-route-simple-dest/backend/0 +- clusterName: tcp-route-simple-1-dest endpoints: - lbEndpoints: - endpoint: @@ -21,14 +24,17 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: tcp-route-simple-2 + locality: + region: tcp-route-simple-1-dest/backend/0 +- clusterName: tcp-route-simple-2-dest endpoints: - lbEndpoints: - endpoint: @@ -36,14 +42,17 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: tcp-route-simple-3 + locality: + region: tcp-route-simple-2-dest/backend/0 +- clusterName: tcp-route-simple-3-dest endpoints: - lbEndpoints: - endpoint: @@ -51,14 +60,17 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: tcp-route-simple-4 + locality: + region: tcp-route-simple-3-dest/backend/0 +- clusterName: tcp-route-simple-4-dest endpoints: - lbEndpoints: - endpoint: @@ -66,10 +78,13 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tcp-route-simple-4-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml index f4f03c8dbf3..663e7b98908 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml @@ -7,31 +7,31 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-simple + cluster: tcp-route-simple-dest statPrefix: tcp - filters: - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-simple-1 + cluster: tcp-route-simple-1-dest statPrefix: tcp - filters: - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-simple-2 + cluster: tcp-route-simple-2-dest statPrefix: tcp - filters: - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-simple-3 + cluster: tcp-route-simple-3-dest statPrefix: tcp - filters: - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-simple-4 + cluster: tcp-route-simple-4-dest statPrefix: tcp name: tcp-route-simple perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.clusters.yaml new file mode 100755 index 00000000000..5843a79b8f0 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc.clusters.yaml @@ -0,0 +1,100 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: oauth_foo_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.foo.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: oauth_foo_com_443/backend/0 + name: oauth_foo_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: oauth.foo.com + type: STRICT_DNS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: oauth_bar_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.bar.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: oauth_bar_com_443/backend/0 + name: oauth_bar_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: oauth.bar.com + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.endpoints.yaml new file mode 100755 index 00000000000..475b89a087c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml new file mode 100755 index 00000000000..2a001af7866 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml @@ -0,0 +1,135 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.oauth2_first-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + - email + - profile + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + credentials: + clientId: client.oauth.foo.com + hmacSecret: + name: first-route/oauth2/hmac_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: first-route/oauth2/client_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /oauth2/callback + redirectUri: '%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback' + signoutPath: + path: + exact: /signout + tokenEndpoint: + cluster: oauth_foo_com_443 + timeout: 10s + uri: https://oauth.foo.com/token + - name: envoy.filters.http.oauth2_second-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + - email + - profile + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + credentials: + clientId: client.oauth.bar.com + hmacSecret: + name: second-route/oauth2/hmac_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: second-route/oauth2/client_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /oauth2/callback + redirectUri: '%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback' + signoutPath: + path: + exact: /signout + tokenEndpoint: + cluster: oauth_bar_com_443 + timeout: 10s + uri: https://oauth.bar.com/token + - name: envoy.filters.http.oauth2_third-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + - email + - profile + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + credentials: + clientId: test.oauth.bar.com + hmacSecret: + name: third-route/oauth2/hmac_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: third-route/oauth2/client_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /oauth2/callback + redirectUri: '%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback' + signoutPath: + path: + exact: /signout + tokenEndpoint: + cluster: oauth_bar_com_443 + timeout: 10s + uri: https://oauth.bar.com/token + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml new file mode 100755 index 00000000000..74831a07eb8 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml @@ -0,0 +1,44 @@ +- ignorePortInHostMatching: true + name: first-listener + typedPerFilterConfig: + envoy.filters.http.oauth2_first-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + disabled: true + envoy.filters.http.oauth2_second-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + disabled: true + envoy.filters.http.oauth2_third-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + disabled: true + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.oauth2_first-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} + - match: + path: bar + name: second-route + route: + cluster: second-route-dest + typedPerFilterConfig: + envoy.filters.http.oauth2_second-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} + - match: + path: test + name: third-route + route: + cluster: third-route-dest + typedPerFilterConfig: + envoy.filters.http.oauth2_third-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.clusters.yaml new file mode 100644 index 00000000000..4e4dcf0659e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.clusters.yaml @@ -0,0 +1,24 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocket: + name: envoy.transport_sockets.upstream_proxy_protocol + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport + config: + version: V2 + transportSocket: + name: envoy.transport_sockets.raw_buffer + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.endpoints.yaml new file mode 100644 index 00000000000..3b3f2d09076 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.listeners.yaml new file mode 100644 index 00000000000..73ee1b42ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.routes.yaml new file mode 100644 index 00000000000..2734c7cc42a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/proxy-protocol-upstream.routes.yaml @@ -0,0 +1,12 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.clusters.yaml index 6242077e357..73b916d5c08 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -19,8 +20,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: second-route - name: second-route + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -32,8 +34,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: third-route - name: third-route + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -42,6 +45,7 @@ connectTimeout: 10s dnsLookupFamily: V4_ONLY dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST loadAssignment: clusterName: ratelimit_cluster endpoints: @@ -51,8 +55,10 @@ socketAddress: address: envoy-ratelimit.envoy-gateway-system.svc.example-cluster.local portValue: 8081 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: ratelimit_cluster/backend/0 name: ratelimit_cluster outlierDetection: {} perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.endpoints.yaml index 93b50200d69..475b89a087c 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,9 +6,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: second-route + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest endpoints: - lbEndpoints: - endpoint: @@ -16,9 +18,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: third-route + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest endpoints: - lbEndpoints: - endpoint: @@ -26,5 +30,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.routes.yaml index 81acb86455c..bc11e6c2a51 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-custom-domain.routes.yaml @@ -3,13 +3,13 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: path: foo/bar name: first-route route: - cluster: first-route + cluster: first-route-dest rateLimits: - actions: - headerValueMatch: @@ -24,7 +24,7 @@ path: example name: second-route route: - cluster: second-route + cluster: second-route-dest rateLimits: - actions: - requestHeaders: @@ -34,7 +34,7 @@ path: test name: third-route route: - cluster: third-route + cluster: third-route-dest rateLimits: - actions: - genericKey: diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.clusters.yaml index cc3bf3940f0..abe58dd36f1 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -19,8 +20,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: second-route - name: second-route + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -32,8 +34,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: third-route - name: third-route + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -45,8 +48,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: fourth-route - name: fourth-route + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -55,6 +59,7 @@ connectTimeout: 10s dnsLookupFamily: V4_ONLY dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST loadAssignment: clusterName: ratelimit_cluster endpoints: @@ -64,8 +69,10 @@ socketAddress: address: envoy-ratelimit.envoy-gateway-system.svc.cluster.local portValue: 8081 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: ratelimit_cluster/backend/0 name: ratelimit_cluster outlierDetection: {} perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.endpoints.yaml index 2a8c1e0d200..f185af17da7 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,9 +6,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: second-route + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest endpoints: - lbEndpoints: - endpoint: @@ -16,9 +18,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: third-route + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest endpoints: - lbEndpoints: - endpoint: @@ -26,9 +30,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: fourth-route + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest endpoints: - lbEndpoints: - endpoint: @@ -36,5 +42,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: fourth-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.routes.yaml index 5ccaa9e8c41..93f0e01997d 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-sourceip.routes.yaml @@ -3,13 +3,13 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: path: foo/bar name: first-route route: - cluster: first-route + cluster: first-route-dest rateLimits: - actions: - maskedRemoteAddress: @@ -18,7 +18,7 @@ path: example name: second-route route: - cluster: second-route + cluster: second-route-dest rateLimits: - actions: - maskedRemoteAddress: @@ -27,7 +27,7 @@ path: test name: third-route route: - cluster: third-route + cluster: third-route-dest rateLimits: - actions: - genericKey: @@ -37,7 +37,7 @@ path: distinct name: fourth-route route: - cluster: fourth-route + cluster: fourth-route-dest rateLimits: - actions: - maskedRemoteAddress: diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml index bd70e842d57..1ef3710072f 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -19,8 +20,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: second-route - name: second-route + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -32,8 +34,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: third-route - name: third-route + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -42,6 +45,7 @@ connectTimeout: 10s dnsLookupFamily: V4_ONLY dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST loadAssignment: clusterName: ratelimit_cluster endpoints: @@ -51,8 +55,10 @@ socketAddress: address: envoy-ratelimit.envoy-gateway-system.svc.cluster.local portValue: 8081 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: ratelimit_cluster/backend/0 name: ratelimit_cluster outlierDetection: {} perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml index 93b50200d69..475b89a087c 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,9 +6,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: second-route + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest endpoints: - lbEndpoints: - endpoint: @@ -16,9 +18,11 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} -- clusterName: third-route + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest endpoints: - lbEndpoints: - endpoint: @@ -26,5 +30,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml index 81acb86455c..bc11e6c2a51 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml @@ -3,13 +3,13 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: path: foo/bar name: first-route route: - cluster: first-route + cluster: first-route-dest rateLimits: - actions: - headerValueMatch: @@ -24,7 +24,7 @@ path: example name: second-route route: - cluster: second-route + cluster: second-route-dest rateLimits: - actions: - requestHeaders: @@ -34,7 +34,7 @@ path: test name: third-route route: - cluster: third-route + cluster: third-route-dest rateLimits: - actions: - genericKey: diff --git a/internal/xds/translator/testdata/out/xds-ir/simple-tls.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/simple-tls.clusters.yaml index 2c908f7ef69..c8692b81602 100644 --- a/internal/xds/translator/testdata/out/xds-ir/simple-tls.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/simple-tls.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: first-route - name: first-route + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml index f5f137f9374..3b3f2d09076 100644 --- a/internal/xds/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: first-route +- clusterName: first-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/simple-tls.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/simple-tls.routes.yaml index d7666a2b56a..2734c7cc42a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/simple-tls.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/simple-tls.routes.yaml @@ -3,10 +3,10 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - match: prefix: / name: first-route route: - cluster: first-route + cluster: first-route-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml index 7444d36745d..a4f9048c783 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-complex - name: tcp-route-complex + serviceName: tcp-route-complex-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-complex-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml index 73afba74f84..f1cd2f6a2dd 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: tcp-route-complex +- clusterName: tcp-route-complex-dest endpoints: - lbEndpoints: - endpoint: @@ -6,10 +6,13 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tcp-route-complex-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml index 18e0489c069..fb16f3e53ae 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml @@ -12,7 +12,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-complex + cluster: tcp-route-complex-dest statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml index a20d0223257..2e9e07958c4 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-simple - name: tcp-route-simple + serviceName: tcp-route-simple-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml index f511c9b9e16..7eb06a08f40 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: tcp-route-simple +- clusterName: tcp-route-simple-dest endpoints: - lbEndpoints: - endpoint: @@ -6,10 +6,13 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tcp-route-simple-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml index e14418086ce..ee24bc19f55 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml @@ -7,7 +7,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-simple + cluster: tcp-route-simple-dest statPrefix: tcp name: tcp-route-simple perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml index 87243f47d0d..e4bea76097a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tls-terminate - name: tls-terminate + serviceName: tls-terminate-dest + lbPolicy: LEAST_REQUEST + name: tls-terminate-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml index bcc2a6bc1d8..164abc9de25 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: tls-terminate +- clusterName: tls-terminate-dest endpoints: - lbEndpoints: - endpoint: @@ -6,10 +6,13 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tls-terminate-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml index 386d3b29576..cb355428d86 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml @@ -7,7 +7,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tls-terminate + cluster: tls-terminate-dest statPrefix: terminate transportSocket: name: envoy.transport_sockets.tls diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml index f4d91b0ab6e..b3af74933f2 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tcp-route-weighted-backend - name: tcp-route-weighted-backend + serviceName: tcp-route-weighted-backend-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-weighted-backend-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml index c4fe6fcf0bb..73db3a800a6 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: tcp-route-weighted-backend +- clusterName: tcp-route-weighted-backend-dest endpoints: - lbEndpoints: - endpoint: @@ -6,24 +6,37 @@ socketAddress: address: 1.1.1.1 portValue: 50001 - loadBalancingWeight: 20 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: tcp-route-weighted-backend-dest/backend/0 + - lbEndpoints: - endpoint: address: socketAddress: address: 2.2.2.2 portValue: 50002 - loadBalancingWeight: 40 + loadBalancingWeight: 1 + loadBalancingWeight: 40 + locality: + region: tcp-route-weighted-backend-dest/backend/1 + - lbEndpoints: - endpoint: address: socketAddress: address: 3.3.3.3 portValue: 50003 - loadBalancingWeight: 20 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: tcp-route-weighted-backend-dest/backend/2 + - lbEndpoints: - endpoint: address: socketAddress: address: 4.4.4.4 portValue: 50004 - loadBalancingWeight: 20 - loadBalancingWeight: 1 - locality: {} + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: tcp-route-weighted-backend-dest/backend/3 diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml index 1f102b78242..75d5b912e49 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml @@ -12,7 +12,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tcp-route-weighted-backend + cluster: tcp-route-weighted-backend-dest statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector diff --git a/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml index 0f44e69c316..6601b3394c7 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: tls-passthrough - name: tls-passthrough + serviceName: tls-passthrough-dest + lbPolicy: LEAST_REQUEST + name: tls-passthrough-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml index 1764e254af7..ec2795413be 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: tls-passthrough +- clusterName: tls-passthrough-dest endpoints: - lbEndpoints: - endpoint: @@ -6,10 +6,13 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tls-passthrough-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml index 87f541928af..f2f1197f943 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml @@ -10,7 +10,7 @@ - name: envoy.filters.network.tcp_proxy typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: tls-passthrough + cluster: tls-passthrough-dest statPrefix: passthrough listenerFilters: - name: envoy.filters.listener.tls_inspector diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml index a63f2f97a4c..2053a4bdeb1 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: direct-route - name: direct-route + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS @@ -16,6 +17,7 @@ connectTimeout: 10s dnsLookupFamily: V4_ONLY dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST loadAssignment: clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 endpoints: @@ -25,8 +27,10 @@ socketAddress: address: otel-collector.monitoring.svc.cluster.local portValue: 4317 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: tracing|otel-collector.monitoring.svc.cluster.local|4317/backend/0 name: tracing|otel-collector.monitoring.svc.cluster.local|4317 outlierDetection: {} perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing.endpoints.yaml index cddb5bf893b..20c80b3aaaa 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: direct-route +- clusterName: direct-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,5 +6,7 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: direct-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing.routes.yaml index 72e9dfd4264..d4a7fa5ae20 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing.routes.yaml @@ -3,7 +3,7 @@ virtualHosts: - domains: - '*' - name: first-listener + name: first-listener/* routes: - directResponse: body: diff --git a/internal/xds/translator/testdata/out/xds-ir/udp-route.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/udp-route.clusters.yaml index 1e104fd40cc..3afb28bd407 100644 --- a/internal/xds/translator/testdata/out/xds-ir/udp-route.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/udp-route.clusters.yaml @@ -6,8 +6,9 @@ edsConfig: ads: {} resourceApiVersion: V3 - serviceName: udp-route - name: udp-route + serviceName: udp-route-dest + lbPolicy: LEAST_REQUEST + name: udp-route-dest outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/udp-route.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/udp-route.endpoints.yaml index 13add436c4a..2e3c84e672c 100644 --- a/internal/xds/translator/testdata/out/xds-ir/udp-route.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/udp-route.endpoints.yaml @@ -1,4 +1,4 @@ -- clusterName: udp-route +- clusterName: udp-route-dest endpoints: - lbEndpoints: - endpoint: @@ -6,10 +6,13 @@ socketAddress: address: 1.2.3.4 portValue: 50000 + loadBalancingWeight: 1 - endpoint: address: socketAddress: address: 5.6.7.8 portValue: 50001 + loadBalancingWeight: 1 loadBalancingWeight: 1 - locality: {} + locality: + region: udp-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/udp-route.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/udp-route.listeners.yaml index 09df7513be7..317a7ddc4dd 100644 --- a/internal/xds/translator/testdata/out/xds-ir/udp-route.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/udp-route.listeners.yaml @@ -13,6 +13,6 @@ name: route typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route - cluster: udp-route + cluster: udp-route-dest statPrefix: service name: udp-route diff --git a/internal/xds/translator/tracing.go b/internal/xds/translator/tracing.go index 3deb72f24d8..1349995ff86 100644 --- a/internal/xds/translator/tracing.go +++ b/internal/xds/translator/tracing.go @@ -14,8 +14,9 @@ import ( tracingtype "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" "github.com/pkg/errors" + "k8s.io/utils/ptr" - egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils/protocov" "github.com/envoyproxy/gateway/internal/xds/types" @@ -47,7 +48,7 @@ func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, e // TODO: consider add some default tags for better UX for k, v := range tracing.CustomTags { switch v.Type { - case egcfgv1a1.CustomTagTypeLiteral: + case egv1a1.CustomTagTypeLiteral: tags = append(tags, &tracingtype.CustomTag{ Tag: k, Type: &tracingtype.CustomTag_Literal_{ @@ -56,7 +57,7 @@ func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, e }, }, }) - case egcfgv1a1.CustomTagTypeEnvironment: + case egv1a1.CustomTagTypeEnvironment: defaultVal := "" if v.Environment.DefaultValue != nil { defaultVal = *v.Environment.DefaultValue @@ -71,7 +72,7 @@ func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, e }, }, }) - case egcfgv1a1.CustomTagTypeRequestHeader: + case egv1a1.CustomTagTypeRequestHeader: defaultVal := "" if v.RequestHeader.DefaultValue != nil { defaultVal = *v.RequestHeader.DefaultValue @@ -115,21 +116,25 @@ func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, e }, nil } -func processClusterForTracing(tCtx *types.ResourceVersionTable, tracing *ir.Tracing) { +func processClusterForTracing(tCtx *types.ResourceVersionTable, tracing *ir.Tracing) error { if tracing == nil { - return + return nil } clusterName := buildClusterName("tracing", tracing.Provider.Host, uint32(tracing.Provider.Port)) - if existingCluster := findXdsCluster(tCtx, clusterName); existingCluster == nil { - destinations := []*ir.RouteDestination{ir.NewRouteDest(tracing.Provider.Host, uint32(tracing.Provider.Port))} - addXdsCluster(tCtx, addXdsClusterArgs{ - name: clusterName, - destinations: destinations, - tSocket: nil, - protocol: HTTP2, - endpoint: DefaultEndpointType, - }) + ds := &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Protocol: ir.GRPC, + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(tracing.Provider.Host, uint32(tracing.Provider.Port))}, } + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: clusterName, + settings: []*ir.DestinationSetting{ds}, + tSocket: nil, + endpointType: EndpointTypeDNS, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + return nil } diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index 625dbfcc5a4..47a04d27c7f 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -8,12 +8,16 @@ package translator import ( "errors" "fmt" + "strings" + "time" clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "github.com/tetratelabs/multierror" @@ -22,6 +26,13 @@ import ( "github.com/envoyproxy/gateway/internal/xds/types" ) +var ( + ErrXdsClusterExists = errors.New("xds cluster exists") + ErrXdsSecretExists = errors.New("xds secret exists") +) + +const AuthorityHeaderKey = ":authority" + // Translator translates the xDS IR into xDS resources. type Translator struct { // GlobalRateLimit holds the global rate limit settings @@ -37,6 +48,14 @@ type GlobalRateLimitSettings struct { // ServiceURL is the URL of the global // rate limit service. ServiceURL string + + // Timeout specifies the timeout period for the proxy to access the ratelimit server + // If not set, timeout is 20000000(20ms). + Timeout time.Duration + + // FailClosed is a switch used to control the flow of traffic + // when the response from the ratelimit server cannot be obtained. + FailClosed bool } // Translate translates the XDS IR into xDS resources @@ -47,45 +66,76 @@ func (t *Translator) Translate(ir *ir.Xds) (*types.ResourceVersionTable, error) tCtx := new(types.ResourceVersionTable) - if err := t.processHTTPListenerXdsTranslation(tCtx, ir.HTTP, ir.AccessLog, ir.Tracing); err != nil { - return nil, err + // xDS translation is done in a best-effort manner, so we collect all errors + // and return them at the end. + // + // Reasoning: The validation in the CRD validation and API Gateway API + // translator should already catch most errors, there are just few rare cases + // where xDS translation can fail, for example, failed to call an extension + // hook or failed to patch an EnvoyPatchPolicy. In those cases, we don't want + // to fail the entire xDS translation to panic users, but instead, we want + // to collect all errors and reflect them in the status of the CRDs. + var errs error + if err := t.processHTTPListenerXdsTranslation( + tCtx, ir.HTTP, ir.AccessLog, ir.Tracing, ir.Metrics); err != nil { + errs = multierror.Append(errs, err) } if err := processTCPListenerXdsTranslation(tCtx, ir.TCP, ir.AccessLog); err != nil { - return nil, err + errs = multierror.Append(errs, err) } if err := processUDPListenerXdsTranslation(tCtx, ir.UDP, ir.AccessLog); err != nil { - return nil, err + errs = multierror.Append(errs, err) } if err := processJSONPatches(tCtx, ir.EnvoyPatchPolicies); err != nil { - return nil, err + errs = multierror.Append(errs, err) } - processClusterForAccessLog(tCtx, ir.AccessLog) - processClusterForTracing(tCtx, ir.Tracing) + if err := processClusterForAccessLog(tCtx, ir.AccessLog); err != nil { + errs = multierror.Append(errs, err) + } + if err := processClusterForTracing(tCtx, ir.Tracing); err != nil { + errs = multierror.Append(errs, err) + } // Check if an extension want to inject any clusters/secrets // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op if err := processExtensionPostTranslationHook(tCtx, t.ExtensionManager); err != nil { - return nil, err + errs = multierror.Append(errs, err) } - return tCtx, nil + return tCtx, errs } -func (t *Translator) processHTTPListenerXdsTranslation(tCtx *types.ResourceVersionTable, httpListeners []*ir.HTTPListener, - accesslog *ir.AccessLog, tracing *ir.Tracing) error { +func (t *Translator) processHTTPListenerXdsTranslation( + tCtx *types.ResourceVersionTable, + httpListeners []*ir.HTTPListener, + accessLog *ir.AccessLog, + tracing *ir.Tracing, + metrics *ir.Metrics, +) error { + // The XDS translation is done in a best-effort manner, so we collect all + // errors and return them at the end. + var errs error for _, httpListener := range httpListeners { addFilterChain := true var xdsRouteCfg *routev3.RouteConfiguration // Search for an existing listener, if it does not exist, create one. - xdsListener := findXdsListenerByHostPort(tCtx, httpListener.Address, httpListener.Port, corev3.SocketAddress_TCP) + xdsListener := findXdsListenerByHostPort( + tCtx, httpListener.Address, httpListener.Port, corev3.SocketAddress_TCP) if xdsListener == nil { - xdsListener = buildXdsTCPListener(httpListener.Name, httpListener.Address, httpListener.Port, accesslog) - tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener) + xdsListener = buildXdsTCPListener( + httpListener.Name, httpListener.Address, httpListener.Port, + httpListener.TCPKeepalive, accessLog) + if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { + // skip this listener if failed to add xds listener to the + // resource version table. Normally, this should not happen. + errs = multierror.Append(errs, err) + continue + } } else if httpListener.TLS == nil { // Find the route config associated with this listener that // maps to the default filter chain for http traffic @@ -97,14 +147,16 @@ func (t *Translator) processHTTPListenerXdsTranslation(tCtx *types.ResourceVersi addFilterChain = false xdsRouteCfg = findXdsRouteConfig(tCtx, routeName) if xdsRouteCfg == nil { - return errors.New("unable to find xds route config") + // skip this listener if failed to find xds route config + errs = multierror.Append(errs, errors.New("unable to find xds route config")) + continue } } } if addFilterChain { - if err := t.addXdsHTTPFilterChain(xdsListener, httpListener, accesslog, tracing); err != nil { - return err + if err := t.addXdsHTTPFilterChain(xdsListener, httpListener, accessLog, tracing); err != nil { + errs = multierror.Append(errs, err) } } @@ -114,150 +166,211 @@ func (t *Translator) processHTTPListenerXdsTranslation(tCtx *types.ResourceVersi IgnorePortInHostMatching: true, Name: httpListener.Name, } - tCtx.AddXdsResource(resourcev3.RouteType, xdsRouteCfg) + + if err := tCtx.AddXdsResource(resourcev3.RouteType, xdsRouteCfg); err != nil { + errs = multierror.Append(errs, err) + } } // 1:1 between IR TLSListenerConfig and xDS Secret if httpListener.TLS != nil { for t := range httpListener.TLS { secret := buildXdsDownstreamTLSSecret(httpListener.TLS[t]) - tCtx.AddXdsResource(resourcev3.SecretType, secret) + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + errs = multierror.Append(errs, err) + } } } - // Allocate virtual host for this httpListener. - // 1:1 between IR HTTPListener and xDS VirtualHost - vHost := &routev3.VirtualHost{ - Name: httpListener.Name, - Domains: httpListener.Hostnames, - } - protocol := DefaultProtocol - if httpListener.IsHTTP2 { - protocol = HTTP2 - } + // store virtual hosts by domain + vHosts := map[string]*routev3.VirtualHost{} + // keep track of order by using a list as well as the map + var vHostsList []*routev3.VirtualHost // Check if an extension is loaded that wants to modify xDS Routes after they have been generated for _, httpRoute := range httpListener.Routes { + // 1:1 between IR HTTPRoute Hostname and xDS VirtualHost. + vHost := vHosts[httpRoute.Hostname] + if vHost == nil { + // Remove dots from the hostname before appending it to the virtualHost name + // since dots are special chars used in stats tag extraction in Envoy + underscoredHostname := strings.ReplaceAll(httpRoute.Hostname, ".", "_") + // Allocate virtual host for this httpRoute. + vHost = &routev3.VirtualHost{ + Name: fmt.Sprintf("%s/%s", httpListener.Name, underscoredHostname), + Domains: []string{httpRoute.Hostname}, + } + if metrics != nil && metrics.EnableVirtualHostStats { + vHost.VirtualClusters = []*routev3.VirtualCluster{ + { + Name: underscoredHostname, + Headers: []*routev3.HeaderMatcher{ + { + Name: AuthorityHeaderKey, + HeaderMatchSpecifier: &routev3.HeaderMatcher_StringMatch{ + StringMatch: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Prefix{ + Prefix: httpRoute.Hostname, + }, + }, + }, + }, + }, + }, + } + } + vHosts[httpRoute.Hostname] = vHost + vHostsList = append(vHostsList, vHost) + } + // 1:1 between IR HTTPRoute and xDS config.route.v3.Route - xdsRoute := buildXdsRoute(httpRoute, xdsListener) + xdsRoute, err := buildXdsRoute(httpRoute) + if err != nil { + // skip this route if failed to build xds route + errs = multierror.Append(errs, err) + continue + } // Check if an extension want to modify the route we just generated // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op. - if err := processExtensionPostRouteHook(xdsRoute, vHost, httpRoute, t.ExtensionManager); err != nil { - return err + if err = processExtensionPostRouteHook(xdsRoute, vHost, httpRoute, t.ExtensionManager); err != nil { + if err != nil { + errs = multierror.Append(errs, err) + } } vHost.Routes = append(vHost.Routes, xdsRoute) - // Skip trying to build an IR cluster if the httpRoute only has invalid backends - if len(httpRoute.Destinations) == 0 && httpRoute.BackendWeights.Invalid > 0 { - continue + if httpRoute.Destination != nil { + if err = processXdsCluster(tCtx, httpRoute); err != nil { + errs = multierror.Append(errs, err) + } } - addXdsCluster(tCtx, addXdsClusterArgs{ - name: httpRoute.Name, - destinations: httpRoute.Destinations, - tSocket: nil, - protocol: protocol, - endpoint: Static, - }) - - // If the httpRoute has a list of mirrors create clusters for them unless they already have one - for i, mirror := range httpRoute.Mirrors { - mirrorClusterName := fmt.Sprintf("%s-mirror-%d", httpRoute.Name, i) - if cluster := findXdsCluster(tCtx, mirrorClusterName); cluster == nil { - addXdsCluster(tCtx, addXdsClusterArgs{ - name: mirrorClusterName, - destinations: []*ir.RouteDestination{mirror}, + + if httpRoute.Mirrors != nil { + for _, mirrorDest := range httpRoute.Mirrors { + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: mirrorDest.Name, + settings: mirrorDest.Settings, tSocket: nil, - protocol: protocol, - endpoint: Static, - }) + endpointType: EndpointTypeStatic, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = multierror.Append(errs, err) + } } - } } - // Check if an extension want to modify the Virtual Host we just generated - // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op. - if err := processExtensionPostVHostHook(vHost, t.ExtensionManager); err != nil { - return err + for _, vHost := range vHostsList { + // Check if an extension want to modify the Virtual Host we just generated + // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op. + if err := processExtensionPostVHostHook(vHost, t.ExtensionManager); err != nil { + errs = multierror.Append(errs, err) + } } + xdsRouteCfg.VirtualHosts = append(xdsRouteCfg.VirtualHosts, vHostsList...) - xdsRouteCfg.VirtualHosts = append(xdsRouteCfg.VirtualHosts, vHost) + // Add per-route filter configs to the route config. + if err := patchRouteCfgWithPerRouteConfig(xdsRouteCfg, httpListener); err != nil { + errs = multierror.Append(errs, err) + } - // TODO: Make this into a generic interface for API Gateway features. - // https://github.com/envoyproxy/gateway/issues/882 - // Check if a ratelimit cluster exists, if not, add it, if its needed. - if err := t.createRateLimitServiceCluster(tCtx, httpListener); err != nil { + // Add all the other needed resources referenced by this filter to the + // resource version table. + if err := patchResources(tCtx, httpListener.Routes); err != nil { return err } - // Create authn jwks clusters, if needed. - if err := createJwksClusters(tCtx, httpListener.Routes); err != nil { - return err + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + // Check if a ratelimit cluster exists, if not, add it, if it's needed. + if err := t.createRateLimitServiceCluster(tCtx, httpListener); err != nil { + errs = multierror.Append(errs, err) } + // Check if an extension want to modify the listener that was just configured/created // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op if err := processExtensionPostListenerHook(tCtx, xdsListener, t.ExtensionManager); err != nil { - return err + errs = multierror.Append(errs, err) } } - return nil + return errs } func processTCPListenerXdsTranslation(tCtx *types.ResourceVersionTable, tcpListeners []*ir.TCPListener, accesslog *ir.AccessLog) error { + // The XDS translation is done in a best-effort manner, so we collect all + // errors and return them at the end. + var errs error for _, tcpListener := range tcpListeners { + // Search for an existing listener, if it does not exist, create one. + xdsListener := findXdsListenerByHostPort(tCtx, tcpListener.Address, tcpListener.Port, corev3.SocketAddress_TCP) + if xdsListener == nil { + xdsListener = buildXdsTCPListener(tcpListener.Name, tcpListener.Address, tcpListener.Port, tcpListener.TCPKeepalive, accesslog) + if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { + // skip this listener if failed to add xds listener to the + errs = multierror.Append(errs, err) + continue + } + } + + if err := addXdsTCPFilterChain(xdsListener, tcpListener, tcpListener.Destination.Name, accesslog); err != nil { + errs = multierror.Append(errs, err) + } + // 1:1 between IR TCPListener and xDS Cluster - addXdsCluster(tCtx, addXdsClusterArgs{ - name: tcpListener.Name, - destinations: tcpListener.Destinations, + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: tcpListener.Destination.Name, + settings: tcpListener.Destination.Settings, tSocket: nil, - protocol: DefaultProtocol, - endpoint: Static, - }) + endpointType: EndpointTypeStatic, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = multierror.Append(errs, err) + } if tcpListener.TLS != nil && tcpListener.TLS.Terminate != nil { for _, s := range tcpListener.TLS.Terminate { secret := buildXdsDownstreamTLSSecret(s) - tCtx.AddXdsResource(resourcev3.SecretType, secret) + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + errs = multierror.Append(errs, err) + } } } - // Search for an existing listener, if it does not exist, create one. - xdsListener := findXdsListenerByHostPort(tCtx, tcpListener.Address, tcpListener.Port, corev3.SocketAddress_TCP) - if xdsListener == nil { - xdsListener = buildXdsTCPListener(tcpListener.Name, tcpListener.Address, tcpListener.Port, accesslog) - tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener) - } - - if err := addXdsTCPFilterChain(xdsListener, tcpListener, tcpListener.Name, accesslog); err != nil { - return err - } } - return nil + return errs } func processUDPListenerXdsTranslation(tCtx *types.ResourceVersionTable, udpListeners []*ir.UDPListener, accesslog *ir.AccessLog) error { - for _, udpListener := range udpListeners { - // 1:1 between IR UDPListener and xDS Cluster - addXdsCluster(tCtx, addXdsClusterArgs{ - name: udpListener.Name, - destinations: udpListener.Destinations, - tSocket: nil, - protocol: DefaultProtocol, - endpoint: Static, - }) + // The XDS translation is done in a best-effort manner, so we collect all + // errors and return them at the end. + var errs error + for _, udpListener := range udpListeners { // There won't be multiple UDP listeners on the same port since it's already been checked at the gateway api // translator - xdsListener, err := buildXdsUDPListener(udpListener.Name, udpListener, accesslog) + xdsListener, err := buildXdsUDPListener(udpListener.Destination.Name, udpListener, accesslog) if err != nil { - return multierror.Append(err, errors.New("error building xds cluster")) + // skip this listener if failed to build xds listener + errs = multierror.Append(errs, err) + continue + } + if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { + // skip this listener if failed to add xds listener to the resource version table + errs = multierror.Append(errs, err) + continue } - tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener) - } - return nil + // 1:1 between IR UDPListener and xDS Cluster + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: udpListener.Destination.Name, + settings: udpListener.Destination.Settings, + tSocket: nil, + endpointType: EndpointTypeStatic, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = multierror.Append(errs, err) + } + } + return errs } // findXdsListenerByHostPort finds a xds listener with the same address, port and protocol, and returns nil if there is no match. @@ -295,7 +408,7 @@ func findXdsListener(tCtx *types.ResourceVersionTable, name string) *listenerv3. return nil } -// findXdsRouteConfig finds an xds route with the name and returns nil if there is no match. +// findXdsRouteConfig finds a xds route with the name and returns nil if there is no match. func findXdsRouteConfig(tCtx *types.ResourceVersionTable, name string) *routev3.RouteConfiguration { if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.RouteType] == nil { return nil @@ -343,39 +456,79 @@ func findXdsEndpoint(tCtx *types.ResourceVersionTable, name string) *endpointv3. return nil } -func addXdsCluster(tCtx *types.ResourceVersionTable, args addXdsClusterArgs) { - xdsCluster := buildXdsCluster(args.name, args.tSocket, args.protocol, args.endpoint) - xdsEndpoints := buildXdsClusterLoadAssignment(args.name, args.destinations) - // Use EDS for static endpoints - if args.endpoint == Static { - tCtx.AddXdsResource(resourcev3.EndpointType, xdsEndpoints) +// processXdsCluster processes a xds cluster by its endpoint address type. +func processXdsCluster(tCtx *types.ResourceVersionTable, httpRoute *ir.HTTPRoute) error { + // Get endpoint address type for xds cluster by returning the first DestinationSetting's AddressType, + // since there's no Mixed AddressType among all the DestinationSettings. + addrTypeState := httpRoute.Destination.Settings[0].AddressType + + var endpointType EndpointType + if addrTypeState != nil && *addrTypeState == ir.FQDN { + endpointType = EndpointTypeDNS } else { - xdsCluster.LoadAssignment = xdsEndpoints + endpointType = EndpointTypeStatic + } + + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: httpRoute.Destination.Name, + settings: httpRoute.Destination.Settings, + tSocket: nil, + endpointType: endpointType, + loadBalancer: httpRoute.LoadBalancer, + proxyProtocol: httpRoute.ProxyProtocol, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err } - tCtx.AddXdsResource(resourcev3.ClusterType, xdsCluster) + + return nil } -type addXdsClusterArgs struct { - name string - destinations []*ir.RouteDestination - tSocket *corev3.TransportSocket - protocol ProtocolType - endpoint EndpointType +// findXdsSecret finds a xds secret with the same name, and returns nil if there is no match. +func findXdsSecret(tCtx *types.ResourceVersionTable, name string) *tlsv3.Secret { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.SecretType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.SecretType] { + secret := r.(*tlsv3.Secret) + if secret.Name == name { + return secret + } + } + + return nil } -type ProtocolType int -type EndpointType int +func addXdsSecret(tCtx *types.ResourceVersionTable, secret *tlsv3.Secret) error { + // Return early if cluster with the same name exists + if c := findXdsSecret(tCtx, secret.Name); c != nil { + return ErrXdsSecretExists + } -const ( - DefaultProtocol ProtocolType = iota - TCP - UDP - HTTP - HTTP2 -) + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + return err + } + return nil +} -const ( - DefaultEndpointType EndpointType = iota - Static - EDS -) +func addXdsCluster(tCtx *types.ResourceVersionTable, args *xdsClusterArgs) error { + // Return early if cluster with the same name exists + if c := findXdsCluster(tCtx, args.name); c != nil { + return ErrXdsClusterExists + } + + xdsCluster := buildXdsCluster(args) + xdsEndpoints := buildXdsClusterLoadAssignment(args.name, args.settings) + // Use EDS for static endpoints + if args.endpointType == EndpointTypeStatic { + if err := tCtx.AddXdsResource(resourcev3.EndpointType, xdsEndpoints); err != nil { + return err + } + } else { + xdsCluster.LoadAssignment = xdsEndpoints + } + if err := tCtx.AddXdsResource(resourcev3.ClusterType, xdsCluster); err != nil { + return err + } + return nil +} diff --git a/internal/xds/translator/translator_test.go b/internal/xds/translator/translator_test.go index af7f0a4dd85..a501964fabb 100644 --- a/internal/xds/translator/translator_test.go +++ b/internal/xds/translator/translator_test.go @@ -9,6 +9,7 @@ import ( "embed" "flag" "path/filepath" + "strings" "testing" "time" @@ -21,7 +22,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" - "github.com/envoyproxy/gateway/api/config/v1alpha1" + "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/extension/testutils" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" "github.com/envoyproxy/gateway/internal/ir" @@ -62,6 +63,12 @@ func TestTranslateXds(t *testing.T) { { name: "http-route-mirror", }, + { + name: "http-route-multiple-mirrors", + }, + { + name: "http-route-multiple-matches", + }, { name: "http-route-direct-response", }, @@ -80,6 +87,9 @@ func TestTranslateXds(t *testing.T) { { name: "http-route-weighted-invalid-backend", }, + { + name: "http-route-dns-cluster", + }, { name: "simple-tls", requireSecrets: true, @@ -127,6 +137,10 @@ func TestTranslateXds(t *testing.T) { { name: "http-route-rewrite-url-host", }, + { + name: "http-route-timeout", + }, + { name: "ratelimit", }, @@ -137,24 +151,15 @@ func TestTranslateXds(t *testing.T) { { name: "ratelimit-sourceip", }, - { - name: "authn-single-route-single-match", - }, - { - name: "authn-multi-route-single-provider", - }, - { - name: "authn-multi-route-multi-provider", - }, - { - name: "authn-ratelimit", - }, { name: "accesslog", }, { name: "tracing", }, + { + name: "metrics-virtual-host", + }, { name: "jsonpatch", requireEnvoyPatchPolicies: true, @@ -167,6 +172,45 @@ func TestTranslateXds(t *testing.T) { name: "jsonpatch-invalid-patch", requireEnvoyPatchPolicies: true, }, + { + name: "listener-tcp-keepalive", + }, + { + name: "load-balancer", + }, + { + name: "cors", + }, + { + name: "jwt-multi-route-multi-provider", + }, + { + name: "jwt-multi-route-single-provider", + }, + { + name: "jwt-ratelimit", + }, + { + name: "jwt-single-route-single-match", + }, + { + name: "oidc", + }, + { + name: "http-route-partial-invalid", + }, + { + name: "listener-proxy-protocol", + }, + { + name: "jwt-custom-extractor", + }, + { + name: "proxy-protocol-upstream", + }, + { + name: "basic-auth", + }, } for _, tc := range testCases { @@ -184,7 +228,10 @@ func TestTranslateXds(t *testing.T) { } tCtx, err := tr.Translate(ir) - require.NoError(t, err) + if !strings.HasSuffix(tc.name, "partial-invalid") { + require.NoError(t, err) + } + listeners := tCtx.XdsResources[resourcev3.ListenerType] routes := tCtx.XdsResources[resourcev3.RouteType] clusters := tCtx.XdsResources[resourcev3.ClusterType] @@ -227,6 +274,61 @@ func TestTranslateXds(t *testing.T) { } } +func TestTranslateXdsNegative(t *testing.T) { + testCases := []struct { + name string + dnsDomain string + requireSecrets bool + }{ + { + name: "http-route-invalid", + }, + { + name: "tcp-route-invalid", + }, + { + name: "tcp-route-invalid-endpoint", + }, + { + name: "udp-route-invalid", + }, + { + name: "jsonpatch-invalid", + }, + { + name: "accesslog-invalid", + }, + { + name: "tracing-invalid", + }, + { + name: "jsonpatch-invalid-listener", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + dnsDomain := tc.dnsDomain + if dnsDomain == "" { + dnsDomain = "cluster.local" + } + ir := requireXdsIRFromInputTestData(t, "xds-ir", tc.name+".yaml") + tr := &Translator{ + GlobalRateLimit: &GlobalRateLimitSettings{ + ServiceURL: ratelimit.GetServiceURL("envoy-gateway-system", dnsDomain), + }, + } + + _, err := tr.Translate(ir) + require.Error(t, err) + if tc.name != "jsonpatch-invalid" { + require.Contains(t, err.Error(), "validation failed for xds resource") + } + }) + } +} + func TestTranslateRateLimitConfig(t *testing.T) { testCases := []struct { name string diff --git a/internal/xds/translator/utils.go b/internal/xds/translator/utils.go new file mode 100644 index 00000000000..407d646408c --- /dev/null +++ b/internal/xds/translator/utils.go @@ -0,0 +1,109 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "errors" + "fmt" + "net" + "net/url" + "strconv" + "strings" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/envoyproxy/gateway/internal/ir" +) + +const ( + defaultPort = 443 +) + +// urlCluster is a cluster that is created from a URL. +type urlCluster struct { + name string + hostname string + port uint32 + endpointType EndpointType +} + +// url2Cluster returns a urlCluster from the provided url. +func url2Cluster(strURL string) (*urlCluster, error) { + epType := EndpointTypeDNS + + // The URL should have already been validated in the gateway API translator. + u, err := url.Parse(strURL) + if err != nil { + return nil, err + } + + if u.Scheme != "https" { + return nil, fmt.Errorf("unsupported URI scheme %s", u.Scheme) + } + + port := defaultPort + if u.Port() != "" { + port, err = strconv.Atoi(u.Port()) + if err != nil { + return nil, err + } + } + + name := fmt.Sprintf("%s_%d", strings.ReplaceAll(u.Hostname(), ".", "_"), port) + + if ip := net.ParseIP(u.Hostname()); ip != nil { + if v4 := ip.To4(); v4 != nil { + epType = EndpointTypeStatic + } + } + + return &urlCluster{ + name: name, + hostname: u.Hostname(), + port: uint32(port), + endpointType: epType, + }, nil +} + +// enableFilterOnRoute enables a filterType on the provided route. +func enableFilterOnRoute(filterType string, route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + + filterName := perRouteFilterName(filterType, irRoute.Name) + filterCfg := route.GetTypedPerFilterConfig() + if _, ok := filterCfg[filterName]; ok { + // This should not happen since this is the only place where the filter + // config is added in a route. + return fmt.Errorf("route already contains filter config: %s, %+v", + filterType, route) + } + + // Enable the corresponding filter for this route. + routeCfgAny, err := anypb.New(&routev3.FilterConfig{ + Config: &anypb.Any{}, + }) + if err != nil { + return err + } + + if filterCfg == nil { + route.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + route.TypedPerFilterConfig[filterName] = routeCfgAny + + return nil +} + +func perRouteFilterName(filterType, routeName string) string { + return fmt.Sprintf("%s_%s", filterType, routeName) +} diff --git a/internal/xds/types/resourceversiontable.go b/internal/xds/types/resourceversiontable.go index 0aca38e2218..067f67273c1 100644 --- a/internal/xds/types/resourceversiontable.go +++ b/internal/xds/types/resourceversiontable.go @@ -6,6 +6,13 @@ package types import ( + "fmt" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" "github.com/envoyproxy/go-control-plane/pkg/cache/types" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "google.golang.org/protobuf/proto" @@ -68,7 +75,65 @@ func (t *ResourceVersionTable) GetXdsResources() XdsResources { return t.XdsResources } -func (t *ResourceVersionTable) AddXdsResource(rType resourcev3.Type, xdsResource types.Resource) { +func (t *ResourceVersionTable) AddXdsResource(rType resourcev3.Type, xdsResource types.Resource) error { + // Perform type switch to handle different types of xdsResource + switch rType { + case resourcev3.ListenerType: + // Handle Type specific operations + if resourceOfType, ok := xdsResource.(*listenerv3.Listener); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err:%v", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to Listener type", xdsResource) + } + case resourcev3.RouteType: + // Handle Type specific operations + if resourceOfType, ok := xdsResource.(*routev3.RouteConfiguration); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err:%v", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to RouteConfiguration type", xdsResource) + } + + case resourcev3.SecretType: + // Handle specific operations + if resourceOfType, ok := xdsResource.(*tlsv3.Secret); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err:%v", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to Secret type", xdsResource) + } + + case resourcev3.EndpointType: + if resourceOfType, ok := xdsResource.(*endpointv3.ClusterLoadAssignment); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err:%v", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to ClusterLoadAssignment type", xdsResource) + } + + case resourcev3.ClusterType: + // Handle specific operations + if resourceOfType, ok := xdsResource.(*clusterv3.Cluster); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err:%v", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to Cluster type", xdsResource) + } + case resourcev3.RateLimitConfigType: + // Handle specific operations + // cfg resource from runner.go is the RateLimitConfig type from "github.com/envoyproxy/go-control-plane/ratelimit/config/ratelimit/v3", which does have validate function. + + // Add more cases for other types as needed + default: + // Handle the case when the type is not recognized or supported + } + if t.XdsResources == nil { t.XdsResources = make(XdsResources) } @@ -77,15 +142,19 @@ func (t *ResourceVersionTable) AddXdsResource(rType resourcev3.Type, xdsResource } t.XdsResources[rType] = append(t.XdsResources[rType], xdsResource) + return nil } // AddOrReplaceXdsResource will update an existing resource of rType according to matchFunc or add as a new resource // if none satisfy the match criteria. It will only update the first match it finds, regardless // if multiple resources satisfy the match criteria. -func (t *ResourceVersionTable) AddOrReplaceXdsResource(rType resourcev3.Type, resource types.Resource, matchFunc func(existing types.Resource, new types.Resource) bool) { +func (t *ResourceVersionTable) AddOrReplaceXdsResource(rType resourcev3.Type, resource types.Resource, matchFunc func(existing types.Resource, new types.Resource) bool) error { if t.XdsResources == nil || t.XdsResources[rType] == nil { - t.AddXdsResource(rType, resource) - return + if err := t.AddXdsResource(rType, resource); err != nil { + return err + } else { + return nil + } } var found bool @@ -97,8 +166,13 @@ func (t *ResourceVersionTable) AddOrReplaceXdsResource(rType resourcev3.Type, re } } if !found { - t.AddXdsResource(rType, resource) + if err := t.AddXdsResource(rType, resource); err != nil { + return err + } else { + return nil + } } + return nil } // SetResources will update an entire entry of the XdsResources for a certain type to the provided resources diff --git a/internal/xds/types/resourceversiontable_test.go b/internal/xds/types/resourceversiontable_test.go index f50e663c5e7..8da1c0cc7ca 100644 --- a/internal/xds/types/resourceversiontable_test.go +++ b/internal/xds/types/resourceversiontable_test.go @@ -12,6 +12,7 @@ import ( corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" "github.com/envoyproxy/go-control-plane/pkg/cache/types" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" @@ -169,6 +170,102 @@ func TestAddOrReplaceXdsResource(t *testing.T) { }, }, } + testEndpoint := &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "exampleservice.examplenamespace.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + updatedEndpoint := &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "modified.example.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + testRouteConfig := &routev3.RouteConfiguration{ + Name: "test-route-config", + VirtualHosts: []*routev3.VirtualHost{ + { + Name: "test-virtual-host", + Domains: []string{"test.example.com"}, + Routes: []*routev3.Route{ + { + Match: &routev3.RouteMatch{ + PathSpecifier: &routev3.RouteMatch_Prefix{ + Prefix: "/", + }, + }, + Action: &routev3.Route_Route{ + Route: &routev3.RouteAction{ + ClusterSpecifier: &routev3.RouteAction_Cluster{ + Cluster: "test-cluster", + }, + }, + }, + }, + }, + }, + }, + } + testSecret := &tlsv3.Secret{ + Name: "example-secret", + Type: &tlsv3.Secret_TlsCertificate{ + TlsCertificate: &tlsv3.TlsCertificate{ + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: []byte("-----BEGIN CERTIFICATE-----\n... Your certificate data ... \n-----END CERTIFICATE-----"), + }, + }, + PrivateKey: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: []byte("-----BEGIN PRIVATE KEY-----\n... Your private key data ... \n-----END PRIVATE KEY-----"), + }, + }, + }, + }, + // Add other fields for the secret as needed. + } + testCases := []struct { name string tableIn *ResourceVersionTable @@ -229,11 +326,63 @@ func TestAddOrReplaceXdsResource(t *testing.T) { }, }, }, + { + name: "inject-new-endpoint", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: testEndpoint, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{testEndpoint}, + }, + }, + }, + { + name: "replace-endpoint", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{testEndpoint}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: updatedEndpoint, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{updatedEndpoint}, + }, + }, + }, { name: "inject-new-listener", tableIn: &ResourceVersionTable{ XdsResources: XdsResources{ - resourcev3.ListenerType: []types.Resource{testListener}, + resourcev3.ListenerType: []types.Resource{}, }, }, typeIn: resourcev3.ListenerType, @@ -327,14 +476,421 @@ func TestAddOrReplaceXdsResource(t *testing.T) { }, }, }, + { + name: "inject-route-config", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{}, + }, + }, + typeIn: resourcev3.RouteType, + resourceIn: testRouteConfig, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{testRouteConfig}, + }, + }, + }, + { + name: "new-secret", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{}, + }, + }, + typeIn: resourcev3.SecretType, + resourceIn: testSecret, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{testSecret}, + }, + }, + }, } for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - tc.tableIn.AddOrReplaceXdsResource(tc.typeIn, tc.resourceIn, tc.funcIn) + err := tc.tableIn.AddOrReplaceXdsResource(tc.typeIn, tc.resourceIn, tc.funcIn) + require.NoError(t, err) diff := cmp.Diff(tc.tableOut, tc.tableIn.DeepCopy(), protocmp.Transform()) require.Empty(t, diff) }) } } + +func TestInvalidAddXdsResource(t *testing.T) { + invalidListener := &listenerv3.Listener{ + Name: "invalid-listener", + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + } + invalidRouteConfig := &routev3.RouteConfiguration{ + Name: "test-route-config", + VirtualHosts: []*routev3.VirtualHost{ + { + Name: "", // missing name + Domains: []string{"test.example.com"}, + Routes: []*routev3.Route{ + { + Match: &routev3.RouteMatch{ + PathSpecifier: &routev3.RouteMatch_Prefix{ + Prefix: "/", + }, + }, + Action: &routev3.Route_Route{ + Route: &routev3.RouteAction{ + ClusterSpecifier: &routev3.RouteAction_Cluster{ + Cluster: "test-cluster", + }, + }, + }, + }, + }, + }, + }, + } + + invalidCluster := &clusterv3.Cluster{ + Name: "test-cluster", + LoadAssignment: &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + invalidEndpoint := &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + invalidSecret := &tlsv3.Secret{ + Name: "=*&", + Type: &tlsv3.Secret_TlsCertificate{ + TlsCertificate: &tlsv3.TlsCertificate{ + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: []byte("-----BEGIN CERTIFICATE-----\n... Your certificate data ... \n-----END CERTIFICATE-----"), + }, + }, + PrivateKey: &corev3.DataSource{}, + }, + }, + // Add other fields for the secret as needed. + } + + testCases := []struct { + name string + tableIn *ResourceVersionTable + typeIn resourcev3.Type + resourceIn types.Resource + funcIn func(existing types.Resource, new types.Resource) bool + tableOut *ResourceVersionTable + }{ + { + name: "inject-invalid-listener", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ListenerType, + resourceIn: invalidListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + }, + }, + }, + { + name: "inject-invalid-route-config", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{}, + }, + }, + typeIn: resourcev3.RouteType, + resourceIn: invalidRouteConfig, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "inject-invalid-cluster", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ClusterType, + resourceIn: invalidCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-cluster-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ClusterType, + resourceIn: invalidListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-listener-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ListenerType, + resourceIn: invalidCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-route-config-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{}, + }, + }, + typeIn: resourcev3.RouteType, + resourceIn: invalidCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-secret-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{}, + }, + }, + typeIn: resourcev3.SecretType, + resourceIn: invalidRouteConfig, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "invalid-secret", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{}, + }, + }, + typeIn: resourcev3.SecretType, + resourceIn: invalidSecret, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "inject-invalid-endpoint", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: invalidEndpoint, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-endpoint-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: invalidListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: nil, + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.tableIn.AddOrReplaceXdsResource(tc.typeIn, tc.resourceIn, tc.funcIn) + require.Error(t, err) + }) + } +} diff --git a/proto/extension/context.pb.go b/proto/extension/context.pb.go index 8c8b2512f62..623e30e8c96 100644 --- a/proto/extension/context.pb.go +++ b/proto/extension/context.pb.go @@ -12,10 +12,11 @@ package extension import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/proto/extension/service.pb.go b/proto/extension/service.pb.go index 8e89b80788e..6d4428283d5 100644 --- a/proto/extension/service.pb.go +++ b/proto/extension/service.pb.go @@ -12,14 +12,15 @@ package extension import ( + reflect "reflect" + sync "sync" + v32 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" v31 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" v33 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/proto/extension/service_grpc.pb.go b/proto/extension/service_grpc.pb.go index 2d01d54b8a2..e19d68387d3 100644 --- a/proto/extension/service_grpc.pb.go +++ b/proto/extension/service_grpc.pb.go @@ -13,6 +13,7 @@ package extension import ( context "context" + grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" diff --git a/release-notes/v0.5.0.yaml b/release-notes/v0.5.0.yaml new file mode 100644 index 00000000000..52462de8926 --- /dev/null +++ b/release-notes/v0.5.0.yaml @@ -0,0 +1,75 @@ +date: July 26, 2023 + +changes: + - area: documentation + change: | + Added Docs for Installation page using Helm + Added Docs for Cert Manager Integration + Added Docs for Presentation Links + Added Docs for configuring multiple TLS Certificates per Listener + + - area: installation + change: | + Added Support for configuring Envoy Gateway Label and Annotations using Helm + Increased default Resource defaults for Envoy Gateway to 100m CPU and 256Mi Memory + Fixes Helm values for EnvoyGateway startup configuration + Added opt-in field to skip creating control plane TLS Certificates allowing users to bring their own certificates. + + - area: api + change: | + Upgraded to Gateway API v0.7.1 + Added Support for EnvoyPatchPolicy + Added Support for EnvoyProxy Telemetry - Access Logging, Traces and Metrics + Added Support for configuring EnvoyProxy Pod Labels + Added Support for configuring EnvoyProxy Deployment Strategy Settings, Volumes and Volume Mounts + Added Support for configuring EnvoyProxy as a NodePort Type Service + Added Support for Distinct RateLimiting for IP Addresses + Added Support for converting JWT Claims to Headers, to be used for RateLimiting + Added Admin Server for Envoy Gateway + Added Pprof Debug Support for Envoy Gateway + Added Support to Watch for Resources in Select Namespaces + breaking-change: | + Renamed field in EnvoyGateway API from Extension to ExtensionManager + + - area: ci-tooling-testing + change: | + Added Retest Github Action + Added CherryPick Github Action + Added E2E Step in Github CI Workflow + Added RateLimit E2E Tests + Added JWT Claim based RateLimit E2E Tests + Added Access Logging E2E tests + Added Metrics E2E tests + Added Tracing E2E tests + + - area: conformance + change: | + Enabled GatewayWithAttachedRoutes Test + Enabled HttpRouteRequestMirror Test + Skipped HTTPRouteRedirectPortAndScheme Test + + - area: translator + breaking-change: | + Renamed IR resources from - to / + which also affects generated Xds Resources + + - area: providers + change: | + Reconcile Node resources to be able to compute Status Addresses for Gateway + Discard Status before publishing Provider resources to reduce memory consumption + + - area: xds + change: | + Fix Init Race in Xds Runner when starting Xds Server and receiving Xds Input + Switched to Xds SOTW Server for RateLimit Service Configuration + Added Control Plane TLS between EnvoyProxy and RateLimit Service + Enabled adding RateLimit Headers when RateLimit is set + Allowed GRPCRoute and HTTPRoute to be linked to the same HTTPS Listener + Set ALPN in the Xds Listener with TLS enabled. + Added Best Practices Default Edge Settings to Xds Resources + Compute and Publish EnvoyPatchPolicy status from xds-translator runner + + - area: cli + change: | + Added egctl x translate Support to generate default missing Resources + Added egctl x translate Support for AuthenticationFilter and EnvoyPatchPolicy diff --git a/release-notes/v0.6.0-rc.1.yaml b/release-notes/v0.6.0-rc.1.yaml new file mode 100644 index 00000000000..6e7809c6bb0 --- /dev/null +++ b/release-notes/v0.6.0-rc.1.yaml @@ -0,0 +1,72 @@ +date: Oct 27, 2023 + +changes: + - area: documentation + change: | + Introduced a new website based on Hugo + Added Grafana dashboards and integration docs for EnvoyProxy metrics + Added Grafana integration docs for Gateway API metrics + + - area: installation + change: | + Added Support for configuring Envoy Gateway Label and Annotations using Helm + Increased default Resource defaults for Envoy Gateway to 100m CPU and 256Mi Memory + Fixes Helm values for EnvoyGateway startup configuration + Added opt-in field to skip creating control plane TLS Certificates allowing users to bring their own certificates. + + - area: api + change: | + Upgraded to Gateway API v1.0.0 + Added the ClientTrafficPolicy CRD with Keep Alive Support + Added the BackendTrafficPolicy CRD with RateLimit and LoadBalancer Support + Added the SecurityPolicy CRD with CORS and JWT Support + Added EnvoyGateway Metrics with Prometheus and OpenTelemetry support + Added Support for InitContainers in EnvoyProxy CRD + Added Support for LoadBalancerIP in EnvoyProxy CRD + Added Support for AllocateLoadBalancerNodePorts in EnvoyProxy CRD + Added Support for LoadBalancerClass in EnvoyProxy CRD + Added Support for selecting EnvoyProxy stats to be generated + Added Support for enabling EnvoyProxy Virtual Host metrics + Added Support for Merging Gateway resources onto the same infrastructure + + breaking-change: | + Removed the AuthenticationFilter CRD + Removed the RateLimitFilter CRD + Enabled EnvoyProxy Prometheus Endpoint by default with an option to disable it + Updated the Bootstrap field within the EnvoyProxy CRD with an additional value + field to specify bootstrap config + + - area: ci-tooling-testing + change: | + + - area: conformance + change: | + + - area: watchable + change: | + Improved caching of resource by implementing a compare function agnostic of resource order + + - area: translator + breaking-change: | + Added support for routing to EndpointSlice endpoints + Added support for HTTPRoute Timeouts + Added support for multiple RequestMirror filters per HTTPRoute rule + Use / instead of - in IR Route Names + Added Support to ignore ports in Host header + + - area: providers + change: | + Added the generationChangedPredicate to most resources to limit resource reconiliation + Improved reconiliation by using the same enqueue request for all resources + Added support for reconciling ServiceImport CRD + Added support for selectively watching resources based on Namespace Selector + + - area: xds + change: | + Fixed Layered Runtime warnings + Upgraded to the latest version of go-control-plane that fixed xDS Resource ordering issues for ADS. + Added HTTP2 Keep Alives to the xds connection + + - area: cli + change: | + Added Support for egctl stats command diff --git a/release-notes/v0.6.0.yaml b/release-notes/v0.6.0.yaml new file mode 100644 index 00000000000..d370027a0a5 --- /dev/null +++ b/release-notes/v0.6.0.yaml @@ -0,0 +1,75 @@ +date: Nov 1, 2023 + +changes: + - area: documentation + change: | + Introduced a new website based on Hugo + Added Grafana dashboards and integration docs for EnvoyProxy metrics + Added Grafana integration docs for Gateway API metrics + + - area: installation + change: | + Updated EnvoyProxy image to be a distroless variant. + Removed resources around kube-rbac-proxy + + + - area: api + change: | + Upgraded to Gateway API v1.0.0 + Added the ClientTrafficPolicy CRD with Keep Alive Support + Added the BackendTrafficPolicy CRD with RateLimit and LoadBalancer Support + Added the SecurityPolicy CRD with CORS and JWT Support + Added EnvoyGateway Metrics with Prometheus and OpenTelemetry support + Added Support for InitContainers in EnvoyProxy CRD + Added Support for LoadBalancerIP in EnvoyProxy CRD + Added Support for AllocateLoadBalancerNodePorts in EnvoyProxy CRD + Added Support for LoadBalancerClass in EnvoyProxy CRD + Added Support for selecting EnvoyProxy stats to be generated + Added Support for enabling EnvoyProxy Virtual Host metrics + Added Support for Merging Gateway resources onto the same infrastructure + + breaking-change: | + Removed the AuthenticationFilter CRD + Removed the RateLimitFilter CRD + Moved EnvoyProxy CRD from `config.gateway.envoyproxy.io` to `gateway.envoyproxy.io` + Enabled EnvoyProxy Prometheus Endpoint by default with an option to disable it + Updated the Bootstrap field within the EnvoyProxy CRD with an additional value + field to specify bootstrap config + + - area: conformance + change: | + Added Support for HTTPRouteBackendProtocolH2C Test + Added Support for HTTPRouteBackendProtocolWebSocket Test + Added Support for HTTPRouteRequestMultipleMirrors Test + Added Support for HTTPRouteTimeoutRequest Test + Added Support for HTTPRouteTimeoutBackendRequest Test + Added Support for HTTPRouteRedirectPortAndScheme Test + + - area: watchable + change: | + Improved caching of resource by implementing a compare function agnostic of resource order + + - area: translator + change: | + Added support for routing to EndpointSlice endpoints + Added support for HTTPRoute Timeouts + Added support for multiple RequestMirror filters per HTTPRoute rule + Use / instead of - in IR Route Names + Added Support to ignore ports in Host header + + - area: providers + change: | + Added the generationChangedPredicate to most resources to limit resource reconiliation + Improved reconiliation by using the same enqueue request for all resources + Added support for reconciling ServiceImport CRD + Added support for selectively watching resources based on Namespace Selector + + - area: xds + change: | + Fixed Layered Runtime warnings + Upgraded to the latest version of go-control-plane that fixed xDS Resource ordering issues for ADS. + Added HTTP2 Keep Alives to the xds connection + + - area: cli + change: | + Added Support for egctl stats command diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 00000000000..40b67f41a76 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,5 @@ +/public +resources/ +node_modules/ +package-lock.json +.hugo_build.lock \ No newline at end of file diff --git a/site/.nvmrc b/site/.nvmrc new file mode 100644 index 00000000000..b009dfb9d9f --- /dev/null +++ b/site/.nvmrc @@ -0,0 +1 @@ +lts/* diff --git a/site/Dockerfile b/site/Dockerfile new file mode 100644 index 00000000000..4e653452e09 --- /dev/null +++ b/site/Dockerfile @@ -0,0 +1,4 @@ +FROM klakegg/hugo:ext-alpine + +RUN apk add git && \ + git config --global --add safe.directory /src diff --git a/site/assets/icons/logo.svg b/site/assets/icons/logo.svg new file mode 100644 index 00000000000..b0e579bd9d4 --- /dev/null +++ b/site/assets/icons/logo.svg @@ -0,0 +1,170 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/site/assets/scss/_variables_project.scss b/site/assets/scss/_variables_project.scss new file mode 100644 index 00000000000..7e918d43018 --- /dev/null +++ b/site/assets/scss/_variables_project.scss @@ -0,0 +1,9 @@ +/* + +Add styles or override variables from the theme here. + +*/ + +$primary: #280C53; +$secondary: #802A78; +$dark: #280C53; diff --git a/site/config.yaml b/site/config.yaml new file mode 100644 index 00000000000..9070e384f0f --- /dev/null +++ b/site/config.yaml @@ -0,0 +1,15 @@ +# THIS IS A TEST CONFIG ONLY! +# FOR THE CONFIGURATION OF YOUR SITE USE hugo.yaml. +# +# As of Docsy 0.7.0, Hugo 0.110.0 or later must be used. +# +# The sole purpose of this config file is to detect Hugo-module builds that use +# an older version of Hugo. +# +# DO NOT add any config parameters to this file. You can safely delete this file +# if your project is using the required Hugo version. + +module: + hugoVersion: + extended: true + min: 0.110.0 diff --git a/site/content/en/_index.md b/site/content/en/_index.md new file mode 100644 index 00000000000..e724ec80a57 --- /dev/null +++ b/site/content/en/_index.md @@ -0,0 +1,87 @@ +--- +title: Envoy Gateway +--- + +{{< blocks/cover title="Welcome to Envoy Gateway!" image_anchor="top" height="full" >}} + + GET STARTED + + + CONTRIBUTING + +

Manages Envoy Proxy as a Standalone or Kubernetes-based Application Gateway

+{{< blocks/link-down color="white" >}} +{{< /blocks/cover >}} + +{{% blocks/lead color="black" %}} +Manage **Envoy Proxy** as a **Standalone** or **Kubernetes-based** Application Gateway. + +**Gateway API** are used to **dynamically** provision and configure the managed Envoy Proxies. +{{% /blocks/lead %}} + +{{% blocks/section type="row" color="dark" %}} + +{{% blocks/feature icon="fa fa-commenting" title="Expressive API" %}} +Based on Gateway API, with reasonable default settings to simplify the Envoy user experience, without knowing details of Envoy proxy. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-battery-full" title="Batteries included" %}} +Automatically Envoy infrastructure provisioning and management. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-tree" title="All environments" %}} +Support for heterogeneous environments. Initially, Kubernetes will receive the most focus. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-cubes" title="Extensibility" %}} +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-lock" title="Security"%}} +Supports a variety of Security features, such as TLS, TLS pass-through, secure gRPC, authentication. rate-limiting, etc. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fa fa-bolt" title="High Performance"%}} +Built on top of the high-performance Envoy proxy, which can handle millions of requests per second. +{{% /blocks/feature %}} + +{{% /blocks/section %}} + +{{% blocks/lead color="black" %}} +Lower barriers to adoption through **Expressive, Extensible, Role-oriented APIs** + +Support a multitude of **ingress** and **L7/L4** traffic routing + +Common foundation for vendors to build **value-added** products + +Without having to **re-engineer** +fundamental interactions. + +{{% /blocks/lead %}} + +{{% blocks/section type="row" color="dark" %}} + +{{% blocks/feature icon="fab fa-app-store-ios" title="Download **from Github**" url="https://github.com/envoyproxy/gateway/releases" %}} +Try Envoy Gateway in GitHub Releases +{{% /blocks/feature %}} + +{{% blocks/feature icon="fab fa-github" title="Contributions Welcome!" + url="/latest/contributions/" %}} +We do a [Pull Request](https://github.com/envoyproxy/gateway/pulls) +contributions workflow on **GitHub**. +{{% /blocks/feature %}} + +{{% blocks/feature icon="fab fa-slack" title="Contact us on Slack!" + url="https://envoyproxy.slack.com/archives/C03E6NHLESV" %}} +For announcement of latest features etc. +{{% /blocks/feature %}} + +{{% /blocks/section %}} + +{{% blocks/lead type="row" color="white" %}} + +CNCF + +--- +Member of the [Envoy Proxy](https://www.envoyproxy.io/) family aimed at significantly **decreasing the barrier** to entry when using Envoy for **API Gateway**. +{{% /blocks/lead %}} diff --git a/site/content/en/about/featured-background.jpg b/site/content/en/about/featured-background.jpg new file mode 100644 index 00000000000..b1f8c56bc29 Binary files /dev/null and b/site/content/en/about/featured-background.jpg differ diff --git a/site/content/en/about/index.md b/site/content/en/about/index.md new file mode 100644 index 00000000000..accb967eecc --- /dev/null +++ b/site/content/en/about/index.md @@ -0,0 +1,136 @@ +--- +title: About Envoy Gateway +linkTitle: About +--- + +{{% blocks/cover title="About Envoy Gateway" height="auto" %}} + +Envoy Gateway is an open source project for managing Envoy Proxy as a standalone or Kubernetes-based application gateway. + +Gateway API resources are used to dynamically provision and configure the managed Envoy Proxies. + +Read on to find out more, or visit our [documentation](/latest/) to get started! +{{% /blocks/cover %}} + +{{% blocks/section color="black" %}} + +## Objectives + +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer fundamental interactions. + +{{% /blocks/section %}} + +{{% blocks/section color="dark" %}} + +### ***Expressive API*** + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API](https://gateway-api.sigs.k8s.io), plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +--- + +### ***Batteries included*** + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +--- + +### ***All environments*** + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API](https://gateway-api.sigs.k8s.io/). +Additional goals include multi-cluster support and various runtime environments. + +--- + +### ***Extensibility*** + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +{{% /blocks/section %}} +{{% blocks/section color="black" %}} + +## Non-objectives + +--- + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +--- + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +{{% /blocks/section %}} + +{{% blocks/section color="dark" %}} + +## Personas + +***In order of priority*** + +--- + +### Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +--- + +### Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +--- + +{{% /blocks/section %}} + +{{% blocks/lead type="row" color="white" %}} + +CNCF + +--- +Member of the [Envoy Proxy](https://www.envoyproxy.io/) family aimed at significantly decreasing the barrier to entry when using Envoy for **API Gateway**. +{{% /blocks/lead %}} diff --git a/site/content/en/announcements/_index.md b/site/content/en/announcements/_index.md new file mode 100644 index 00000000000..6a22f4f48d1 --- /dev/null +++ b/site/content/en/announcements/_index.md @@ -0,0 +1,55 @@ ++++ +title = "Release Announcement" +description = "Envoy Gateway Release Announcement" +linktitle = "Announcement" + +[[cascade]] +type = "docs" ++++ + +This document provides details for Envoy Gateway releases. Envoy Gateway follows the Semantic Versioning [v2.0.0 spec][] +for release versioning. Since Envoy Gateway is a new project, minor releases are the only defined releases. Envoy +Gateway maintainers will establish additional release details, e.g. patch releases, at a future date. + +## Stable Releases + +Stable releases of Envoy Gateway include: + +* Minor Releases- A new release branch and corresponding tag are created from the `main` branch. A minor release + is supported for 6 months following the release date. As the project matures, Envoy Gateway maintainers will reassess + the support timeframe. + +Minor releases happen quarterly and follow the schedule below. + +## Release Management + +Minor releases are handled by a designated Envoy Gateway maintainer. This maintainer is considered the Release Manager +for the release. The details for creating a release are outlined in the [release guide][]. The Release Manager is +responsible for coordinating the overall release. This includes identifying issues to be fixed in the release, +communications with the Envoy Gateway community, and the mechanics of the release. + +| Quarter | Release Manager | +|:-------:|:--------------------------------------------------------------:| +| 2022 Q4 | Daneyon Hansen ([danehans](https://github.com/danehans)) | +| 2023 Q1 | Xunzhuo Liu ([Xunzhuo](https://github.com/Xunzhuo)) | +| 2023 Q2 | Alice Wasko ([AliceProxy](https://github.com/AliceProxy)) | +| 2023 Q3 | Arko Dasgupta ([arkodg](https://github.com/arkodg)) | +| 2023 Q4 | Arko Dasgupta ([arkodg](https://github.com/arkodg)) | +| 2024 Q1 | Xunzhuo Liu ([Xunzhuo](https://github.com/Xunzhuo)) | + +## Release Schedule + +In order to align with the Envoy Proxy [release schedule][], Envoy Gateway releases are produced on a fixed schedule +(the 22nd day of each quarter), with an acceptable delay of up to 2 weeks, and a hard deadline of 3 weeks. + +| Version | Expected | Actual | Difference | End of Life | +|:-------:|:-----------:|:-----------:|:----------:|:-----------:| +| 0.2.0 | 2022/10/22 | 2022/10/20 | -2 day | 2023/4/20 | +| 0.3.0 | 2023/01/22 | 2023/02/09 | +17 day | 2023/08/09 | +| 0.4.0 | 2023/04/22 | 2023/04/24 | +2 day | 2023/10/24 | +| 0.5.0 | 2023/07/22 | 2023/08/02 | +10 day | 2024/01/02 | +| 0.6.0 | 2023/11/01 | 2023/11/02 | +1 day | 2024/05/02 | + +[v2.0.0 spec]: https://semver.org/spec/v2.0.0.html +[release guide]: ../dev/releasing.md +[release schedule]: https://github.com/envoyproxy/envoy/blob/main/RELEASES.md#major-release-schedule diff --git a/site/content/en/announcements/v0.2.md b/site/content/en/announcements/v0.2.md new file mode 100644 index 00000000000..c4b7bc3da0c --- /dev/null +++ b/site/content/en/announcements/v0.2.md @@ -0,0 +1,46 @@ +--- +title: Announcing Envoy Gateway v0.2 +subtitle: Major Update +linktitle: Release v0.2 +description: Envoy Gateway v0.2 release announcement. +publishdate: 2022-10-20 +release: v0.2.0 +skip_list: true +--- + +We are pleased to announce the release of Envoy Gateway v0.2! + +This is the first functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for +helping publish the release. + +| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | +|-------------------|--------------|--------------------------------|--------------| + +## What's New + +The release adds a ton of features and functionality. Here are some highlights: + +### Kubernetes Support + +Run Envoy Gateway in a Kubernetes cluster. Checkout the [quickstart guide][] to get started with Envoy Gateway in a few +simple steps. + +### Gateway API Support + +Envoy Gateway supports Gateway API resources for running and configuring a managed fleet of Envoy proxies. Envoy Gateway +passes Gateway API core [conformance tests][] and supports GatewayClass, Gateway, HTTPRoute, and TLSRoute resources. See +the [documentation][docs] for additional details on how to use Envoy Gateway for your edge proxy and API gateway needs. + +## Envoy Gateway at EnvoyCon NA + +Envoy Gateway will be at [EnvoyCon NA][] this October in Detroit. Don't miss [our talk][] to learn more about the +release and future direction of the project. + +[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.2.0.yaml +[matrix]: https://gateway.envoyproxy.io/blog/2022/10/01/versions +[docs]: https://gateway.envoyproxy.io/index.html +[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.2.0 +[conformance tests]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=conformance +[quickstart guide]: https://gateway.envoyproxy.io/user/quickstart.html +[EnvoyCon NA]: https://events.linuxfoundation.org/envoycon-north-america/program/schedule/ +[our talk]: https://sched.co/1AO5S diff --git a/site/content/en/announcements/v0.3.md b/site/content/en/announcements/v0.3.md new file mode 100644 index 00000000000..ae620a2c98e --- /dev/null +++ b/site/content/en/announcements/v0.3.md @@ -0,0 +1,46 @@ +--- +title: Announcing Envoy Gateway v0.3 +subtitle: Major Update +linktitle: Release v0.3 +description: Envoy Gateway v0.3 release announcement. +publishdate: 2023-02-09 +release: v0.3.0 +skip_list: true +--- + +We are pleased to announce the release of Envoy Gateway v0.3! + +This is the second functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for +helping publish the release. + +| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | +|-------------------|--------------|--------------------------------|--------------| + +## What's New + +The release adds a ton of features and functionality. Here are some highlights: + +### Add Support for extended Gateway API fields + ++ Added Support for HTTPRoute URLRewrite Filter ++ Added Support for HTTPRoute RequestMirror Filter ++ Added Support for HTTPRoute ResponseHeaderModifier Filter + +### Add Support for experimental Gateway APIs + ++ Added Support for the TCPRoute API ++ Added Support for the UDPRoute API ++ Added Support for the GRPCRoute API + +### Add Support for Rate Limiting + ++ Added Support for Global Rate Limiting + +### Add Support for Authentication + ++ Added Support for Request Authentication + +[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.3.0.yaml +[matrix]: https://gateway.envoyproxy.io/blog/2022/10/01/versions +[docs]: https://gateway.envoyproxy.io/v0.3.0/index.html +[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.3.0 diff --git a/site/content/en/announcements/v0.4.md b/site/content/en/announcements/v0.4.md new file mode 100644 index 00000000000..b5faae47e6d --- /dev/null +++ b/site/content/en/announcements/v0.4.md @@ -0,0 +1,57 @@ +--- +title: Announcing Envoy Gateway v0.4 +subtitle: Major Update +linktitle: Release v0.4 +description: Envoy Gateway v0.4 release announcement. +publishdate: 2023-04-24 +release: v0.4.0 +skip_list: true +--- + +We are pleased to announce the release of Envoy Gateway v0.4! + +This is the third functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for +helping publish the release. + +| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | +|-------------------|--------------|--------------------------------|--------------| + +## What's New + +The release adds a ton of features and functionality. Here are some highlights: + +### Upgrade Gateway API Dependency + ++ Upgraded to Gateway API v0.6.2 + +### Add Helm Support + ++ Installation of Envoy Gateway can now be done through helm + +### Add egctl CLI Tool + ++ Added egctl Support for Dry Runs of Gateway API Config ++ Added egctl Support for Dumping Envoy Proxy xDS Resources + +### Add Support for extending Envoy Gateway + ++ Added Initial Framework for Building an Extension on top of Envoy Gateway + +### Ratelimiting + ++ Added Support for Ratelimiting Based On IP Subnet + +### API Updates + ++ Added Support for Custom Envoy Proxy Bootstrap Config ++ Added Support for Configuring the Envoy Proxy Image and Service ++ Added Support for Configuring Annotations, Resources, and Securitycontext Settings on Ratelimit Infra and Envoy Proxy ++ Added Support for Using Multiple Certificates on a Single Fully Qualified Domain Name ++ Envoy Proxy Pod and Container SecurityContext is now Configurable ++ Added Support for Service Method Match in GRPCRoute ++ Added EDS Support + +[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.4.0.yaml +[matrix]: https://gateway.envoyproxy.io/blog/2022/10/01/versions +[docs]: https://gateway.envoyproxy.io/v0.4.0/index.html +[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.4.0 diff --git a/site/content/en/announcements/v0.5.md b/site/content/en/announcements/v0.5.md new file mode 100644 index 00000000000..e23ae419231 --- /dev/null +++ b/site/content/en/announcements/v0.5.md @@ -0,0 +1,57 @@ +--- +title: Announcing Envoy Gateway v0.5 +subtitle: Major Update +linktitle: Release v0.5 +description: Envoy Gateway v0.5 release announcement. +publishdate: 2023-08-02 +release: v0.5.0 +skip_list: true +--- + +We are pleased to announce the release of Envoy Gateway v0.5! + +This is the third functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for +helping publish the release. + +| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | +|-------------------|--------------|--------------------------------|--------------| + +## What's New + +The release adds a ton of features and functionality. Here are some highlights: + +### Upgrade Gateway API Dependency + ++ Upgraded to Gateway API v0.7.1 + +### Add Data Plane Proxy Telemetry + ++ Added Support for Access Logging, Tracing and Metrics Telemetry + +### Add Support for directly configuring xDS + ++ Added Support for the EnvoyPatchPolicy API + +### Ratelimiting + ++ Added Support for Distinct Ratelimiting Based On IP Addresses ++ Added Support for JWT Claim based Ratelimiting ++ Switched to Xds SOTW Server for RateLimit Service Configuration + +### API Updates + ++ Added Support for configuring EnvoyProxy Pod Labels ++ Added Support for configuring EnvoyProxy Deployment Strategy Settings, Volumes and Volume Mounts ++ Added Support for configuring EnvoyProxy as a NodePort Type Service ++ Added Admin Server for Envoy Gateway ++ Added Pprof Debug Support for Envoy Gateway ++ Added Support to Watch for Resources in Select Namespaces + +## Envoy Proxy + ++ Added Best Practices Default Edge Settings to Xds Resources + +[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.5.0.yaml +[matrix]: https://gateway.envoyproxy.io/blog/2022/10/01/versions +[docs]: https://gateway.envoyproxy.io/v0.5.0/index.html +[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.5.0 diff --git a/site/content/en/announcements/v0.6.md b/site/content/en/announcements/v0.6.md new file mode 100644 index 00000000000..754adda218b --- /dev/null +++ b/site/content/en/announcements/v0.6.md @@ -0,0 +1,82 @@ +--- +title: Announcing Envoy Gateway v0.6 +subtitle: Major Update +linktitle: Release v0.6 +description: Envoy Gateway v0.6 release announcement. +publishdate: 2023-11-01 +release: v0.6.0 +skip_list: true +--- + +We are pleased to announce the release of Envoy Gateway v0.6! + +This is the fifth functional release of Envoy Gateway. We would like to thank the entire Envoy Gateway community for +helping publish the release. + +| [Release Notes][] | [Docs][docs] | [Compatibility Matrix][matrix] | [Download][] | +|-------------------|--------------|--------------------------------|--------------| + +## What's New + +The release adds a ton of features and functionality. Here are some highlights: + +### Gateway API + ++ Upgraded to Gateway API v1.0 ++ Added support for HTTPRoute Timeouts + +### Add Control Plane Proxy Telemetry + ++ Added Support for Metrics Telemetry + +### Add Support for directly configuring xDS + ++ Added Support for the EnvoyPatchPolicy API + +### ClientTrafficPolicy + ++ Added Support for configuring Downstream Keep Alives + +### BackendTrafficPolicy + ++ Added Support for configuring Rate limiting ++ Added Support for configuring load balancing + +### SecurityPolicy + ++ Added Support for configuring JWT ++ Added Support for configuring CORS + + +### API Updates + ++ Added support for selectively watching resources based on Namespace Selector ++ Added EnvoyGateway Metrics with Prometheus and OpenTelemetry support ++ Added Support for InitContainers in EnvoyProxy CRD ++ Added Support for LoadBalancerIP in EnvoyProxy CRD ++ Added Support for AllocateLoadBalancerNodePorts in EnvoyProxy CRD ++ Added Support for LoadBalancerClass in EnvoyProxy CRD ++ Added Support for selecting EnvoyProxy stats to be generated ++ Added Support for enabling EnvoyProxy Virtual Host metrics ++ Added Support for Merging Gateway resources onto the same infrastructure + +### CLI + ++ Added `egctl stats` command + +### Kubernetes Provider + ++ Improved reconiliation by using the same enqueue request for all resources ++ Added support for reconciling ServiceImport CRD + +### Breaking changes + ++ Removed RateLimitFilter, and replaced it with BackendTrafficPolicy ++ Removed AuthenticationFilter, and replaced it with SecurityPolicy ++ Moved the EnvoyProxy CRD from `config.gateway.envoyproxy.io` to `gateway.envoyproxy.io` ++ Converted the `bootstrap` field within `EnvoyProxy` into a struct to support merge operations. + +[Release Notes]: https://github.com/envoyproxy/gateway/blob/main/release-notes/v0.6.0.yaml +[matrix]: https://gateway.envoyproxy.io/blog/2022/10/01/versions +[docs]: https://gateway.envoyproxy.io/v0.6.0/index.html +[Download]: https://github.com/envoyproxy/gateway/releases/tag/v0.6.0 diff --git a/site/content/en/blog/_index.md b/site/content/en/blog/_index.md new file mode 100644 index 00000000000..45f940a5823 --- /dev/null +++ b/site/content/en/blog/_index.md @@ -0,0 +1,7 @@ +--- +title: Blog +--- + +This is the **blog** section. It has two categories: News and Releases. + +Files in these directories will be listed in reverse chronological order. diff --git a/site/content/en/blog/compatibility/_index.md b/site/content/en/blog/compatibility/_index.md new file mode 100644 index 00000000000..5c1ae3b1db3 --- /dev/null +++ b/site/content/en/blog/compatibility/_index.md @@ -0,0 +1,4 @@ +--- +title: Compatibility Matrix +description: This section includes Compatibility Matrix of Envoy Gateway. +--- diff --git a/site/content/en/blog/compatibility/matrix/matrix.md b/site/content/en/blog/compatibility/matrix/matrix.md new file mode 100644 index 00000000000..75271c3fc83 --- /dev/null +++ b/site/content/en/blog/compatibility/matrix/matrix.md @@ -0,0 +1,21 @@ +--- +title: Versions +date: 2022-10-01 +description: This section includes Compatibility Matrix of Envoy Gateway. +--- + +Envoy Gateway relies on the Envoy Proxy and the Gateway API, and runs +within a Kubernetes cluster. Not all versions of each of these products +can function together for Envoy Gateway. Supported version combinations +are listed below; **bold** type indicates the versions of the Envoy +Proxy and the Gateway API actually compiled into each Envoy Gateway +release. + +| Envoy Gateway version | Envoy Proxy version | Rate Limit version | Gateway API version | Kubernetes version | +| --------------------- | ------------------- | ------------------ | ------------------- | ------------------- | +| v0.6.0 | **distroless-v1.28-latest** | **b9796237** | **v1.0.0** | v1.26, v1.27, v1.28 | +| v0.5.0 | **v1.27-latest** | **e059638d** | **v0.7.1** | v1.25, v1.26, v1.27 | +| v0.4.0 | **v1.26-latest** | **542a6047** | **v0.6.2** | v1.25, v1.26, v1.27 | +| v0.3.0 | **v1.25-latest** | **f28024e3** | **v0.6.1** | v1.24, v1.25, v1.26 | +| v0.2.0 | **v1.23-latest** | | **v0.5.1** | v1.24 | +| latest | **dev-latest** | **master** | **v1.0.0** | v1.26, v1.27, v1.28 | diff --git a/site/content/en/blog/news/_index.md b/site/content/en/blog/news/_index.md new file mode 100644 index 00000000000..c609aa25431 --- /dev/null +++ b/site/content/en/blog/news/_index.md @@ -0,0 +1,4 @@ +--- +title: News +weight: 20 +--- diff --git a/site/content/en/blog/news/new-website/new-website.md b/site/content/en/blog/news/new-website/new-website.md new file mode 100644 index 00000000000..3ab77791826 --- /dev/null +++ b/site/content/en/blog/news/new-website/new-website.md @@ -0,0 +1,68 @@ +--- +date: 2023-10-08 +title: Welcome to new website! +linkTitle: Welcome to new website +description: > + We migrate our docs from Sphinx to Hugo now! +author: Xunzhuo Liu +--- + +{{% alert title="Summary" color="primary" %}} +Migrate from ***Sphinx*** to ***Hugo*** for Envoy Gateway Documents. +{{% /alert %}} + +## Introduction + +In the realm of static site generators, two names often come up: Sphinx and Hugo. While both are powerful tools, we recently made the decision to migrate our documentation from Sphinx to Hugo. This article aims to shed light on the reasons behind this move and the advantages we've discovered in using Hugo for static blogging. + +## Why Migrate? + +Sphinx, originally created for Python documentation, has served us well over the years. It offers a robust and flexible solution for technical documentation. However, as our needs evolved, we found ourselves seeking a tool that could offer faster build times, ease of use, and a more dynamic community. This led us to Hugo. + +## Advantages of Hugo + ++ **Speed**: Hugo is renowned for its speed. It can build a site in a fraction of the time it takes other static site generators. This is a significant advantage when working with large sites or when quick updates are necessary. + ++ **Ease of Use**: Hugo's simplicity is another strong point. It doesn't require a runtime environment, and its installation process is straightforward. Moreover, Hugo's content management is intuitive, making it easy for non-technical users to create and update content. + ++ **Flexibility**: Hugo supports a wide range of content types, from blog posts to documentation. It also allows for custom outputs, enabling us to tailor our site to our specific needs. + ++ **Active Community**: Hugo boasts a vibrant and active community. This means regular updates, a wealth of shared themes and plugins, and a responsive support network. + ++ **Multilingual Support**: Hugo's built-in support for multiple languages is a boon for global teams. It allows us to create content in various languages without the need for additional plugins or tools. + ++ **Markdown Support**: Hugo's native support for Markdown makes it easy to write and format content. This is particularly beneficial for technical writing, where code snippets and technical formatting are common. + +## Challenges Encountered During the Migration + +While the migration from Sphinx to Hugo has brought numerous benefits, it was not without its challenges and it really took me a lot of time on it. Here are some of the difficulties we encountered during the process: + ++ **Converting RST to Markdown**: Our documentation contained a large number of reStructuredText (RST) files, which needed to be converted to Markdown, the format Hugo uses. This required a careful and meticulous conversion process to ensure no information was lost or incorrectly formatted. + ++ **Adding Headings to tons of Markdown Files**: Hugo requires headings in its Markdown files, which our old documents did not have. We had to write scripts to add these headings in bulk, a task that required a deep understanding of both our content and Hugo's requirements. + ++ **Handling Multiple Versions**: Our documentation had already gone through five iterations, resulting in a large number of files to manage. We had to ensure that all versions were correctly migrated and that the versioning system in Hugo was correctly set up. + ++ **Designing a New Page Structure and Presentation**: To provide a better reading experience, we needed to design a new way to organize and present our pages. This involved understanding our readers' needs and how best to structure our content to meet those needs. + ++ **Updating Existing Toolchains**: The migration also required us to update our existing toolchains, including Makefile, CI, release processes, and auto-generation tools. This was a complex task that required a deep understanding of both our old and new systems. + +Despite these challenges, the benefits of migrating to Hugo have far outweighed the difficulties. The process, while complex, has provided us with a more efficient, user-friendly, and flexible system for managing our static blog. It's a testament to the power of Hugo and the value of continuous improvement in the tech world. + +## Conclusion + +While Sphinx has its strengths, our migration to Hugo has opened up new possibilities for our static blogging. The speed, ease of use, flexibility, and active community offered by Hugo have made it a powerful tool in our arsenal. + +## Some Words + +{{% alert title="Author" color="primary" %}} +[Xunzhuo Liu](https://github.com/Xunzhuo), the maintainer and steering committee member of Envoy Gateway. +{{% /alert %}} + +From the inception of my involvement in the project, I have placed great emphasis on user experience, including both developer and end-user experiences. I have built a rich toolchain, automated pipelines, Helm Charts, command-line tools, and documentation to enhance the overall experience of interacting with the project. + +My dedication to improving user experience was a driving force behind the decision to migrate from Sphinx to Hugo. I recognized the potential for Hugo to provide a more intuitive and efficient platform for managing the project's static blog. Despite the challenges encountered during the migration, my commitment to enhancing user experience ensured a successful transition. + +Through this migration, I have further demonstrated my commitment to continuous improvement and my ability to adapt to the evolving needs of the project. My work serves as a testament to the importance of user experience in software development and the value of embracing new tools and technologies to meet these needs. + +Enjoy new Envoy Gateway Website! ❤️ diff --git a/site/content/en/blog/presentations/_index.md b/site/content/en/blog/presentations/_index.md new file mode 100644 index 00000000000..c7e0ad1a052 --- /dev/null +++ b/site/content/en/blog/presentations/_index.md @@ -0,0 +1,4 @@ +--- +title: Presentations +weight: 20 +--- diff --git a/site/content/en/blog/presentations/kubecon-cn-2023/kubecon-cn-2023.md b/site/content/en/blog/presentations/kubecon-cn-2023/kubecon-cn-2023.md new file mode 100644 index 00000000000..433773033b8 --- /dev/null +++ b/site/content/en/blog/presentations/kubecon-cn-2023/kubecon-cn-2023.md @@ -0,0 +1,25 @@ +--- +title: "KubeCon China 2023" +date: 2023-09-28 +--- + +{{% alert title="Topic: Envoy Gateway: The API Gateway in the Cloud Native Era" color="warning" %}} + +***--- KubeCon China 2023*** + +{{% /alert %}} + +{{% alert title="Speaker" color="primary" %}} + ++ **Xunzhuo Liu**: Tencent, Envoy Gateway Maintainer ++ **Huabing Zhao**: Tetrate.io, Envoy Gateway Reviewer + +{{% /alert %}} + +{{% alert title="Slides" color="info" %}} +Download from [Link](https://static.sched.com/hosted_files/kccncosschn2023/60/KubeCon-Envoy%20Gateway_%20The%20API%20Gateway-in-the-Cloud-Native-Era.pptx?_gl=1*1aq9xw*_ga*MTM2MDcyNzI2My4xNjkwODU3ODMy*_ga_XH5XM35VHB*MTY5NjgzNjA1NC4xNS4xLjE2OTY4MzYwNjIuNTIuMC4w). +{{% /alert %}} + +## Watch Videos + +{{< youtube XBsDe9stMcg >}} diff --git a/site/content/en/blog/presentations/kubecon-eu-2023/kubecon-eu-2023.md b/site/content/en/blog/presentations/kubecon-eu-2023/kubecon-eu-2023.md new file mode 100644 index 00000000000..13a0ccb8c51 --- /dev/null +++ b/site/content/en/blog/presentations/kubecon-eu-2023/kubecon-eu-2023.md @@ -0,0 +1,24 @@ +--- +title: "KubeCon Europe 2023" +date: 2023-05-01 +--- + +{{% alert title="Topic: Envoy Gateway Update" color="warning" %}} + +***--- KubeCon Europe 2023*** + +{{% /alert %}} + +{{% alert title="Speaker" color="primary" %}} + ++ **Alice Wasko**: Ambassador Labs, Envoy Gateway Maintainer + +{{% /alert %}} + +{{% alert title="Slides" color="info" %}} +Download from [Link](https://static.sched.com/hosted_files/kccnceu2023/58/Kubecon_EU_2023_Envoy_Gateway_Update.pptx). +{{% /alert %}} + +## Watch Videos + +{{< youtube 4vnJxt9sVho >}} diff --git a/site/content/en/blog/presentations/kubecon-na-2022/kubecon-na-2022.md b/site/content/en/blog/presentations/kubecon-na-2022/kubecon-na-2022.md new file mode 100644 index 00000000000..4714c0f3d37 --- /dev/null +++ b/site/content/en/blog/presentations/kubecon-na-2022/kubecon-na-2022.md @@ -0,0 +1,25 @@ +--- +title: "KubeCon North America 2022" +date: 2022-10-28 +--- + +{{% alert title="Topic: Envoy Gateway Update" color="warning" %}} + +***--- KubeCon North America 2022*** + +{{% /alert %}} + +{{% alert title="Speaker" color="primary" %}} + ++ **Daneyon Hansen**: Solo.io, Envoy Gateway Maintainer ++ **Alice Wasko**: Ambassador Labs, Envoy Gateway Maintainer + +{{% /alert %}} + +{{% alert title="Slides" color="info" %}} +Download from [Link](https://static.sched.com/hosted_files/envoyconna22/2f/Envoy_Gateway_Project_Update_EnvoyCon_NA_2022.pptx). +{{% /alert %}} + +## Watch Videos + +{{< youtube 3MUOZc8XNCc >}} diff --git a/site/content/en/featured-background.jpg b/site/content/en/featured-background.jpg new file mode 100644 index 00000000000..b1f8c56bc29 Binary files /dev/null and b/site/content/en/featured-background.jpg differ diff --git a/site/content/en/latest/_index.md b/site/content/en/latest/_index.md new file mode 100644 index 00000000000..567b43bfe36 --- /dev/null +++ b/site/content/en/latest/_index.md @@ -0,0 +1,21 @@ ++++ +title = "Welcome to Envoy Gateway" +linktitle = "Documentation" +description = "Envoy Gateway Documents" + +[[cascade]] +type = "docs" ++++ + +{{% alert title="Note" color="primary" %}} + +This project is under **active** development. Many features are not complete. We would love for you to [Get Involved](contributions/)! + +{{% /alert %}} + +Envoy Gateway is an open source project for managing [Envoy Proxy](https://www.envoyproxy.io/) as a standalone or Kubernetes-based application +gateway. [Gateway API](https://gateway-api.sigs.k8s.io/) resources are used to dynamically provision and configure the managed Envoy Proxies. + +![architecture](/img/traffic.png) + +## Ready to get started? diff --git a/site/content/en/latest/api/_index.md b/site/content/en/latest/api/_index.md new file mode 100644 index 00000000000..396d9ffcefc --- /dev/null +++ b/site/content/en/latest/api/_index.md @@ -0,0 +1,5 @@ +--- +title: "API" +description: This section includes APIs of Envoy Gateway. +weight: 80 +--- diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md new file mode 100644 index 00000000000..dc11cd57a0c --- /dev/null +++ b/site/content/en/latest/api/extension_types.md @@ -0,0 +1,1815 @@ ++++ +title = "API Reference" ++++ + + +## Packages +- [gateway.envoyproxy.io/v1alpha1](#gatewayenvoyproxyiov1alpha1) + + +## gateway.envoyproxy.io/v1alpha1 + +Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io +API group. + + +### Resource Types +- [BackendTrafficPolicy](#backendtrafficpolicy) +- [BackendTrafficPolicyList](#backendtrafficpolicylist) +- [ClientTrafficPolicy](#clienttrafficpolicy) +- [ClientTrafficPolicyList](#clienttrafficpolicylist) +- [EnvoyGateway](#envoygateway) +- [EnvoyPatchPolicy](#envoypatchpolicy) +- [EnvoyPatchPolicyList](#envoypatchpolicylist) +- [EnvoyProxy](#envoyproxy) +- [SecurityPolicy](#securitypolicy) +- [SecurityPolicyList](#securitypolicylist) + + + +#### BackendTrafficPolicy + + + +BackendTrafficPolicy allows the user to configure the behavior of the connection between the downstream client and Envoy Proxy listener. + +_Appears in:_ +- [BackendTrafficPolicyList](#backendtrafficpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `BackendTrafficPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[BackendTrafficPolicySpec](#backendtrafficpolicyspec)_ | spec defines the desired state of BackendTrafficPolicy. | + + +#### BackendTrafficPolicyList + + + +BackendTrafficPolicyList contains a list of BackendTrafficPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `BackendTrafficPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[BackendTrafficPolicy](#backendtrafficpolicy) array_ | | + + +#### BackendTrafficPolicySpec + + + +spec defines the desired state of BackendTrafficPolicy. + +_Appears in:_ +- [BackendTrafficPolicy](#backendtrafficpolicy) + +| Field | Description | +| --- | --- | +| `targetRef` _[PolicyTargetReferenceWithSectionName](#policytargetreferencewithsectionname)_ | targetRef is the name of the resource this policy is being attached to. This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway. | +| `rateLimit` _[RateLimitSpec](#ratelimitspec)_ | RateLimit allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. | +| `loadBalancer` _[LoadBalancer](#loadbalancer)_ | LoadBalancer policy to apply when routing traffic from the gateway to the backend endpoints | +| `proxyProtocol` _[ProxyProtocol](#proxyprotocol)_ | ProxyProtocol enables the Proxy Protocol when communicating with the backend. | +| `tcpKeepalive` _[TCPKeepalive](#tcpkeepalive)_ | TcpKeepalive settings associated with the upstream client connection. Disabled by default. | + + + + +#### BasicAuth + + + +BasicAuth defines the configuration for the HTTP Basic Authentication. + +_Appears in:_ +- [SecurityPolicySpec](#securitypolicyspec) + +| Field | Description | +| --- | --- | +| `users` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | The Kubernetes secret which contains the username-password pairs in htpasswd format, used to verify user credentials in the "Authorization" header. + This is an Opaque secret. The username-password pairs should be stored in the key ".htpasswd". As the key name indicates, the value needs to be the htpasswd format, for example: "user1:{SHA}hashed_user1_password". Right now, only SHA hash algorithm is supported. Reference to https://httpd.apache.org/docs/2.4/programs/htpasswd.html for more details. + Note: The secret must be in the same namespace as the SecurityPolicy. | + + +#### BootstrapType + +_Underlying type:_ `string` + +BootstrapType defines the types of bootstrap supported by Envoy Gateway. + +_Appears in:_ +- [ProxyBootstrap](#proxybootstrap) + + + +#### CORS + + + +CORS defines the configuration for Cross-Origin Resource Sharing (CORS). + +_Appears in:_ +- [SecurityPolicySpec](#securitypolicyspec) + +| Field | Description | +| --- | --- | +| `allowOrigins` _[StringMatch](#stringmatch) array_ | AllowOrigins defines the origins that are allowed to make requests. | +| `allowMethods` _string array_ | AllowMethods defines the methods that are allowed to make requests. | +| `allowHeaders` _string array_ | AllowHeaders defines the headers that are allowed to be sent with requests. | +| `exposeHeaders` _string array_ | ExposeHeaders defines the headers that can be exposed in the responses. | +| `maxAge` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ | MaxAge defines how long the results of a preflight request can be cached. | +| `allowCredentials` _boolean_ | AllowCredentials indicates whether a request can include user credentials like cookies, authentication headers, or TLS client certificates. | + + +#### ClaimToHeader + + + +ClaimToHeader defines a configuration to convert JWT claims into HTTP headers + +_Appears in:_ +- [JWTProvider](#jwtprovider) + +| Field | Description | +| --- | --- | +| `header` _string_ | Header defines the name of the HTTP request header that the JWT Claim will be saved into. | +| `claim` _string_ | Claim is the JWT Claim that should be saved into the header : it can be a nested claim of type (eg. "claim.nested.key", "sub"). The nested claim name must use dot "." to separate the JSON name path. | + + +#### ClientTrafficPolicy + + + +ClientTrafficPolicy allows the user to configure the behavior of the connection between the downstream client and Envoy Proxy listener. + +_Appears in:_ +- [ClientTrafficPolicyList](#clienttrafficpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `ClientTrafficPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[ClientTrafficPolicySpec](#clienttrafficpolicyspec)_ | Spec defines the desired state of ClientTrafficPolicy. | + + +#### ClientTrafficPolicyList + + + +ClientTrafficPolicyList contains a list of ClientTrafficPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `ClientTrafficPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[ClientTrafficPolicy](#clienttrafficpolicy) array_ | | + + +#### ClientTrafficPolicySpec + + + +ClientTrafficPolicySpec defines the desired state of ClientTrafficPolicy. + +_Appears in:_ +- [ClientTrafficPolicy](#clienttrafficpolicy) + +| Field | Description | +| --- | --- | +| `targetRef` _[PolicyTargetReferenceWithSectionName](#policytargetreferencewithsectionname)_ | TargetRef is the name of the Gateway resource this policy is being attached to. This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway. TargetRef | +| `tcpKeepalive` _[TCPKeepalive](#tcpkeepalive)_ | TcpKeepalive settings associated with the downstream client connection. If defined, sets SO_KEEPALIVE on the listener socket to enable TCP Keepalives. Disabled by default. | +| `enableProxyProtocol` _boolean_ | EnableProxyProtocol interprets the ProxyProtocol header and adds the Client Address into the X-Forwarded-For header. Note Proxy Protocol must be present when this field is set, else the connection is closed. | + + + + +#### ConsistentHash + + + +ConsistentHash defines the configuration related to the consistent hash load balancer policy + +_Appears in:_ +- [LoadBalancer](#loadbalancer) + +| Field | Description | +| --- | --- | +| `type` _[ConsistentHashType](#consistenthashtype)_ | | + + +#### ConsistentHashType + +_Underlying type:_ `string` + +ConsistentHashType defines the type of input to hash on. + +_Appears in:_ +- [ConsistentHash](#consistenthash) + + + +#### CustomTag + + + + + +_Appears in:_ +- [ProxyTracing](#proxytracing) + +| Field | Description | +| --- | --- | +| `type` _[CustomTagType](#customtagtype)_ | Type defines the type of custom tag. | +| `literal` _[LiteralCustomTag](#literalcustomtag)_ | Literal adds hard-coded value to each span. It's required when the type is "Literal". | +| `environment` _[EnvironmentCustomTag](#environmentcustomtag)_ | Environment adds value from environment variable to each span. It's required when the type is "Environment". | +| `requestHeader` _[RequestHeaderCustomTag](#requestheadercustomtag)_ | RequestHeader adds value from request header to each span. It's required when the type is "RequestHeader". | + + +#### CustomTagType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [CustomTag](#customtag) + + + +#### EnvironmentCustomTag + + + +EnvironmentCustomTag adds value from environment variable to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines the name of the environment variable which to extract the value from. | +| `defaultValue` _string_ | DefaultValue defines the default value to use if the environment variable is not set. | + + +#### EnvoyGateway + + + +EnvoyGateway is the schema for the envoygateways API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyGateway` +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | +| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | +| `telemetry` _[EnvoyGatewayTelemetry](#envoygatewaytelemetry)_ | Telemetry defines the desired control plane telemetry related abilities. If unspecified, the telemetry is used with default configuration. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | +| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | + + +#### EnvoyGatewayAdmin + + + +EnvoyGatewayAdmin defines the Envoy Gateway Admin configuration. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `address` _[EnvoyGatewayAdminAddress](#envoygatewayadminaddress)_ | Address defines the address of Envoy Gateway Admin Server. | +| `enableDumpConfig` _boolean_ | EnableDumpConfig defines if enable dump config in Envoy Gateway logs. | +| `enablePprof` _boolean_ | EnablePprof defines if enable pprof in Envoy Gateway Admin Server. | + + +#### EnvoyGatewayAdminAddress + + + +EnvoyGatewayAdminAddress defines the Envoy Gateway Admin Address configuration. + +_Appears in:_ +- [EnvoyGatewayAdmin](#envoygatewayadmin) + +| Field | Description | +| --- | --- | +| `port` _integer_ | Port defines the port the admin server is exposed on. | +| `host` _string_ | Host defines the admin server hostname. | + + +#### EnvoyGatewayCustomProvider + + + +EnvoyGatewayCustomProvider defines configuration for the Custom provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `resource` _[EnvoyGatewayResourceProvider](#envoygatewayresourceprovider)_ | Resource defines the desired resource provider. This provider is used to specify the provider to be used to retrieve the resource configurations such as Gateway API resources | +| `infrastructure` _[EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider)_ | Infrastructure defines the desired infrastructure provider. This provider is used to specify the provider to be used to provide an environment to deploy the out resources like the Envoy Proxy data plane. | + + +#### EnvoyGatewayFileResourceProvider + + + +EnvoyGatewayFileResourceProvider defines configuration for the File Resource provider. + +_Appears in:_ +- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) + +| Field | Description | +| --- | --- | +| `paths` _string array_ | Paths are the paths to a directory or file containing the resource configuration. Recursive sub directories are not currently supported. | + + +#### EnvoyGatewayHostInfrastructureProvider + + + +EnvoyGatewayHostInfrastructureProvider defines configuration for the Host Infrastructure provider. + +_Appears in:_ +- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) + + + +#### EnvoyGatewayInfrastructureProvider + + + +EnvoyGatewayInfrastructureProvider defines configuration for the Custom Infrastructure provider. + +_Appears in:_ +- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) + +| Field | Description | +| --- | --- | +| `type` _[InfrastructureProviderType](#infrastructureprovidertype)_ | Type is the type of infrastructure providers to use. Supported types are "Host". | +| `host` _[EnvoyGatewayHostInfrastructureProvider](#envoygatewayhostinfrastructureprovider)_ | Host defines the configuration of the Host provider. Host provides runtime deployment of the data plane as a child process on the host environment. | + + +#### EnvoyGatewayKubernetesProvider + + + +EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `rateLimitDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. If unspecified, default settings for the managed Envoy ratelimit deployment resource are applied. | +| `watch` _[KubernetesWatchMode](#kuberneteswatchmode)_ | Watch holds configuration of which input resources should be watched and reconciled. | +| `deploy` _[KubernetesDeployMode](#kubernetesdeploymode)_ | Deploy holds configuration of how output managed resources such as the Envoy Proxy data plane should be deployed | +| `overwriteControlPlaneCerts` _boolean_ | OverwriteControlPlaneCerts updates the secrets containing the control plane certs, when set. | + + +#### EnvoyGatewayLogComponent + +_Underlying type:_ `string` + +EnvoyGatewayLogComponent defines a component that supports a configured logging level. + +_Appears in:_ +- [EnvoyGatewayLogging](#envoygatewaylogging) + + + +#### EnvoyGatewayLogging + + + +EnvoyGatewayLogging defines logging for Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[EnvoyGatewayLogComponent](#envoygatewaylogcomponent), values:[LogLevel](#loglevel))_ | Level is the logging level. If unspecified, defaults to "info". EnvoyGatewayLogComponent options: default/provider/gateway-api/xds-translator/xds-server/infrastructure/global-ratelimit. LogLevel options: debug/info/error/warn. | + + +#### EnvoyGatewayMetricSink + + + +EnvoyGatewayMetricSink defines control plane metric sinks where metrics are sent to. + +_Appears in:_ +- [EnvoyGatewayMetrics](#envoygatewaymetrics) + +| Field | Description | +| --- | --- | +| `type` _[MetricSinkType](#metricsinktype)_ | Type defines the metric sink type. EG control plane currently supports OpenTelemetry. | +| `openTelemetry` _[EnvoyGatewayOpenTelemetrySink](#envoygatewayopentelemetrysink)_ | OpenTelemetry defines the configuration for OpenTelemetry sink. It's required if the sink type is OpenTelemetry. | + + +#### EnvoyGatewayMetrics + + + +EnvoyGatewayMetrics defines control plane push/pull metrics configurations. + +_Appears in:_ +- [EnvoyGatewayTelemetry](#envoygatewaytelemetry) + +| Field | Description | +| --- | --- | +| `sinks` _[EnvoyGatewayMetricSink](#envoygatewaymetricsink) array_ | Sinks defines the metric sinks where metrics are sent to. | +| `prometheus` _[EnvoyGatewayPrometheusProvider](#envoygatewayprometheusprovider)_ | Prometheus defines the configuration for prometheus endpoint. | + + +#### EnvoyGatewayOpenTelemetrySink + + + + + +_Appears in:_ +- [EnvoyGatewayMetricSink](#envoygatewaymetricsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the sink service hostname. | +| `protocol` _string_ | Protocol define the sink service protocol. | +| `port` _integer_ | Port defines the port the sink service is exposed on. | + + +#### EnvoyGatewayPrometheusProvider + + + +EnvoyGatewayPrometheusProvider will expose prometheus endpoint in pull mode. + +_Appears in:_ +- [EnvoyGatewayMetrics](#envoygatewaymetrics) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable defines if disables the prometheus metrics in pull mode. | + + +#### EnvoyGatewayProvider + + + +EnvoyGatewayProvider defines the desired configuration of a provider. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of provider to use. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider)_ | Kubernetes defines the configuration of the Kubernetes provider. Kubernetes provides runtime configuration via the Kubernetes API. | +| `custom` _[EnvoyGatewayCustomProvider](#envoygatewaycustomprovider)_ | Custom defines the configuration for the Custom provider. This provider allows you to define a specific resource provider and a infrastructure provider. | + + +#### EnvoyGatewayResourceProvider + + + +EnvoyGatewayResourceProvider defines configuration for the Custom Resource provider. + +_Appears in:_ +- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) + +| Field | Description | +| --- | --- | +| `type` _[ResourceProviderType](#resourceprovidertype)_ | Type is the type of resource provider to use. Supported types are "File". | +| `file` _[EnvoyGatewayFileResourceProvider](#envoygatewayfileresourceprovider)_ | File defines the configuration of the File provider. File provides runtime configuration defined by one or more files. | + + +#### EnvoyGatewaySpec + + + +EnvoyGatewaySpec defines the desired state of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) + +| Field | Description | +| --- | --- | +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | +| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | +| `telemetry` _[EnvoyGatewayTelemetry](#envoygatewaytelemetry)_ | Telemetry defines the desired control plane telemetry related abilities. If unspecified, the telemetry is used with default configuration. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | +| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | + + +#### EnvoyGatewayTelemetry + + + +EnvoyGatewayTelemetry defines telemetry configurations for envoy gateway control plane. Control plane will focus on metrics observability telemetry and tracing telemetry later. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `metrics` _[EnvoyGatewayMetrics](#envoygatewaymetrics)_ | Metrics defines metrics configuration for envoy gateway. | + + +#### EnvoyJSONPatchConfig + + + +EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource using JSONPatch semantic + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyResourceType](#envoyresourcetype)_ | Type is the typed URL of the Envoy xDS Resource | +| `name` _string_ | Name is the name of the resource | +| `operation` _[JSONPatchOperation](#jsonpatchoperation)_ | Patch defines the JSON Patch Operation | + + +#### EnvoyPatchPolicy + + + +EnvoyPatchPolicy allows the user to modify the generated Envoy xDS resources by Envoy Gateway using this patch API + +_Appears in:_ +- [EnvoyPatchPolicyList](#envoypatchpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyPatchPolicySpec](#envoypatchpolicyspec)_ | Spec defines the desired state of EnvoyPatchPolicy. | + + +#### EnvoyPatchPolicyList + + + +EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[EnvoyPatchPolicy](#envoypatchpolicy) array_ | | + + +#### EnvoyPatchPolicySpec + + + +EnvoyPatchPolicySpec defines the desired state of EnvoyPatchPolicy. + +_Appears in:_ +- [EnvoyPatchPolicy](#envoypatchpolicy) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyPatchType](#envoypatchtype)_ | Type decides the type of patch. Valid EnvoyPatchType values are "JSONPatch". | +| `jsonPatches` _[EnvoyJSONPatchConfig](#envoyjsonpatchconfig) array_ | JSONPatch defines the JSONPatch configuration. | +| `targetRef` _[PolicyTargetReference](#policytargetreference)_ | TargetRef is the name of the Gateway API resource this policy is being attached to. Currently only attaching to Gateway is supported This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway TargetRef | +| `priority` _integer_ | Priority of the EnvoyPatchPolicy. If multiple EnvoyPatchPolicies are applied to the same TargetRef, they will be applied in the ascending order of the priority i.e. int32.min has the highest priority and int32.max has the lowest priority. Defaults to 0. | + + + + +#### EnvoyPatchType + +_Underlying type:_ `string` + +EnvoyPatchType specifies the types of Envoy patching mechanisms. + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + + + +#### EnvoyProxy + + + +EnvoyProxy is the schema for the envoyproxies API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyProxy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyProxySpec](#envoyproxyspec)_ | EnvoyProxySpec defines the desired state of EnvoyProxy. | + + +#### EnvoyProxyKubernetesProvider + + + +EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource provider. + +_Appears in:_ +- [EnvoyProxyProvider](#envoyproxyprovider) + +| Field | Description | +| --- | --- | +| `envoyDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | EnvoyDeployment defines the desired state of the Envoy deployment resource. If unspecified, default settings for the managed Envoy deployment resource are applied. | +| `envoyService` _[KubernetesServiceSpec](#kubernetesservicespec)_ | EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings for the managed Envoy service resource are applied. | +| `envoyHpa` _[KubernetesHorizontalPodAutoscalerSpec](#kuberneteshorizontalpodautoscalerspec)_ | EnvoyHpa defines the Horizontal Pod Autoscaler settings for Envoy Proxy Deployment. Once the HPA is being set, Replicas field from EnvoyDeployment will be ignored. | + + +#### EnvoyProxyProvider + + + +EnvoyProxyProvider defines the desired state of a resource provider. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of resource provider to use. A resource provider provides infrastructure resources for running the data plane, e.g. Envoy proxy, and optional auxiliary control planes. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider)_ | Kubernetes defines the desired state of the Kubernetes resource provider. Kubernetes provides infrastructure resources for running the data plane, e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings for managed Kubernetes resources are applied. | + + +#### EnvoyProxySpec + + + +EnvoyProxySpec defines the desired state of EnvoyProxy. + +_Appears in:_ +- [EnvoyProxy](#envoyproxy) + +| Field | Description | +| --- | --- | +| `provider` _[EnvoyProxyProvider](#envoyproxyprovider)_ | Provider defines the desired resource provider and provider-specific configuration. If unspecified, the "Kubernetes" resource provider is used with default configuration parameters. | +| `logging` _[ProxyLogging](#proxylogging)_ | Logging defines logging parameters for managed proxies. | +| `telemetry` _[ProxyTelemetry](#proxytelemetry)_ | Telemetry defines telemetry parameters for managed proxies. | +| `bootstrap` _[ProxyBootstrap](#proxybootstrap)_ | Bootstrap defines the Envoy Bootstrap as a YAML string. Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap to learn more about the syntax. If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration set by Envoy Gateway. Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources from it are not configurable and will result in the `EnvoyProxy` resource being rejected. Backward compatibility across minor versions is not guaranteed. We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. | +| `concurrency` _integer_ | Concurrency defines the number of worker threads to run. If unset, it defaults to the number of cpuset threads on the platform. | +| `mergeGateways` _boolean_ | MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure. Setting this field to true would merge all Gateway Listeners under the parent Gateway Class. This means that the port, protocol and hostname tuple must be unique for every listener. If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. | + + + + +#### EnvoyResourceType + +_Underlying type:_ `string` + +EnvoyResourceType specifies the type URL of the Envoy resource. + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + + + +#### ExtensionAPISettings + + + +ExtensionAPISettings defines the settings specific to Gateway API Extensions. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `enableEnvoyPatchPolicy` _boolean_ | EnableEnvoyPatchPolicy enables Envoy Gateway to reconcile and implement the EnvoyPatchPolicy resources. | + + +#### ExtensionHooks + + + +ExtensionHooks defines extension hooks across all supported runners + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `xdsTranslator` _[XDSTranslatorHooks](#xdstranslatorhooks)_ | XDSTranslator defines all the supported extension hooks for the xds-translator runner | + + +#### ExtensionManager + + + +ExtensionManager defines the configuration for registering an extension manager to the Envoy Gateway control plane. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `resources` _[GroupVersionKind](#groupversionkind) array_ | Resources defines the set of K8s resources the extension will handle. | +| `hooks` _[ExtensionHooks](#extensionhooks)_ | Hooks defines the set of hooks the extension supports | +| `service` _[ExtensionService](#extensionservice)_ | Service defines the configuration of the extension service that the Envoy Gateway Control Plane will call through extension hooks. | + + +#### ExtensionService + + + +ExtensionService defines the configuration for connecting to a registered extension service. + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `tls` _[ExtensionTLS](#extensiontls)_ | TLS defines TLS configuration for communication between Envoy Gateway and the extension service. | + + +#### ExtensionTLS + + + +ExtensionTLS defines the TLS configuration when connecting to an extension service + +_Appears in:_ +- [ExtensionService](#extensionservice) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | CertificateRef contains a references to objects (Kubernetes objects or otherwise) that contains a TLS certificate and private keys. These certificates are used to establish a TLS handshake to the extension server. + CertificateRef can only reference a Kubernetes Secret at this time. | + + +#### FileEnvoyProxyAccessLog + + + + + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + +| Field | Description | +| --- | --- | +| `path` _string_ | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). | + + +#### Gateway + + + +Gateway defines the desired Gateway API configuration of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `controllerName` _string_ | ControllerName defines the name of the Gateway API controller. If unspecified, defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following for additional details: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1.GatewayClass | + + +#### GlobalRateLimit + + + +GlobalRateLimit defines global rate limit configuration. + +_Appears in:_ +- [RateLimitSpec](#ratelimitspec) + +| Field | Description | +| --- | --- | +| `rules` _[RateLimitRule](#ratelimitrule) array_ | Rules are a list of RateLimit selectors and limits. Each rule and its associated limit is applied in a mutually exclusive way i.e. if multiple rules get selected, each of their associated limits get applied, so a single traffic request might increase the rate limit counters for multiple rules if selected. | + + +#### GroupVersionKind + + + +GroupVersionKind unambiguously identifies a Kind. It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `group` _string_ | | +| `version` _string_ | | +| `kind` _string_ | | + + +#### HeaderMatch + + + +HeaderMatch defines the match attributes within the HTTP Headers of the request. + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + + + +#### InfrastructureProviderType + +_Underlying type:_ `string` + +InfrastructureProviderType defines the types of custom infrastructure providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) + + + +#### JSONPatchOperation + + + +JSONPatchOperation defines the JSON Patch Operation as defined in https://datatracker.ietf.org/doc/html/rfc6902 + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + +| Field | Description | +| --- | --- | +| `op` _[JSONPatchOperationType](#jsonpatchoperationtype)_ | Op is the type of operation to perform | +| `path` _string_ | Path is the location of the target document/field where the operation will be performed Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. | +| `value` _[JSON](#json)_ | Value is the new value of the path location. | + + +#### JSONPatchOperationType + +_Underlying type:_ `string` + +JSONPatchOperationType specifies the JSON Patch operations that can be performed. + +_Appears in:_ +- [JSONPatchOperation](#jsonpatchoperation) + + + +#### JWT + + + +JWT defines the configuration for JSON Web Token (JWT) authentication. + +_Appears in:_ +- [SecurityPolicySpec](#securitypolicyspec) + +| Field | Description | +| --- | --- | +| `providers` _[JWTProvider](#jwtprovider) array_ | Providers defines the JSON Web Token (JWT) authentication provider type. When multiple JWT providers are specified, the JWT is considered valid if any of the providers successfully validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | + + +#### JWTExtractor + + + +JWTExtractor defines a custom JWT token extraction from HTTP request. + +_Appears in:_ +- [JWTProvider](#jwtprovider) + +| Field | Description | +| --- | --- | +| `cookies` _string array_ | Cookies represents a list of cookie names to extract the JWT token from. If specified, Envoy will extract the JWT token from the listed cookies and validate each of them. If any cookie is found to be an invalid JWT, a 401 error will be returned. | + + +#### JWTProvider + + + +JWTProvider defines how a JSON Web Token (JWT) can be verified. + +_Appears in:_ +- [JWT](#jwt) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines a unique name for the JWT provider. A name can have a variety of forms, including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. | +| `issuer` _string_ | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. | +| `audiences` _string array_ | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. | +| `remoteJWKS` _[RemoteJWKS](#remotejwks)_ | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. | +| `claimToHeaders` _[ClaimToHeader](#claimtoheader) array_ | ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers For examples, following config: The claim must be of type; string, int, double, bool. Array type claims are not supported | +| `extractFrom` _[JWTExtractor](#jwtextractor)_ | ExtractFrom defines different ways to extract the JWT token from HTTP request. If empty, it defaults to extract JWT token from the Authorization HTTP request header using Bearer schema or access_token from query parameters. | + + +#### KubernetesContainerSpec + + + +KubernetesContainerSpec defines the desired state of the Kubernetes container resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#envvar-v1-core) array_ | List of environment variables to set in the container. | +| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources required by this container. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | +| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#securitycontext-v1-core)_ | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| `image` _string_ | Image specifies the EnvoyProxy container image to be used, instead of the default image. | +| `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volumemount-v1-core) array_ | VolumeMounts are volumes to mount into the container's filesystem. Cannot be updated. | + + +#### KubernetesDeployMode + + + +KubernetesDeployMode holds configuration for how to deploy managed resources such as the Envoy Proxy data plane fleet. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) + + + +#### KubernetesDeploymentSpec + + + +KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `replicas` _integer_ | Replicas is the number of desired pods. Defaults to 1. | +| `strategy` _[DeploymentStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#deploymentstrategy-v1-apps)_ | The deployment strategy to use to replace existing pods with new ones. | +| `pod` _[KubernetesPodSpec](#kubernetespodspec)_ | Pod defines the desired specification of pod. | +| `container` _[KubernetesContainerSpec](#kubernetescontainerspec)_ | Container defines the desired specification of main container. | +| `initContainers` _[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#container-v1-core) array_ | List of initialization containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | + + +#### KubernetesHorizontalPodAutoscalerSpec + + + +KubernetesHorizontalPodAutoscalerSpec defines Kubernetes Horizontal Pod Autoscaler settings of Envoy Proxy Deployment. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec. + +_Appears in:_ +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `minReplicas` _integer_ | minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 replica. | +| `maxReplicas` _integer_ | maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas. | +| `metrics` _[MetricSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#metricspec-v2-autoscaling) array_ | metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). If left empty, it defaults to being based on CPU utilization with average on 80% usage. | +| `behavior` _[HorizontalPodAutoscalerBehavior](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#horizontalpodautoscalerbehavior-v2-autoscaling)_ | behavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). If not set, the default HPAScalingRules for scale up and scale down are used. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. | + + +#### KubernetesPodSpec + + + +KubernetesPodSpec defines the desired state of the Kubernetes pod resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations are the annotations that should be appended to the pods. By default, no pod annotations are appended. | +| `labels` _object (keys:string, values:string)_ | Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. | +| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)_ | SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. | +| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#affinity-v1-core)_ | If specified, the pod's scheduling constraints. | +| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#toleration-v1-core) array_ | If specified, the pod's tolerations. | +| `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volume-v1-core) array_ | Volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes | +| `hostNetwork` _boolean_ | HostNetwork, If this is set to true, the pod will use host's network namespace. | + + +#### KubernetesServiceSpec + + + +KubernetesServiceSpec defines the desired state of the Kubernetes service resource. + +_Appears in:_ +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations that should be appended to the service. By default, no annotations are appended. | +| `type` _[ServiceType](#servicetype)_ | Type determines how the Service is exposed. Defaults to LoadBalancer. Valid options are ClusterIP, LoadBalancer and NodePort. "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | +| `loadBalancerClass` _string_ | LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider implementation if more than one are available or is otherwise expected to be specified | +| `allocateLoadBalancerNodePorts` _boolean_ | AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a value), those requests will be respected, regardless of this field. This field may only be set for services with type LoadBalancer and will be cleared if the type is changed to any other type. | +| `loadBalancerIP` _string_ | LoadBalancerIP defines the IP Address of the underlying load balancer service. This field may be ignored if the load balancer provider does not support this feature. This field has been deprecated in Kubernetes, but it is still used for setting the IP Address in some cloud providers such as GCP. | + + +#### KubernetesWatchMode + + + +KubernetesWatchMode holds the configuration for which input resources to watch and reconcile. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) + +| Field | Description | +| --- | --- | +| `Type` _[KubernetesWatchModeType](#kuberneteswatchmodetype)_ | Type indicates what watch mode to use. KubernetesWatchModeTypeNamespaces and KubernetesWatchModeTypeNamespaceSelectors are currently supported By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources from all namespaces. | +| `Namespaces` _string array_ | Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped resources such as Gateway, HTTPRoute and Service. Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set | +| `namespaces` _string array_ | NamespaceSelectors holds a list of labels that namespaces have to have in order to be watched. Note this doesn't set the informer to watch the namespaces with the given labels. Informer still watches all namespaces. But the events for objects whois namespce have no given labels will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set | + + +#### KubernetesWatchModeType + +_Underlying type:_ `string` + +KubernetesWatchModeType defines the type of KubernetesWatchMode + +_Appears in:_ +- [KubernetesWatchMode](#kuberneteswatchmode) + + + +#### LiteralCustomTag + + + +LiteralCustomTag adds hard-coded value to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `value` _string_ | Value defines the hard-coded value to add to each span. | + + +#### LoadBalancer + + + +LoadBalancer defines the load balancer policy to be applied. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[LoadBalancerType](#loadbalancertype)_ | Type decides the type of Load Balancer policy. Valid LoadBalancerType values are "ConsistentHash", "LeastRequest", "Random", "RoundRobin", | +| `consistentHash` _[ConsistentHash](#consistenthash)_ | ConsistentHash defines the configuration when the load balancer type is set to ConsistentHash | +| `slowStart` _[SlowStart](#slowstart)_ | SlowStart defines the configuration related to the slow start load balancer policy. If set, during slow start window, traffic sent to the newly added hosts will gradually increase. Currently this is only supported for RoundRobin and LeastRequest load balancers | + + +#### LoadBalancerType + +_Underlying type:_ `string` + +LoadBalancerType specifies the types of LoadBalancer. + +_Appears in:_ +- [LoadBalancer](#loadbalancer) + + + +#### LogLevel + +_Underlying type:_ `string` + +LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. + +_Appears in:_ +- [EnvoyGatewayLogging](#envoygatewaylogging) +- [ProxyLogging](#proxylogging) + + + +#### MetricSinkType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [EnvoyGatewayMetricSink](#envoygatewaymetricsink) +- [ProxyMetricSink](#proxymetricsink) + + + +#### OIDC + + + +OIDC defines the configuration for the OpenID Connect (OIDC) authentication. + +_Appears in:_ +- [SecurityPolicySpec](#securitypolicyspec) + +| Field | Description | +| --- | --- | +| `provider` _[OIDCProvider](#oidcprovider)_ | The OIDC Provider configuration. | +| `clientID` _string_ | The client ID to be used in the OIDC [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). | +| `clientSecret` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | The Kubernetes secret which contains the OIDC client secret to be used in the [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + This is an Opaque secret. The client secret should be stored in the key "client-secret". | +| `scopes` _string array_ | The OIDC scopes to be used in the [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). The "openid" scope is always added to the list of scopes if not already specified. | + + +#### OIDCProvider + + + +OIDCProvider defines the OIDC Provider configuration. To make the EG OIDC config easy to use, some of the low-level ouath2 filter configuration knobs are hidden from the user, and default values will be provided when translating to XDS. For example: + * redirect_uri: uses a default redirect URI "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" + * signout_path: uses a default signout path "/signout" + * redirect_path_matcher: uses a default redirect path matcher "/oauth2/callback" + If we get requests to expose these knobs, we can always do so later. + +_Appears in:_ +- [OIDC](#oidc) + +| Field | Description | +| --- | --- | +| `issuer` _string_ | The OIDC Provider's [issuer identifier](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery). Issuer MUST be a URI RFC 3986 [RFC3986] with a scheme component that MUST be https, a host component, and optionally, port and path components and no query or fragment components. | +| `authorizationEndpoint` _string_ | The OIDC Provider's [authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint). If not provided, EG will try to discover it from the provider's [Well-Known Configuration Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). | +| `tokenEndpoint` _string_ | The OIDC Provider's [token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint). If not provided, EG will try to discover it from the provider's [Well-Known Configuration Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). | + + +#### OpenTelemetryEnvoyProxyAccessLog + + + +TODO: consider reuse ExtensionService? + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `resources` _object (keys:string, values:string)_ | Resources is a set of labels that describe the source of a log entry, including envoy node info. It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). | + + +#### ProviderType + +_Underlying type:_ `string` + +ProviderType defines the types of providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) +- [EnvoyProxyProvider](#envoyproxyprovider) + + + +#### ProxyAccessLog + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable disables access logging for managed proxies if set to true. | +| `settings` _[ProxyAccessLogSetting](#proxyaccesslogsetting) array_ | Settings defines accesslog settings for managed proxies. If unspecified, will send default format to stdout. | + + +#### ProxyAccessLogFormat + + + +ProxyAccessLogFormat defines the format of accesslog. By default accesslogs are written to standard output. + +_Appears in:_ +- [ProxyAccessLogSetting](#proxyaccesslogsetting) + +| Field | Description | +| --- | --- | +| `type` _[ProxyAccessLogFormatType](#proxyaccesslogformattype)_ | Type defines the type of accesslog format. | +| `text` _string_ | Text defines the text accesslog format, following Envoy accesslog formatting, It's required when the format type is "Text". Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. | +| `json` _object (keys:string, values:string)_ | JSON is additional attributes that describe the specific event occurrence. Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) can be used as values for fields within the Struct. It's required when the format type is "JSON". | + + +#### ProxyAccessLogFormatType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [ProxyAccessLogFormat](#proxyaccesslogformat) + + + +#### ProxyAccessLogSetting + + + + + +_Appears in:_ +- [ProxyAccessLog](#proxyaccesslog) + +| Field | Description | +| --- | --- | +| `format` _[ProxyAccessLogFormat](#proxyaccesslogformat)_ | Format defines the format of accesslog. | +| `sinks` _[ProxyAccessLogSink](#proxyaccesslogsink) array_ | Sinks defines the sinks of accesslog. | + + +#### ProxyAccessLogSink + + + +ProxyAccessLogSink defines the sink of accesslog. + +_Appears in:_ +- [ProxyAccessLogSetting](#proxyaccesslogsetting) + +| Field | Description | +| --- | --- | +| `type` _[ProxyAccessLogSinkType](#proxyaccesslogsinktype)_ | Type defines the type of accesslog sink. | +| `file` _[FileEnvoyProxyAccessLog](#fileenvoyproxyaccesslog)_ | File defines the file accesslog sink. | +| `openTelemetry` _[OpenTelemetryEnvoyProxyAccessLog](#opentelemetryenvoyproxyaccesslog)_ | OpenTelemetry defines the OpenTelemetry accesslog sink. | + + +#### ProxyAccessLogSinkType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + + + +#### ProxyBootstrap + + + +ProxyBootstrap defines Envoy Bootstrap configuration. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `type` _[BootstrapType](#bootstraptype)_ | Type is the type of the bootstrap configuration, it should be either Replace or Merge. If unspecified, it defaults to Replace. | +| `value` _string_ | Value is a YAML string of the bootstrap. | + + +#### ProxyLogComponent + +_Underlying type:_ `string` + +ProxyLogComponent defines a component that supports a configured logging level. + +_Appears in:_ +- [ProxyLogging](#proxylogging) + + + +#### ProxyLogging + + + +ProxyLogging defines logging parameters for managed proxies. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[ProxyLogComponent](#proxylogcomponent), values:[LogLevel](#loglevel))_ | Level is a map of logging level per component, where the component is the key and the log level is the value. If unspecified, defaults to "default: warn". | + + +#### ProxyMetricSink + + + +ProxyMetricSink defines the sink of metrics. Default metrics sink is OpenTelemetry. + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `type` _[MetricSinkType](#metricsinktype)_ | Type defines the metric sink type. EG currently only supports OpenTelemetry. | +| `openTelemetry` _[ProxyOpenTelemetrySink](#proxyopentelemetrysink)_ | OpenTelemetry defines the configuration for OpenTelemetry sink. It's required if the sink type is OpenTelemetry. | + + +#### ProxyMetrics + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `prometheus` _[ProxyPrometheusProvider](#proxyprometheusprovider)_ | Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. | +| `sinks` _[ProxyMetricSink](#proxymetricsink) array_ | Sinks defines the metric sinks where metrics are sent to. | +| `matches` _[StringMatch](#stringmatch) array_ | Matches defines configuration for selecting specific metrics instead of generating all metrics stats that are enabled by default. This helps reduce CPU and memory overhead in Envoy, but eliminating some stats may after critical functionality. Here are the stats that we strongly recommend not disabling: `cluster_manager.warming_clusters`, `cluster..membership_total`,`cluster..membership_healthy`, `cluster..membership_degraded`,reference https://github.com/envoyproxy/envoy/issues/9856, https://github.com/envoyproxy/envoy/issues/14610 | +| `enableVirtualHostStats` _boolean_ | EnableVirtualHostStats enables envoy stat metrics for virtual hosts. | + + +#### ProxyOpenTelemetrySink + + + + + +_Appears in:_ +- [ProxyMetricSink](#proxymetricsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the service hostname. | +| `port` _integer_ | Port defines the port the service is exposed on. | + + +#### ProxyPrometheusProvider + + + + + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable the Prometheus endpoint. | + + +#### ProxyProtocol + + + +ProxyProtocol defines the configuration related to the proxy protocol when communicating with the backend. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `version` _[ProxyProtocolVersion](#proxyprotocolversion)_ | Version of ProxyProtol Valid ProxyProtocolVersion values are "V1" "V2" | + + +#### ProxyProtocolVersion + +_Underlying type:_ `string` + +ProxyProtocolVersion defines the version of the Proxy Protocol to use. + +_Appears in:_ +- [ProxyProtocol](#proxyprotocol) + + + +#### ProxyTelemetry + + + + + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `accessLog` _[ProxyAccessLog](#proxyaccesslog)_ | AccessLogs defines accesslog parameters for managed proxies. If unspecified, will send default format to stdout. | +| `tracing` _[ProxyTracing](#proxytracing)_ | Tracing defines tracing configuration for managed proxies. If unspecified, will not send tracing data. | +| `metrics` _[ProxyMetrics](#proxymetrics)_ | Metrics defines metrics configuration for managed proxies. | + + +#### ProxyTracing + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `samplingRate` _integer_ | SamplingRate controls the rate at which traffic will be selected for tracing if no prior sampling decision has been made. Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. | +| `customTags` _object (keys:string, values:[CustomTag](#customtag))_ | CustomTags defines the custom tags to add to each span. If provider is kubernetes, pod name and namespace are added by default. | +| `provider` _[TracingProvider](#tracingprovider)_ | Provider defines the tracing provider. Only OpenTelemetry is supported currently. | + + +#### RateLimit + + + +RateLimit defines the configuration associated with the Rate Limit Service used for Global Rate Limiting. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `backend` _[RateLimitDatabaseBackend](#ratelimitdatabasebackend)_ | Backend holds the configuration associated with the database backend used by the rate limit service to store state associated with global ratelimiting. | +| `timeout` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ | Timeout specifies the timeout period for the proxy to access the ratelimit server If not set, timeout is 20ms. | +| `failClosed` _boolean_ | FailClosed is a switch used to control the flow of traffic when the response from the ratelimit server cannot be obtained. If FailClosed is false, let the traffic pass, otherwise, don't let the traffic pass and return 500. If not set, FailClosed is False. | + + +#### RateLimitDatabaseBackend + + + +RateLimitDatabaseBackend defines the configuration associated with the database backend used by the rate limit service. + +_Appears in:_ +- [RateLimit](#ratelimit) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitDatabaseBackendType](#ratelimitdatabasebackendtype)_ | Type is the type of database backend to use. Supported types are: * Redis: Connects to a Redis database. | +| `redis` _[RateLimitRedisSettings](#ratelimitredissettings)_ | Redis defines the settings needed to connect to a Redis database. | + + +#### RateLimitDatabaseBackendType + +_Underlying type:_ `string` + +RateLimitDatabaseBackendType specifies the types of database backend to be used by the rate limit service. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + + + +#### RateLimitRedisSettings + + + +RateLimitRedisSettings defines the configuration for connecting to redis database. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + +| Field | Description | +| --- | --- | +| `url` _string_ | URL of the Redis Database. | +| `tls` _[RedisTLSSettings](#redistlssettings)_ | TLS defines TLS configuration for connecting to redis database. | + + +#### RateLimitRule + + + +RateLimitRule defines the semantics for matching attributes from the incoming requests, and setting limits for them. + +_Appears in:_ +- [GlobalRateLimit](#globalratelimit) + +| Field | Description | +| --- | --- | +| `clientSelectors` _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | ClientSelectors holds the list of select conditions to select specific clients using attributes from the traffic flow. All individual select conditions must hold True for this rule and its limit to be applied. If this field is empty, it is equivalent to True, and the limit is applied. | +| `limit` _[RateLimitValue](#ratelimitvalue)_ | Limit holds the rate limit values. This limit is applied for traffic flows when the selectors compute to True, causing the request to be counted towards the limit. The limit is enforced and the request is ratelimited, i.e. a response with 429 HTTP status code is sent back to the client when the selected requests have reached the limit. | + + +#### RateLimitSelectCondition + + + +RateLimitSelectCondition specifies the attributes within the traffic flow that can be used to select a subset of clients to be ratelimited. All the individual conditions must hold True for the overall condition to hold True. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `headers` _[HeaderMatch](#headermatch) array_ | Headers is a list of request headers to match. Multiple header values are ANDed together, meaning, a request MUST match all the specified headers. | +| `sourceCIDR` _[SourceMatch](#sourcematch)_ | SourceCIDR is the client IP Address range to match on. | + + +#### RateLimitSpec + + + +RateLimitSpec defines the desired state of RateLimitSpec. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitType](#ratelimittype)_ | Type decides the scope for the RateLimits. Valid RateLimitType values are "Global". | +| `global` _[GlobalRateLimit](#globalratelimit)_ | Global defines global rate limit configuration. | + + +#### RateLimitType + +_Underlying type:_ `string` + +RateLimitType specifies the types of RateLimiting. + +_Appears in:_ +- [RateLimitSpec](#ratelimitspec) + + + +#### RateLimitUnit + +_Underlying type:_ `string` + +RateLimitUnit specifies the intervals for setting rate limits. Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". + +_Appears in:_ +- [RateLimitValue](#ratelimitvalue) + + + +#### RateLimitValue + + + +RateLimitValue defines the limits for rate limiting. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `requests` _integer_ | | +| `unit` _[RateLimitUnit](#ratelimitunit)_ | | + + +#### RedisTLSSettings + + + +RedisTLSSettings defines the TLS configuration for connecting to redis database. + +_Appears in:_ +- [RateLimitRedisSettings](#ratelimitredissettings) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | CertificateRef defines the client certificate reference for TLS connections. Currently only a Kubernetes Secret of type TLS is supported. | + + +#### RemoteJWKS + + + +RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. + +_Appears in:_ +- [JWTProvider](#jwtprovider) + +| Field | Description | +| --- | --- | +| `uri` _string_ | URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to validate the server certificate. | + + +#### RequestHeaderCustomTag + + + +RequestHeaderCustomTag adds value from request header to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines the name of the request header which to extract the value from. | +| `defaultValue` _string_ | DefaultValue defines the default value to use if the request header is not set. | + + +#### ResourceProviderType + +_Underlying type:_ `string` + +ResourceProviderType defines the types of custom resource providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) + + + +#### SecurityPolicy + + + +SecurityPolicy allows the user to configure various security settings for a Gateway. + +_Appears in:_ +- [SecurityPolicyList](#securitypolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `SecurityPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[SecurityPolicySpec](#securitypolicyspec)_ | Spec defines the desired state of SecurityPolicy. | + + +#### SecurityPolicyList + + + +SecurityPolicyList contains a list of SecurityPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `SecurityPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[SecurityPolicy](#securitypolicy) array_ | | + + +#### SecurityPolicySpec + + + +SecurityPolicySpec defines the desired state of SecurityPolicy. + +_Appears in:_ +- [SecurityPolicy](#securitypolicy) + +| Field | Description | +| --- | --- | +| `targetRef` _[PolicyTargetReferenceWithSectionName](#policytargetreferencewithsectionname)_ | TargetRef is the name of the Gateway resource this policy is being attached to. This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway. TargetRef | +| `cors` _[CORS](#cors)_ | CORS defines the configuration for Cross-Origin Resource Sharing (CORS). | +| `basicAuth` _[BasicAuth](#basicauth)_ | BasicAuth defines the configuration for the HTTP Basic Authentication. | +| `jwt` _[JWT](#jwt)_ | JWT defines the configuration for JSON Web Token (JWT) authentication. | +| `oidc` _[OIDC](#oidc)_ | OIDC defines the configuration for the OpenID Connect (OIDC) authentication. | + + + + +#### ServiceType + +_Underlying type:_ `string` + +ServiceType string describes ingress methods for a service + +_Appears in:_ +- [KubernetesServiceSpec](#kubernetesservicespec) + + + +#### SlowStart + + + +SlowStart defines the configuration related to the slow start load balancer policy. + +_Appears in:_ +- [LoadBalancer](#loadbalancer) + +| Field | Description | +| --- | --- | +| `window` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ | Window defines the duration of the warm up period for newly added host. During slow start window, traffic sent to the newly added hosts will gradually increase. Currently only supports linear growth of traffic. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster-slowstartconfig | + + +#### SourceMatch + + + + + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + + + +#### StringMatch + + + +StringMatch defines how to match any strings. This is a general purpose match condition that can be used by other EG APIs that need to match against a string. + +_Appears in:_ +- [CORS](#cors) +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `type` _[StringMatchType](#stringmatchtype)_ | Type specifies how to match against a string. | +| `value` _string_ | Value specifies the string value that the match must have. | + + +#### StringMatchType + +_Underlying type:_ `string` + +StringMatchType specifies the semantics of how a string value should be compared. Valid MatchType values are "Exact", "Prefix", "Suffix", "RegularExpression". + +_Appears in:_ +- [StringMatch](#stringmatch) + + + +#### TCPKeepalive + + + +TCPKeepalive define the TCP Keepalive configuration. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) +- [ClientTrafficPolicySpec](#clienttrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `probes` _integer_ | The total number of unacknowledged probes to send before deciding the connection is dead. Defaults to 9. | +| `idleTime` _Duration_ | The duration a connection needs to be idle before keep-alive probes start being sent. The duration format is Defaults to `7200s`. | +| `interval` _Duration_ | The duration between keep-alive probes. Defaults to `75s`. | + + +#### TracingProvider + + + + + +_Appears in:_ +- [ProxyTracing](#proxytracing) + +| Field | Description | +| --- | --- | +| `type` _[TracingProviderType](#tracingprovidertype)_ | Type defines the tracing provider type. EG currently only supports OpenTelemetry. | +| `host` _string_ | Host define the provider service hostname. | +| `port` _integer_ | Port defines the port the provider service is exposed on. | + + +#### TracingProviderType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [TracingProvider](#tracingprovider) + + + +#### XDSTranslatorHook + +_Underlying type:_ `string` + +XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support for the xds-translator + +_Appears in:_ +- [XDSTranslatorHooks](#xdstranslatorhooks) + + + +#### XDSTranslatorHooks + + + +XDSTranslatorHooks contains all the pre and post hooks for the xds-translator runner. + +_Appears in:_ +- [ExtensionHooks](#extensionhooks) + +| Field | Description | +| --- | --- | +| `pre` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | +| `post` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | + + diff --git a/site/content/en/latest/contributions/CODEOWNERS.md b/site/content/en/latest/contributions/CODEOWNERS.md new file mode 100644 index 00000000000..63b751abde5 --- /dev/null +++ b/site/content/en/latest/contributions/CODEOWNERS.md @@ -0,0 +1,19 @@ +--- +title: "Maintainers" +description: "This section includes Maintainers of Envoy Gateway." +--- + +## The following maintainers, listed in alphabetical order, own everything + +- @AliceProxy +- @arkodg +- @Xunzhuo +- @zirain +- @qicz + +## Emeritus Maintainers + +- @danehans +- @alexgervais +- @skriss +- @youngnick diff --git a/site/content/en/latest/contributions/CODE_OF_CONDUCT.md b/site/content/en/latest/contributions/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..e19da050dff --- /dev/null +++ b/site/content/en/latest/contributions/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ +--- +title: "Code of Conduct" +description: "This section includes Code of Conduct of Envoy Gateway." +--- + +Envoy Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/site/content/en/latest/contributions/CONTRIBUTING.md b/site/content/en/latest/contributions/CONTRIBUTING.md new file mode 100644 index 00000000000..f94b2c940e9 --- /dev/null +++ b/site/content/en/latest/contributions/CONTRIBUTING.md @@ -0,0 +1,190 @@ +--- +title: "Contributing" +description: "This section tells how to contribute to Envoy Gateway." +weight: 3 +--- + +We welcome contributions from the community. Please carefully review the [project goals](/about) +and following guidelines to streamline your contributions. + +## Communication + +* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no + one else is working on it and ask you to open a GitHub issue. +* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or + changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to + agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process + for major features is also important so that [affiliations with commit access](../codeowners) can + come to agreement on the design. If it's appropriate to write a design document, the document must + be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable + location. +* Small patches and bug fixes don't need prior communication. + +## Inclusivity + +The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere +to the following guidelines for all code, APIs, and documentation: + +* The following words and phrases are not allowed: + * *Whitelist*: use allowlist instead. + * *Blacklist*: use denylist or blocklist instead. + * *Master*: use primary instead. + * *Slave*: use secondary or replica instead. +* Documentation should be written in an inclusive style. The [Google developer + documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent + reference on this topic. +* The above policy is not considered definitive and may be amended in the future as industry best + practices evolve. Additional comments on this topic may be provided by maintainers during code + review. + +## Submitting a PR + +* Fork the repo. +* Hack +* DCO sign-off each commit. This can be done with `git commit -s`. +* Submit your PR. +* Tests will automatically run for you. +* We will **not** merge any PR that is not passing tests. +* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage + build. If your PR cannot have 100% coverage for some reason please clearly explain why when you + open it. +* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/site) folder of the repo as + well as the [changelog](/blog/releases). +* All code comments and documentation are expected to have proper English grammar and punctuation. + If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try + to find some help but there are no guarantees. +* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary + and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. + Examples: + * "docs: fix grammar error" + * "feat(translator): add new feature" + * "fix: fix xx bug" + * "chore: change ci & build tools etc" +* Your PR commit message will be used as the commit message when your PR is merged. You should + update this field if your PR diverges during review. +* Your PR description should have details on what the PR does. If it fixes an existing issue it + should end with "Fixes #XXX". +* If your PR is co-authored or based on an earlier PR from another contributor, + please attribute them with `Co-authored-by: name `. See + GitHub's [multiple author + guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) + for further details. +* When all tests are passing and all other conditions described herein are satisfied, a maintainer + will be assigned to review and merge the PR. +* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits + are new commits and/or merges. We squash and merge so the number of commits you have in the PR + doesn't matter. +* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. + We reserve the right to close PRs that are not making progress. This is generally defined as no + changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. + Closing stale PRs helps us to keep on top of all the work currently in flight. + +## Maintainer PR Review Policy + +* See [CODEOWNERS.md](../codeowners) for the current list of maintainers. +* A maintainer representing a different affiliation from the PR owner is required to review and + approve the PR. +* When the project matures, it is expected that a "domain expert" for the code the PR touches should + review the PR. This person does not require commit access, just domain knowledge. +* The above rules may be waived for PRs which only update docs or comments, or trivial changes to + tests and tools (where trivial is decided by the maintainer in question). +* If there is a question on who should review a PR please discuss in Slack. +* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. +* Please make sure that the PR title, commit message, and description are updated if the PR changes + significantly during review. +* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge + title with the original title, and the commit body with every individual commit from the PR. + The maintainer doing the merge should make sure the title follows the guidelines above and should + overwrite the body with the original commit message from the PR (cleaning it up if necessary) + while preserving the PR author's final DCO sign-off. + +## Decision making + +This is a new and complex project, and we need to make a lot of decisions very quickly. +To this end, we've settled on this process for making (possibly contentious) decisions: + +* For decisions that need a record, we create an issue. +* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. +* Maintainers can cast binding votes on that comment by reacting or replying in another comment. +* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. +* Voting will be resolved by simple majority. +* In the event of deadlocks, the question will be put to steering instead. + +## DCO: Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign-off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +```bash +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` + +## Fixing DCO + +If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best +practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +the commit history to a single commit, append the DCO sign-off as described above, and [force +push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in +your history: + +```bash +git rebase -i HEAD^^ +(interactive squash + DCO append) +git push origin -f +``` + +Note, that in general rewriting history in this way is a hindrance to the review process and this +should only be done to correct a DCO mistake. diff --git a/site/content/en/latest/contributions/DEVELOP.md b/site/content/en/latest/contributions/DEVELOP.md new file mode 100644 index 00000000000..6f82c4a411f --- /dev/null +++ b/site/content/en/latest/contributions/DEVELOP.md @@ -0,0 +1,163 @@ +--- +title: "Developer Guide" +description: "This section tells how to develop Envoy Gateway." +weight: 2 +--- + +Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. + +## Prerequisites + +### go + +* Version: 1.20 +* Installation Guide: https://go.dev/doc/install + +### make + +* Recommended Version: 4.0 or later +* Installation Guide: https://www.gnu.org/software/make + +### docker + +* Optional when you want to build a Docker image or run `make` inside Docker. +* Recommended Version: 20.10.16 +* Installation Guide: https://docs.docker.com/engine/install + +### python3 + +* Need a `python3` program +* Must have a functioning `venv` module; this is part of the standard + library, but some distributions (such as Debian and Ubuntu) replace + it with a stub and require you to install a `python3-venv` package + separately. + +## Quickstart + +* Run `make help` to see all the available targets to build, test and run Envoy Gateway. + +### Building + +* Run `make build` to build all the binaries. +* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. +* Run `make build BINS="egctl"` to build the egctl binary. + +__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. + +### Testing + +* Run `make test` to run the golang tests. + +* Run `make testdata` to generate the golden YAML testdata files. + +### Running Linters + +* Run `make lint` to make sure your code passes all the linter checks. +__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). + +### Building and Pushing the Image + +* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. +* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. + +__Note:__ Replace `IMAGE` with your registry's image name. + +### Deploying Envoy Gateway for Test/Dev + +* Run `make create-cluster` to create a [Kind][] cluster. + +#### Option 1: Use the Latest [gateway-dev][] Image + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` + to use a different image tag. + +#### Option 2: Use a Custom Image + +* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. +* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. + +### Deploying Envoy Gateway in Kubernetes + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to + the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or + tag. +* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. + +__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. + +### Demo Setup + +* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource +(similar to steps outlined in the [Quickstart][] docs) and test the configuration. +* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. + +### Run Gateway API Conformance Tests + +The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the +Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run +`TAG=latest make run-conformance` to run the conformance tests. + +#### On a Linux Host + +* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] + image, and run Gateway API conformance tests. + +#### On a Mac Host + +Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following +workarounds to run conformance tests: + +* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run + `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image + to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to + uninstall Envoy Gateway. +* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. + +__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` +is unspecified, the short SHA of your current branch is used. + +### Debugging the Envoy Config + +An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port +(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. + +Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: + +```shell +export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward the admin interface port: + +```shell +kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 +``` + +Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. + +There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. + +### JWT Testing + +An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] +user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs +verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching +settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure +and is hosted in the repo. + +[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md +[make]: https://www.gnu.org/software/make/ +[Github Actions]: https://docs.github.com/en/actions +[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows +[Kind]: https://kind.sigs.k8s.io/ +[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ +[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ +[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ +[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags +[mac_connect]: https://github.com/chipmk/docker-mac-net-connect +[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[JWT Debugger]: https://jwt.io/ +[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/site/content/en/latest/contributions/DOCS.md b/site/content/en/latest/contributions/DOCS.md new file mode 100644 index 00000000000..ae19953a8b5 --- /dev/null +++ b/site/content/en/latest/contributions/DOCS.md @@ -0,0 +1,69 @@ +--- +title: "Working on Envoy Gateway Docs" +description: "This section tells the development of + Envoy Gateway Documents." +--- + +{{% alert title="Note" color="warning" %}} +We migrated from ***Sphinx*** to ***Hugo*** for Envoy Gateway Documents. + +Read blog: [Welcome to new website!](/blog/2023/10/08/welcome-to-new-website/) +{{% /alert %}} + +The documentation for the Envoy Gateway lives in the `site/content/en` directory. Any +individual document can be written using [Markdown]. + +## Documentation Structure + +We supported the versioned Docs now, the directory name under docs represents +the version of docs. The root of the latest site is in `site/content/en/latest`. +This is probably where to start if you're trying to understand how things fit together. + +Note that the new contents should be added to `site/content/en/latest` and will be cut off at +the next release. The contents under `site/content/en/v0.5.0` are auto-generated, +and usually do not need to make changes to them, unless if you find the current release pages have +some incorrect contents. If so, you should send a PR to update contents both of `site/content/en/latest` +and `site/content/en/v0.5.0`. + +You can access the website which represents the current release in default, +and you can access the website which contains the latest version changes in +[Here][latest-website] or at the footer of the pages. + +## Documentation Workflow + +To work with the docs, just edit Markdown files in `site/content/en/latest`, +then run + +```bash +make docs +``` + +This will create `site/public` with the built HTML pages. You can preview it +by running: + +``` shell +make docs-serve +``` + +If you want to generate a new release version of the docs, like `v0.6.0`, then run + +```bash +make docs-release TAG=v0.6.0 +``` + +This will update the VERSION file at the project root, which records current release version, +and it will be used in the pages version context and binary version output. Also, this will generate +new dir `site/content/en/v0.6.0`, which contains docs at v0.6.0 and updates artifact links to `v0.6.0` +in all files under `site/content/en/v0.6.0/user`, like `quickstart.md`, `http-routing.md` and etc. + +## Publishing Docs + +Whenever docs are pushed to `main`, CI will publish the built docs to GitHub +Pages. For more details, see `.github/workflows/docs.yaml`. + +## Reference + +Go to [Hugo](https://gohugo.io) and [Docsy](https://www.docsy.dev/docs) to learn more. + +[Markdown]: https://daringfireball.net/projects/markdown/syntax +[latest-website]: /latest diff --git a/site/content/en/latest/contributions/RELEASING.md b/site/content/en/latest/contributions/RELEASING.md new file mode 100644 index 00000000000..f81d5be457d --- /dev/null +++ b/site/content/en/latest/contributions/RELEASING.md @@ -0,0 +1,252 @@ +--- +title: "Release Process" +description: "This section tells the release process of Envoy Gateway." +--- + +This document guides maintainers through the process of creating an Envoy Gateway release. + +- [Release Candidate](#release-candidate) +- [Minor Release](#minor-release) +- [Announce the Release](#announce-the-release) + +## Release Candidate + +The following steps should be used for creating a release candidate. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export RELEASE_CANDIDATE_NUMBER=1 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and + the [Build and Test][] has successfully completed. +5. Create a new release branch from `main`. The release branch should be named + `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. Push the branch to the Envoy Gateway repo. + + ```shell + git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Create a topic branch for updating the Envoy proxy image and Envoy Ratelimit image to the tag supported by the release. Reference [PR #2098][] + for additional details on updating the image tag. +8. Sign, commit, and push your changes to your fork. +9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not + proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. +10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' + ``` + +11. Push the tag to the Envoy Gateway repository. + + ```shell + git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} + ``` + +12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +13. Confirm that the [release workflow][] completed successfully. +14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +15. Confirm that the [release][] was created. +16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test + the quickstart steps using the release candidate by manually updating the links. +17. [Generate][] the GitHub changelog. +18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. +19. If you find any bugs in this process, please create an issue. + +### Setup cherry picker action + +After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. + +Configuration looks like following: + +```yaml + cherry_pick_release_v0_4: + runs-on: ubuntu-latest + name: Cherry pick into release-v0.4 + if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cherry pick into release/v0.4 + uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 + with: + branch: release/v0.4 + title: "[release/v0.4] {old_title}" + body: "Cherry picking #{old_pull_request_id} onto release/v0.4" + labels: | + cherrypick/release-v0.4 + # put release manager here + reviewers: | + AliceProxy +``` + +Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. + +## Minor Release + +The following steps should be used for creating a minor release. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. +- A release branch that has been cut from the corresponding release candidate. Refer to the + [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. + + 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release + notes should be an accumulation of the release candidate release notes and any changes since the release + candidate. + 2. Create a release announcement. Refer to [PR #635] as an example release announcement. + 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. + 4. Generate the versioned release docs: + + ``` shell + make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + + 5. Update the `Get Started` and `Contributing` button referred link in `site/content/en/_index.md`: + + ```shell + + Get Started + + + Contributing + + ``` + + 6. Uodate the `Documentation` referred link on the menu in `site/hugo.toml`: + + ```shell + [[menu.main]] + name = "Documentation" + weight = -101 + pre = "" + url = "/v0.5.0" + ``` + +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged + and the [Build and Test][] has completed for your final PR. + +5. Checkout the release branch. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. If the tip of the release branch does not match the tip of `main`, perform the following: + + 1. Create a topic branch from the release branch. + 2. Cherry-pick the commits from `main` that differ from the release branch. + 3. Run tests locally, e.g. `make lint`. + 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. + 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. + 6. Do not proceed until the PR has merged and CI passes for the merged PR. + 7. If you are still on your topic branch, change to the release branch: + + ```shell + git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + + 8. Ensure your local release branch is up-to-date: + + ```shell + git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Tag the head of your release branch with the release tag. For example: + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' + ``` + + __Note:__ The tag version differs from the release branch by including the `.0` patch version. + +8. Push the tag to the Envoy Gateway repository. + + ```shell + git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +10. Confirm that the [release workflow][] completed successfully. +11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +12. Confirm that the [release][] was created. +13. Confirm that the steps in the [Quickstart Guide][] work as expected. +14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: + + ```console + # Release Announcement + + Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] + (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. + ``` + +If you find any bugs in this process, please create an issue. + +## Announce the Release + +It's important that the world knows about the release. Use the following steps to announce the release. + +1. Set the release information in the Envoy Gateway Slack channel. For example: + + ```shell + Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +2. Send a message to the Envoy Gateway Slack channel. For example: + + ```shell + On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway + v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. + Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs + to start using Envoy Gateway. + ... + ``` + + Link to the GitHub release and release announcement page that highlights the release. + +[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes +[Pull Request]: https://github.com/envoyproxy/gateway/pulls +[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md +[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml +[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml +[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml +[image]: https://hub.docker.com/r/envoyproxy/gateway/tags +[release]: https://github.com/envoyproxy/gateway/releases +[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +[PR #635]: https://github.com/envoyproxy/gateway/pull/635 +[PR #2098]: https://github.com/envoyproxy/gateway/pull/2098 +[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 +[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/site/content/en/latest/contributions/_index.md b/site/content/en/latest/contributions/_index.md new file mode 100644 index 00000000000..3255d996472 --- /dev/null +++ b/site/content/en/latest/contributions/_index.md @@ -0,0 +1,5 @@ +--- +title: Get Involved +description: "This section includes contents related to **Contributions**" +weight: 100 +--- diff --git a/site/content/en/latest/contributions/roadmap.md b/site/content/en/latest/contributions/roadmap.md new file mode 100644 index 00000000000..955af2a9623 --- /dev/null +++ b/site/content/en/latest/contributions/roadmap.md @@ -0,0 +1,96 @@ +--- +title: "Roadmap" +weight: -1 +description: "This section records the roadmap of Envoy Gateway." +--- + +This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of +the project. + +## Contributing to the Roadmap + +- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use + case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update + this document accordingly. +- To help with an existing roadmap item, comment on or assign yourself to the associated issue. +- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A + maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be + discussed in an issue or a community meeting before implementing it. + +If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. +Look for issues with the `help wanted` label to get started. + +## Details + +Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific +roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by +contributing to the project. + +`Last Updated: April 2023` + +### [v0.2.0][v0.2.0]: Establish a Solid Foundation + +- Complete the core Envoy Gateway implementation- [Issue #60][60]. +- Establish initial testing, e2e, integration, etc- [Issue #64][64]. +- Establish user and developer project documentation- [Issue #17][17]. +- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. +- Setup a CI/CD pipeline- [Issue #63][63]. + +### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms + +- Support extended Gateway API fields [Issue #707][707]. +- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. +- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. +- Rate Limiting [Issue #670][670]. +- Authentication [Issue #336][336]. + +### [v0.4.0][v0.4.0]: Customizing Envoy Gateway + +- Extending Envoy Gateway control plane [Issue #20][20] +- Helm based installation for Envoy Gateway [Issue #650][650] +- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] +- Configuring xDS Bootstrap [Issue #31][31] + +### [v0.5.0][v0.5.0]: Observability and Scale + +- Observability for data plane [Issue #699][699]. +- Allow users to configure xDS Resources [Issue #24][24]. + +### [v0.6.0][v0.6.0]: Preparation for GA + +- Observability for control plane [Issue #700][700]. +- Compute and document Envoy Gateway performance [Issue #1365][1365]. +- Add TrafficPolicy APIs for advanced features [Issue #1492][1492]. +- Envoy Gateway meets readiness criteria [Issue #1160][1160]. + +[issue]: https://github.com/envoyproxy/gateway/issues +[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing +[pr]: https://github.com/envoyproxy/gateway/compare +[milestones]: https://github.com/envoyproxy/gateway/milestones +[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 +[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 +[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 +[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 +[v0.6.0]: https://github.com/envoyproxy/gateway/milestone/15 +[17]: https://github.com/envoyproxy/gateway/issues/17 +[20]: https://github.com/envoyproxy/gateway/issues/20 +[24]: https://github.com/envoyproxy/gateway/issues/24 +[31]: https://github.com/envoyproxy/gateway/issues/31 +[60]: https://github.com/envoyproxy/gateway/issues/60 +[63]: https://github.com/envoyproxy/gateway/issues/63 +[64]: https://github.com/envoyproxy/gateway/issues/64 +[65]: https://github.com/envoyproxy/gateway/issues/65 +[336]: https://github.com/envoyproxy/gateway/issues/336 +[641]: https://github.com/envoyproxy/gateway/issues/641 +[642]: https://github.com/envoyproxy/gateway/issues/642 +[648]: https://github.com/envoyproxy/gateway/issues/648 +[650]: https://github.com/envoyproxy/gateway/issues/650 +[643]: https://github.com/envoyproxy/gateway/issues/643 +[670]: https://github.com/envoyproxy/gateway/issues/670 +[675]: https://github.com/envoyproxy/gateway/issues/675 +[699]: https://github.com/envoyproxy/gateway/issues/699 +[700]: https://github.com/envoyproxy/gateway/issues/700 +[707]: https://github.com/envoyproxy/gateway/issues/707 +[1160]: https://github.com/envoyproxy/gateway/issues/1160 +[1365]: https://github.com/envoyproxy/gateway/issues/1365 +[1492]: https://github.com/envoyproxy/gateway/issues/1492 diff --git a/site/content/en/latest/design/_index.md b/site/content/en/latest/design/_index.md new file mode 100644 index 00000000000..2820db4c216 --- /dev/null +++ b/site/content/en/latest/design/_index.md @@ -0,0 +1,5 @@ +--- +title: "Design" +weight: 1 +description: This section includes Designs of Envoy Gateway. +--- diff --git a/site/content/en/latest/design/accesslog.md b/site/content/en/latest/design/accesslog.md new file mode 100644 index 00000000000..a229d5f6eff --- /dev/null +++ b/site/content/en/latest/design/accesslog.md @@ -0,0 +1,243 @@ +--- +title: "Observability: Accesslog" +--- + +## Overview + +Envoy supports extensible accesslog to different sinks, File, gRPC etc. Envoy supports customizable access log formats using predefined fields as well as arbitrary HTTP request and response headers. Envoy supports several built-in access log filters and extension filters that are registered at runtime. + +Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels][] for implementers such as Envoy Gateway to expose features. Since accesslog is not covered by `Core` or `Extended` APIs, EG should provide an easy to config access log formats and sinks per `EnvoyProxy`. + +## Goals + +- Support send accesslog to `File` or `OpenTelemetry` backend +- TODO: Support access log filters base on [CEL][] expression + +## Non-Goals + +- Support non-CEL filters, e.g. `status_code_filter`, `response_flag_filter` +- Support [HttpGrpcAccessLogConfig][] or [TcpGrpcAccessLogConfig][] + +## Use-Cases + +- Configure accesslog for a `EnvoyProxy` to `File` +- Configure accesslog for a `EnvoyProxy` to `OpenTelemetry` backend +- Configure multi accesslog providers for a `EnvoyProxy` + +### ProxyAccessLog API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/accesslogging_types.go" +type ProxyAccessLog struct { + // Disable disables access logging for managed proxies if set to true. + Disable bool `json:"disable,omitempty"` + // Settings defines accesslog settings for managed proxies. + // If unspecified, will send default format to stdout. + // +optional + Settings []ProxyAccessLogSetting `json:"settings,omitempty"` +} + +type ProxyAccessLogSetting struct { + // Format defines the format of accesslog. + Format ProxyAccessLogFormat `json:"format"` + // Sinks defines the sinks of accesslog. + // +kubebuilder:validation:MinItems=1 + Sinks []ProxyAccessLogSink `json:"sinks"` +} + +type ProxyAccessLogFormatType string + +const ( + // ProxyAccessLogFormatTypeText defines the text accesslog format. + ProxyAccessLogFormatTypeText ProxyAccessLogFormatType = "Text" + // ProxyAccessLogFormatTypeJSON defines the JSON accesslog format. + ProxyAccessLogFormatTypeJSON ProxyAccessLogFormatType = "JSON" + // TODO: support format type "mix" in the future. +) + +// ProxyAccessLogFormat defines the format of accesslog. +// +union +type ProxyAccessLogFormat struct { + // Type defines the type of accesslog format. + // +kubebuilder:validation:Enum=Text;JSON + // +unionDiscriminator + Type ProxyAccessLogFormatType `json:"type,omitempty"` + // Text defines the text accesslog format, following Envoy accesslog formatting, + // empty value results in proxy's default access log format. + // It's required when the format type is "Text". + // Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. + // The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. + // +optional + Text *string `json:"text,omitempty"` + // JSON is additional attributes that describe the specific event occurrence. + // Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) + // can be used as values for fields within the Struct. + // It's required when the format type is "JSON". + // +optional + JSON map[string]string `json:"json,omitempty"` +} + +type ProxyAccessLogSinkType string + +const ( + // ProxyAccessLogSinkTypeFile defines the file accesslog sink. + ProxyAccessLogSinkTypeFile ProxyAccessLogSinkType = "File" + // ProxyAccessLogSinkTypeOpenTelemetry defines the OpenTelemetry accesslog sink. + ProxyAccessLogSinkTypeOpenTelemetry ProxyAccessLogSinkType = "OpenTelemetry" +) + +type ProxyAccessLogSink struct { + // Type defines the type of accesslog sink. + // +kubebuilder:validation:Enum=File;OpenTelemetry + Type ProxyAccessLogSinkType `json:"type,omitempty"` + // File defines the file accesslog sink. + // +optional + File *FileEnvoyProxyAccessLog `json:"file,omitempty"` + // OpenTelemetry defines the OpenTelemetry accesslog sink. + // +optional + OpenTelemetry *OpenTelemetryEnvoyProxyAccessLog `json:"openTelemetry,omitempty"` +} + +type FileEnvoyProxyAccessLog struct { + // Path defines the file path used to expose envoy access log(e.g. /dev/stdout). + // Empty value disables accesslog. + Path string `json:"path,omitempty"` +} + +// TODO: consider reuse ExtensionService? +type OpenTelemetryEnvoyProxyAccessLog struct { + // Host define the extension service hostname. + Host string `json:"host"` + // Port defines the port the extension service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + // Resources is a set of labels that describe the source of a log entry, including envoy node info. + // It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). + // +optional + Resources map[string]string `json:"resources,omitempty"` + + // TODO: support more OpenTelemetry accesslog options(e.g. TLS, auth etc.) in the future. +} +``` + +### Example + +- The following is an example to disable access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/disable-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: disable-accesslog + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + disable: true +``` + +- The following is an example with text format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/text-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: text-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: File + file: + path: /dev/stdout +``` + +- The following is an example with json format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/json-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: json-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + json: + status: "%RESPONSE_CODE%" + message: "%LOCAL_REPLY_BODY%" + sinks: + - type: File + file: + path: /dev/stdout +``` + +- The following is an example with OpenTelemetry format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/otel-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: otel-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" +``` + +- The following is an example of sending same format to different sinks. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/multi-sinks.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: multi-sinks + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: File + file: + path: /dev/stdout + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" +``` + +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels +[CEL]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/filters/cel/v3/cel.proto#extension-envoy-access-loggers-extension-filters-cel +[HttpGrpcAccessLogConfig]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-httpgrpcaccesslogconfig +[TcpGrpcAccessLogConfig]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-tcpgrpcaccesslogconfig diff --git a/site/content/en/latest/design/backend-traffic-policy.md b/site/content/en/latest/design/backend-traffic-policy.md new file mode 100644 index 00000000000..9411ef20978 --- /dev/null +++ b/site/content/en/latest/design/backend-traffic-policy.md @@ -0,0 +1,156 @@ +--- +title: "BackendTrafficPolicy" +--- + +## Overview + +This design document introduces the `BackendTrafficPolicy` API allowing users to configure +the behavior for how the Envoy Proxy server communicates with upstream backend services/endpoints. + +## Goals + +- Add an API definition to hold settings for configuring behavior of the connection between the backend services +and Envoy Proxy listener. + +## Non Goals + +- Define the API configuration fields in this API. + +## Implementation + +`BackendTrafficPolicy` is an implied hierarchy type API that can be used to extend [Gateway API][]. +It can target either a `Gateway`, or an xRoute (`HTTPRoute`/`GRPCRoute`/etc.). When targeting a `Gateway`, +it will apply the configured settings within ght `BackendTrafficPolicy` to all children xRoute resources of that `Gateway`. +If a `BackendTrafficPolicy` targets an xRoute and a different `BackendTrafficPolicy` targets the `Gateway` that route belongs to, +then the configuration from the policy that is targeting the xRoute resource will win in a conflict. + +### Example + +Here is an example highlighting how a user can configure this API. + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: ipv4-route + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.foo.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: ipv4-service + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: ipv6-route + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.bar.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: ipv6-service + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: default-ipv-policy + namespace: default +spec: + protocols: + enableIPv6: false + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ipv6-support-policy + namespace: default +spec: + protocols: + enableIPv6: true + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: ipv6-route + namespace: default +``` + +## Features / API Fields + +Here is a list of some features that can be included in this API. Note that this list is not exhaustive. + +- Protocol configuration +- Circuit breaking +- Retries +- Keep alive probes +- Health checking +- Load balancing +- Rate limit + +## Design Decisions + +- This API will only support a single `targetRef` and can bind to only a `Gateway` or xRoute (`HTTPRoute`/`GRPCRoute`/etc.) resource. +- This API resource MUST be part of same namespace as the resource it targets. +- There can be only be ONE policy resource attached to a specific `Listener` (section) within a `Gateway` +- If the policy targets a resource but cannot attach to it, this information should be reflected +in the Policy Status field using the `Conflicted=True` condition. +- If multiple polices target the same resource, the oldest resource (based on creation timestamp) will +attach to the Gateway Listeners, the others will not. +- If Policy A has a `targetRef` that includes a `sectionName` i.e. +it targets a specific Listener within a `Gateway` and Policy B has a `targetRef` that targets the same +entire Gateway then + - Policy A will be applied/attached to the specific Listener defined in the `targetRef.SectionName` + - Policy B will be applied to the remaining Listeners within the Gateway. Policy B will have an additional + status condition `Overridden=True`. + +## Alternatives + +- The project can indefintely wait for these configuration parameters to be part of the [Gateway API][]. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/latest/design/bootstrap.md b/site/content/en/latest/design/bootstrap.md new file mode 100644 index 00000000000..c0581347a24 --- /dev/null +++ b/site/content/en/latest/design/bootstrap.md @@ -0,0 +1,381 @@ +--- +title: "Bootstrap Design" +--- + +## Overview + +[Issue 31][] specifies the need for allowing advanced users to specify their custom +Envoy Bootstrap configuration rather than using the default Bootstrap configuration +defined in Envoy Gateway. This allows advanced users to extend Envoy Gateway and +support their custom use cases such setting up tracing and stats configuration +that is not supported by Envoy Gateway. + +## Goals + +* Define an API field to allow a user to specify a custom Bootstrap +* Provide tooling to allow the user to generate the default Bootstrap configuration + as well as validate their custom Bootstrap. + +## Non Goals + +* Allow user to configure only a section of the Bootstrap + +## API + +Leverage the existing [EnvoyProxy][] resource which can be attached to the [GatewayClass][] using +the [parametersRef][] field, and define a `Bootstrap` field within the resource. If this field is set, +the value is used as the Bootstrap configuration for all managed Envoy Proxies created by Envoy Gateway. + +```go +// EnvoyProxySpec defines the desired state of EnvoyProxy. +type EnvoyProxySpec struct { + ...... + // Bootstrap defines the Envoy Bootstrap as a YAML string. + // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + // to learn more about the syntax. + // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration + // set by Envoy Gateway. + // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources + // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. + // Backward compatibility across minor versions is not guaranteed. + // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default + // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. + // + // +optional + Bootstrap *string `json:"bootstrap,omitempty"` +} +``` + +## Tooling + +A CLI tool `egctl x translate` will be provided to the user to help generate a working Bootstrap configuration. +Here is an example where a user inputs a `GatewayClass` and the CLI generates the `EnvoyProxy` resource with the `Bootstrap` field populated. + +``` +cat < /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: {} +EOF +``` + +This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. + +The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: + foo: bar +EOF +``` + +__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for +illustration purposes only. + +The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the +File provider: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: File + file: + foo: bar +EOF +``` + +__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration +purposes only. + +Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use +default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to +manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: foo +EOF +``` + +With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: + +```shell +$ ./envoy-gateway +``` + +## Data Plane API + +The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. +Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through +`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure +configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: + +* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane + infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. +* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: + > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the + > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are + > not propagated down to existing Gateways. + +The initial `EnvoyProxy` API: + +```go +// gateway/api/config/v1alpha1/envoyproxy.go + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyProxy is the Schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvoyProxySpec `json:"spec,omitempty"` + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure +// configuration. +type EnvoyProxySpec struct { + // Undefined by this design spec. +} + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. +type EnvoyProxyStatus struct { + // Undefined by this design spec. +} +``` + +The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use +cases are better understood. + +### Data Plane Configuration + +GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is +running with the Kubernetes provider. + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration +parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening +on port 80. + +The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer +service: + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + name: example-config + group: gateway.envoyproxy.io + kind: EnvoyProxy +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: example-config +spec: + networkPublishing: + type: ClusterIPService +``` + +__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. + +[issue_51]: https://github.com/envoyproxy/gateway/issues/51 +[design_doc]: ../system-design/ +[gw_api]: https://gateway-api.sigs.k8s.io/ +[gc]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.GatewayClass +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/site/content/en/latest/design/eg-metrics.md b/site/content/en/latest/design/eg-metrics.md new file mode 100644 index 00000000000..0ca0e7229ec --- /dev/null +++ b/site/content/en/latest/design/eg-metrics.md @@ -0,0 +1,233 @@ +--- +date: 2023-10-10 +title: "Control Plane Observability: Metrics" +--- + +This document aims to cover all aspects of envoy gateway control plane metrics observability. + +{{% alert title="Note" color="secondary" %}} +**Data plane** observability (while important) is outside of scope for this document. For dataplane observability, refer to [here](../metrics). +{{% /alert %}} + +## Current State + +At present, the Envoy Gateway control plane provides logs and controller-runtime metrics, without traces. Logs are managed through our proprietary library (`internal/logging`, a shim to `zap`) and are written to `/dev/stdout`. + +## Goals + +Our objectives include: + ++ Supporting **PULL** mode for Prometheus metrics and exposing these metrics on the admin address. ++ Supporting **PUSH** mode for Prometheus metrics, thereby sending metrics to the Open Telemetry Stats sink via gRPC or HTTP. + +## Non-Goals + +Our non-goals include: + ++ Supporting other stats sinks. + +## Use-Cases + +The use-cases include: + ++ Exposing Prometheus metrics in the Envoy Gateway Control Plane. ++ Pushing Envoy Gateway Control Plane metrics via the Open Telemetry Sink. + +## Design + +### Standards + +Our metrics, will be built upon the [OpenTelemetry][] standards. All metrics will be configured via the [OpenTelemetry SDK][], which offers neutral libraries that can be connected to various backends. + +This approach allows the Envoy Gateway code to concentrate on the crucial aspect - generating the metrics - and delegate all other tasks to systems designed for telemetry ingestion. + +### Attributes + +OpenTelemetry defines a set of [Semantic Conventions][], including [Kubernetes specific ones][]. + +These attributes can be expressed in logs (as keys of structured logs), traces (as attributes), and metrics (as labels). + +We aim to use attributes consistently where applicable. Where possible, these should adhere to codified Semantic Conventions; when not possible, they should maintain consistency across the project. + +### Extensibility + +Envoy Gateway supports both **PULL/PUSH** mode metrics, with Metrics exported via Prometheus by default. + +Additionally, Envoy Gateway can export metrics using both the [OTEL gRPC metrics exporter][] and [OTEL HTTP metrics exporter][], which pushes metrics by grpc/http to a remote OTEL collector. + +Users can extend these in two ways: + +#### Downstream Collection + +Based on the exported data, other tools can collect, process, and export telemetry as needed. Some examples include: + ++ Metrics in **PULL** mode: The OTEL collector can scrape Prometheus and export to X. ++ Metrics in **PUSH** mode: The OTEL collector can receive OTEL gRPC/HTTP exporter metrics and export to X. + +While the examples above involve OTEL collectors, there are numerous other systems available. + +#### Vendor extensions + +The OTEL libraries allow for the registration of Providers/Handlers. While we will offer the default ones (PULL via Prometheus, PUSH via OTEL HTTP metrics exporter) mentioned in Envoy Gateway's extensibility, we can easily allow custom builds of Envoy Gateway to plug in alternatives if the default options don't meet their needs. + +For instance, users may prefer to write metrics over the OTLP gRPC metrics exporter instead of the HTTP metrics exporter. This is perfectly acceptable -- and almost impossible to prevent. The OTEL has ways to register their providers/exporters, and Envoy Gateway can ensure its usage is such that it's not overly difficult to swap out a different provider/exporter. + +### Stability + +Observability is, in essence, a user-facing API. Its primary purpose is to be consumed - by both humans and tooling. Therefore, having well-defined guarantees around their formats is crucial. + +Please note that this refers only to the contents of the telemetry - what we emit, the names of things, semantics, etc. Other settings like Prometheus vs OTLP, JSON vs plaintext, logging levels, etc., are not considered. + +I propose the following: + +#### Metrics + +Metrics offer the greatest potential for providing guarantees. They often directly influence alerts and dashboards, making changes highly impactful. This contrasts with traces and logs, which are often used for ad-hoc analysis, where minor changes to information can be easily understood by a human. + +Moreover, there is precedent for this: [Kubernetes Metrics Lifecycle][] has well-defined processes, and Envoy Gateway's dataplane (Envoy Proxy) metrics are de facto stable. + +Currently, all Envoy Gateway metrics lack defined stability. I suggest we categorize all existing metrics as either: + ++ ***Deprecated***: a metric that is intended to be phased out. ++ ***Experimental***: a metric that is off by default. ++ ***Alpha***: a metric that is on by default. + +We should aim to promote a core set of metrics to **Stable** within a few releases. + +## Envoy Gateway API Types + +New APIs will be added to Envoy Gateway config, which are used to manage Control Plane Telemetry bootstrap configs. + +### EnvoyGatewayTelemetry + +``` go +// EnvoyGatewayTelemetry defines telemetry configurations for envoy gateway control plane. +// Control plane will focus on metrics observability telemetry and tracing telemetry later. +type EnvoyGatewayTelemetry struct { + // Metrics defines metrics configuration for envoy gateway. + Metrics *EnvoyGatewayMetrics `json:"metrics,omitempty"` +} +``` + +### EnvoyGatewayMetrics + +> Prometheus will be exposed on 0.0.0.0:19001, which is not supported to be configured yet. + +``` go +// EnvoyGatewayMetrics defines control plane push/pull metrics configurations. +type EnvoyGatewayMetrics struct { + // Sinks defines the metric sinks where metrics are sent to. + Sinks []EnvoyGatewayMetricSink `json:"sinks,omitempty"` + // Prometheus defines the configuration for prometheus endpoint. + Prometheus *EnvoyGatewayPrometheusProvider `json:"prometheus,omitempty"` +} + +// EnvoyGatewayMetricSink defines control plane +// metric sinks where metrics are sent to. +type EnvoyGatewayMetricSink struct { + // Type defines the metric sink type. + // EG control plane currently supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + OpenTelemetry *EnvoyGatewayOpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type EnvoyGatewayOpenTelemetrySink struct { + // Host define the sink service hostname. + Host string `json:"host"` + // Protocol define the sink service protocol. + // +kubebuilder:validation:Enum=grpc;http + Protocol string `json:"protocol"` + // Port defines the port the sink service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +// EnvoyGatewayPrometheusProvider will expose prometheus endpoint in pull mode. +type EnvoyGatewayPrometheusProvider struct { + // Disable defines if disables the prometheus metrics in pull mode. + // + Disable bool `json:"disable,omitempty"` +} + +``` + +#### Example + ++ The following is an example to disable prometheus metric. + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: null + default: info +provider: + type: Kubernetes +telemetry: + metrics: + prometheus: + disable: true +``` + ++ The following is an example to send metric via Open Telemetry sink to OTEL gRPC Collector. + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: null + default: info +provider: + type: Kubernetes +telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: grpc +``` + ++ The following is an example to disable prometheus metric and send metric via Open Telemetry sink to OTEL HTTP Collector at the same time. + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: null + default: info +provider: + type: Kubernetes +telemetry: + metrics: + prometheus: + disable: false + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4318 + protocol: http +``` + +[OpenTelemetry]: https://opentelemetry.io/ +[OpenTelemetry SDK]: https://opentelemetry.io/docs/specs/otel/metrics/sdk/ +[Semantic Conventions]: https://opentelemetry.io/docs/concepts/semantic-conventions/ +[Kubernetes specific ones]: https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/k8s/ +[OTEL gRPC metrics exporter]: https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/otlp/#general +[OTEL HTTP metrics exporter]: https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/otlp/#general +[Kubernetes Metrics Lifecycle]: https://kubernetes.io/docs/concepts/cluster-administration/system-metrics/#metric-lifecycle diff --git a/site/content/en/latest/design/egctl.md b/site/content/en/latest/design/egctl.md new file mode 100644 index 00000000000..0f67d99f100 --- /dev/null +++ b/site/content/en/latest/design/egctl.md @@ -0,0 +1,59 @@ +--- +title: "egctl Design" +--- + +## Motivation + +EG should provide a command line tool with following capabilities: + +- Collect configuration from envoy proxy and gateway +- Analyse system configuration to diagnose any issues in envoy gateway + +This tool is named `egctl`. + +## Syntax + +Use the following syntax to run `egctl` commands from your terminal window: + +```console +egctl [command] [entity] [name] [flags] +``` + +where `command`, `name`, and `flags` are: + +* `command`: Specifies the operation that you want to perform on one or more resources, + for example `config`, `version`. + +* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. + +* `name`: Specifies the name of the specified instance. + +* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. + +If you need help, run `egctl help` from the terminal window. + +## Operation + +The following table includes short descriptions and the general syntax for all the `egctl` operations: + +| Operation | Syntax | Description | +| --------------| -------------------------------- | -------------------------------------------------------------------------------------| +| `version` | `egctl version` | Prints out build version information. | +| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | +| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | +| `experimental`| `egctl experimental` | Subcommand for experimental features. These do not guarantee backwards compatibility | + +## Examples + +Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: + +```console +# Retrieve all information about proxy configuration from envoy +egctl config envoy-proxy all + +# Retrieve listener information about proxy configuration from envoy +egctl config envoy-proxy listener + +# Retrieve information about envoy gateway +egctl config envoy-gateway +``` diff --git a/site/content/en/latest/design/envoy-patch-policy.md b/site/content/en/latest/design/envoy-patch-policy.md new file mode 100644 index 00000000000..47422b0baa3 --- /dev/null +++ b/site/content/en/latest/design/envoy-patch-policy.md @@ -0,0 +1,176 @@ +--- +title: "EnvoyPatchPolicy" +--- + +## Overview + +This design introduces the `EnvoyPatchPolicy` API allowing users to modify the generated Envoy xDS Configuration +that Envoy Gateway generates before sending it to Envoy Proxy. + +Envoy Gateway allows users to configure networking and security intent using the +upstream [Gateway API][] as well as implementation specific [Extension APIs][] defined in this project +to provide a more batteries included experience for application developers. +* These APIs are an abstracted version of the underlying Envoy xDS API to provide a better user experience for the application developer, exposing and setting only a subset of the fields for a specific feature, sometimes in a opinionated way (e.g [RateLimit][]) +* These APIs do not expose all the features capabilities that Envoy has either because these features are desired but the API +is not defined yet or the project cannot support such an extensive list of features. +To alleviate this problem, and provide an interim solution for a small section of advanced users who are well versed in +Envoy xDS API and its capabilities, this API is being introduced. + +## Goals +* Add an API allowing users to modify the generated xDS Configuration + +## Non Goals +* Support multiple patch mechanisims + +## Implementation +`EnvoyPatchPolicy` is a [Direct Policy Attachment][] type API that can be used to extend [Gateway API][] +Modifications to the generated xDS configuration can be provided as a JSON Patch which is defined in +[RFC 6902][]. This patching mechanism has been adopted in [Kubernetes][] as well as [Kustomize][] to update +resource objects. + +### Example +Here is an example highlighting how a user can configure global ratelimiting using an external rate limit service using this API. + +``` +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + name: ratelimit-patch-policy + namespace: default +spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + # The listener name is of the form // + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eag-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + # The route name is of the form // + name: default/eg/http + operation: + op: add + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 +``` + + +## Verification +* Offline - Leverage [egctl x translate][] to ensure that the `EnvoyPatchPolicy` can be successfully applied and the desired +output xDS is created. +* Runtime - Use the `Status` field within `EnvoyPatchPolicy` to highlight whether the patch was applied successfully or not. + +## State of the World +* Istio - Supports the [EnvoyFilter][] API which allows users to customize the output xDS using patches and proto based merge +semantics. + +## Design Decisions +* This API will only support a single `targetRef` and can bind to only a `Gateway` resource. This simplifies reasoning of how +patches will work. +* This API will always be an experimental API and cannot be graduated into a stable API because Envoy Gateway cannot garuntee + * that the naming scheme for the generated resources names will not change across releases + * that the underlying Envoy Proxy API will not change across releases +* This API needs to be explicitly enabled using the [EnvoyGateway][] API + +## Open Questions +* Should the value only support JSON or YAML as well (which is a JSON superset) ? + +## Alternatives +* Users can customize the Envoy [Bootstrap configuration using EnvoyProxy API][] and provide static xDS configuration. +* Users can extend functionality by [Extending the Control Plane][] and adding gRPC hooks to modify the generated xDS configuration. + + + +[Direct Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/#direct-policy-attachment +[RFC 6902]: https://datatracker.ietf.org/doc/html/rfc6902 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[Kubernetes]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ +[Kustomize]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md +[Extension APIs]: ../../api/extension_types/ +[RateLimit]: ../../user/rate-limit/ +[EnvoyGateway]: ../../api/extension_types/#envoygateway +[Extending the Control Plane]: ../extending-envoy-gateway +[EnvoyFilter]: https://istio.io/latest/docs/reference/config/networking/envoy-filter +[egctl x translate]: ../../user/egctl#egctl-experimental-translate +[Bootstrap configuration using EnvoyProxy API]: ../../user/customize-envoyproxy#customize-envoyproxy-bootstrap-config diff --git a/site/content/en/latest/design/extending-envoy-gateway.md b/site/content/en/latest/design/extending-envoy-gateway.md new file mode 100644 index 00000000000..cb2127f3869 --- /dev/null +++ b/site/content/en/latest/design/extending-envoy-gateway.md @@ -0,0 +1,327 @@ +--- +title: "Envoy Gateway Extensions Design" +--- + +As outlined in the [official goals][] for the Envoy Gateway project, one of the main goals is to "provide a common foundation for vendors to build value-added products +without having to re-engineer fundamental interactions". Development of the Envoy Gateway project has been focused on developing the core features for the project and +Kubernetes Gateway API conformance. This system focuses on the “common foundation for vendors” component by introducing a way for vendors to extend Envoy Gateway. + +To meaningfully extend Envoy Gateway and provide additional features, Extensions need to be able to introduce their own custom resources and have a high level of control +over the configuration generated by Envoy Gateway. Simply applying some static xDS configuration patches or relying on the existing Gateway API resources are both insufficient on their own +as means to add larger features that require dynamic user-configuration. + +As an example, an extension developer may wish to provide their own out-of-the-box authentication filters that require configuration from the end-user. This is a scenario where the ability to introduce +custom resources and attach them to [HTTPRoute][]s as an [ExtensionRef][] is necessary. Providing the same feature through a series of xDS patch resources would be too cumbersome for many end-users that want to avoid +that level of complexity when managing their clusters. + +## Goals + +- Provide a foundation for extending the Envoy Gateway control plane +- Allow Extension Developers to introduce their own custom resources for extending the Gateway-API via [ExtensionRefs][], [policyAttachments][] (future) and [backendRefs][] (future). +- Extension developers should **NOT** have to maintain a custom fork of Envoy Gateway +- Provide a system for extending Envoy Gateway which allows extension projects to ship updates independent of Envoy Gateway's release schedule +- Modify the generated Envoy xDS config +- Setup a foundation for the initial iteration of Extending Envoy Gateway +- Allow an Extension to hook into the infra manager pipeline (future) + +## Non-Goals + +- The initial design does not capture every hook that Envoy Gateway will eventually support. +- Extend [Gateway API Policy Attachments][]. At some point, these will be addressed using this extension system, but the initial implementation omits these. +- Support multiple extensions at the same time. Due to the fact that extensions will be modifying xDS resources after they are generated, handling the order of extension execution for each individual hook point is a challenge. Additionally, there is no +real way to prevent one extension from overwriting or breaking modifications to xDS resources that were made by another extension that was executed first. + +## Overview + +Envoy Gateway can be extended by vendors by means of an extension server developed by the vendor and deployed alongside Envoy Gateway. +An extension server can make use of one or more pre/post hooks inside Envoy Gateway before and after its major components (translator, etc.) to allow the extension to modify the data going into or coming out of these components. +An extension can be created external to Envoy Gateway as its own Kubernetes deployment or loaded as a sidecar. gRPC is used for the calls between Envoy Gateway and an extension. In the hook call, Envoy Gateway sends data as well +as context information to the extension and expects a reply with a modified version of the data that was sent to the extension. Since extensions fundamentally alter the logic and data that Envoy Gateway provides, Extension projects assume responsibility for any bugs and issues +they create as a direct result of their modification of Envoy Gateway. + +## Diagram + +![Architecture](/img/extension-example.png) + +## Registering Extensions in Envoy Gateway + +Information about the extension that Envoy Gateway needs to load is configured in the Envoy Gateway config. + +An example configuration: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +extensionManager: + resources: + - group: example.myextension.io + version: v2 + kind: OAuth2Filter + hooks: + xdsTranslator: + post: + - Route + - VirtualHost + - HTTPListener + - Translation + service: + host: my-extension.example + port: 443 + tls: + certificateRef: + name: my-secret + namespace: default +``` + +An extension must supply connection information in the `extension.service` field so that Envoy Gateway can communicate with the extension. The `tls` configuration is optional. + +If the extension wants Envoy Gateway to watch resources for it then the extension must configure the optional `extension.resources` field and supply a list of: + +- `group`: the API group of the resource +- `version`: the API version of the resource +- `kind`: the Kind of resource + +The extension can configure the `extensionManager.hooks` field to specify which hook points it would like to support. If a given hook is not listed here then it will not be executed even +if the extension is configured properly. This allows extension developers to only opt-in to the hook points they want to make use of. + +This configuration is required to be provided at bootstrap and modifying the registered extension during runtime is not currently supported. +Envoy Gateway will keep track of the registered extension and its API `groups` and `kinds` when processing Gateway API resources. + +## Extending Gateway API and the Data Plane + +Envoy Gateway manages [Envoy][] deployments, which act as the data plane that handles actual user traffic. Users configure the data plane using the K8s Gateway API resources which Envoy +Gateway converts into [Envoy specific configuration (xDS)][] to send over to Envoy. + +Gateway API offers [ExtensionRef filters][] and [Policy Attachments][] as extension points for implementers to use. Envoy Gateway extends the Gateway API using these extension points to provide support for [rate limiting][] +and [authentication][] native to the project. The initial design of Envoy Gateway extensions will primarily focus on ExtensionRef filters so that extension developers can reference their own resources as HTTP Filters in the same way +that Envoy Gateway has native support for rate limiting and authentication filters. + +When Envoy Gateway encounters an [HTTPRoute][] or [GRPCRoute][] that has an `ExtensionRef` `filter` with a `group` and `kind` that Envoy Gateway does not support, it will first +check the registered extension to determine if it supports the referenced object before considering it a configuration error. + +This allows users to be able to reference additional filters provided by their Envoy Gateway Extension, in their `HTTPRoute`s / `GRPCRoute`s: + +```yaml +apiVersion: example.myextension.io/v1alpha1 +kind: OAuth2Filter +metadata: + name: oauth2-filter +spec: + ... + +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - clientSelectors: + - path: + type: PathPrefix + value: / + filters: + - type: ExtensionRef + extensionRef: + group: example.myextension.io + kind: OAuth2Filter + name: oauth2-filter + backendRefs: + - name: backend + port: 3000 +``` + +In order to enable the usage of new resources introduced by an extension for translation and xDS modification, Envoy Gateway provides hook points within the translation pipeline, where it calls out to the extension service registered in the [EnvoyGateway config][] +if they specify an `group` that matches the `group` of an `ExtensionRef` filter. The extension will then be able to modify the xDS that Envoy Gateway generated and send back the +modified configuration. If an extension is not registered or if the registered extension does not specify support for the `group` of an `ExtensionRef` filter then Envoy Gateway will treat it as an unknown resource +and provide an error to the user. + +**Note:** Currently (as of [v1][]) Gateway API does not provide a means to specify the namespace or version of an object referenced as an `ExtensionRef`. The extension mechanism will assume that +the namespace of any `ExtensionRef` is the same as the namespace of the `HTTPRoute` or `GRPCRoute` it is attached to rather than treating the `name` field of an `ExtensionRef` as a `name.namespace` string. +If Gateway API adds support for these fields then the design of the Envoy Gateway extensions will be updated to support them. + +## Watching New Resources + +Envoy Gateway will dynamically create new watches on resources introduced by the registered Extension. It does so by using the [controller-runtime][] to create new watches on [Unstructured][] resources that match the `version`s, `group`s, and `kind`s that the +registered extension configured. When communicating with an extension, Envoy Gateway sends these Unstructured resources over to the extension. This eliminates the need for the extension to create its own watches which would have a strong chance of creating race conditions and reconciliation loops when resources change. When an extension receives the Unstructured resources from Envoy Gateway it can perform its own type validation on them. Currently we make the simplifying assumption that the registered extension's `Kinds` are filters referenced by `extensionRef` in `HTTPRouteFilter`s . Support for Policy attachments will be introduced at a later time. + +## xDS Hooks API + +Envoy Gateway supports the following hooks as the initial foundation of the Extension system. Additional hooks can be developed using this extension system at a later point as new use-cases and needs are discovered. The primary iteration of the extension hooks +focuses solely on the modification of xDS resources. + +### Route Modification Hook + +The [Route][] level Hook provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized. +Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the +Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to ext_authz filters. +The Post Route Modify hook also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that created this xDS route +This hook is always executed when an extension is loaded that has added `Route` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`, and only on Routes which were generated from an HTTPRoute that uses extension resources as externalRef filters. + +```go +// PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified +message PostRouteModifyRequest { + envoy.config.route.v3.Route route = 1; + PostRouteExtensionContext post_route_context = 2; +} + +// RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway +// additional context information can be added to this message as more use-cases are discovered +message PostRouteExtensionContext { + // Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute + repeated ExtensionResource extension_resources = 1; + + // hostnames are the fully qualified domain names attached to the HTTPRoute + repeated string hostnames = 2; +} + +// ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter +// extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension +// can marshal the bytes from this resource back into an unstructured.Unstructured and then +// perform type checking to obtain the resource. +message ExtensionResource { + bytes unstructured_bytes = 1; +} + +// PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent +// If an extension returns a nil Route then it will not be modified +message PostRouteModifyResponse { + envoy.config.route.v3.Route route = 1; +} +``` + +### VirtualHost Modification Hook + +The [VirtualHost][] Hook provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized. +An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway. +This hook is always executed when an extension is loaded that has added `VirtualHost` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. +An extension may return nil to not make any changes to the VirtualHost. + +```protobuf +// PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified +message PostVirtualHostModifyRequest { + envoy.config.route.v3.VirtualHost virtual_host = 1; + PostVirtualHostExtensionContext post_virtual_host_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostVirtualHostExtensionContext {} + +// PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent +// If an extension returns a nil Virtual Host then it will not be modified +message PostVirtualHostModifyResponse { + envoy.config.route.v3.VirtualHost virtual_host = 1; +} +``` + +### HTTP Listener Modification Hook + +The HTTP [Listener][] modification hook is the broadest xDS modification Hook available and allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized. +This hook is always executed when an extension is loaded that has added `HTTPListener` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. An extension may return nil +in order to not make any changes to the Listener. + +```protobuf +// PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified +message PostHTTPListenerModifyRequest { + envoy.config.listener.v3.Listener listener = 1; + PostHTTPListenerExtensionContext post_listener_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostHTTPListenerExtensionContext {} + +// PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent +// If an extension returns a nil Listener then it will not be modified +message PostHTTPListenerModifyResponse { + envoy.config.listener.v3.Listener listener = 1; +} +``` + +### Post xDS Translation Modify Hook + +The Post Translate Modify hook allows an extension to modify the clusters and secrets in the xDS config. +This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than +using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed. +An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension. +The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets +This hook is always executed when an extension is loaded that has added `Translation` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. + +```protobuf +// PostTranslateModifyRequest currently sends only clusters and secrets to an extension. +// The extension is free to add/modify/remove the resources it received. +message PostTranslateModifyRequest { + PostTranslateExtensionContext post_translate_context = 1; + repeated envoy.config.cluster.v3.Cluster clusters = 2; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3; +} + +// PostTranslateModifyResponse is the expected response from an extension and contains +// the full list of xDS clusters and secrets to be used for the xDS config. +message PostTranslateModifyResponse { + repeated envoy.config.cluster.v3.Cluster clusters = 1; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 2; +} +``` + +### Extension Service + +Currently, an extension must implement all of the following hooks although it may return the input(s) it received +if no modification of the resource is desired. A future expansion of the extension hooks will allow an Extension to specify +with config which Hooks it would like to "subscribe" to and which Hooks it does not wish to support. These specific Hooks were chosen +in order to provide extensions with the ability to have both broad and specific control over xDS resources and to minimize the amount of data being sent. + +```protobuf +service EnvoyGatewayExtension { + rpc PostRouteModify (PostRouteModifyRequest) returns (PostRouteModifyResponse) {}; + rpc PostVirtualHostModify(PostVirtualHostModifyRequest) returns (PostVirtualHostModifyResponse) {}; + rpc PostHTTPListenerModify(PostHTTPListenerModifyRequest) returns (PostHTTPListenerModifyResponse) {}; + rpc PostTranslateModify(PostTranslateModifyRequest) returns (PostTranslateModifyResponse) {}; +} +``` + +## Design Decisions + +- Envoy Gateway watches new custom resources introduced by a loaded extension and passes the resources back to the extension when they are used. + - This decision was made to solve the problem about how resources introduced by an extension get watched. If an extension server watches its own resources then it would need some way to trigger an Envoy Gateway reconfigure when a resource that Envoy Gateway is not watching gets updated. Having Envoy Gateway watch all resources removes any concern about creating race confitions or reconcile loops that would result from Envoy Gateway and the extension server both having so much separate state that needs to be synchronized. +- The Extension Server takes ownership of producing the correct xDS configuration in the hook responses +- The Extension Server will be responsible for ensuring the performance of the hook processing time +- The Post xDS level gRPC hooks all currently send a context field even though it contains nothing for several hooks. These fields exist so that they can be updadated in the future to pass +additional information to extensions as new use-cases and needs are discovered. +- The initial design supplies the scaffolding for both "pre xDS" and "post xDS" hooks. Only the post hooks are currently implemented which operate on xDS resources after they have been generated. +The pre hooks will be implemented at a later date along with one or more hooks in the infra manager. The infra manager level hook(s) will exist to power use-cases such as dynamically creating Deployments/Services for the extension the +whenever Envoy Gateway creates an instance of Envoy Proxy. An extension developer might want to take advantage of this functionality to inject a new authorization service as a sidecar on the Envoy Proxy deployment for reduced latency. +- Multiple extensions are not be supported at the same time. Preventing conflict between multiple extensions that are mangling xDS resources is too difficult to ensure compatibility with and is likely to only generate issues. + +## Known Challenges + +Extending Envoy Gateway by using an external extension server which makes use of hook points in Envoy Gateway does comes with a few trade-offs. One known trade-off is the impact of the time that it takes for the hook calls to be executed. Since an extension would make use of hook points in Envoy Gateway that use gRPC for communication, the time it takes to perform these requests could become a concern for some extension developers. One way to minimize the request time of the hook calls is to load the extension server as a sidecar to Envoy Gateway to minimize the impact of networking on the hook calls. + +[official goals]: https://github.com/envoyproxy/gateway/blob/main/GOALS.md#extensibility +[ExtensionRef filters]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.LocalObjectReference +[ExtensionRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.LocalObjectReference +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.LocalObjectReference +[backendRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendObjectReference +[Gateway API Policy attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Policy Attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[policyAttachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Envoy]: https://www.envoyproxy.io/ +[Envoy specific configuration (xDS)]: https://www.envoyproxy.io/docs/envoy/v1.25.1/configuration/configuration +[v1]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1 +[rate limiting]: ../../user/rate-limit/ +[authentication]: ../../user/jwt-authentication/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[EnvoyGateway config]: ../../api/extension_types/#envoygateway +[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime +[Unstructured]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured +[Listener]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener +[VirtualHost]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost +[Route]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-route diff --git a/site/content/en/latest/design/gatewayapi-translator.md b/site/content/en/latest/design/gatewayapi-translator.md new file mode 100644 index 00000000000..a086b80bfee --- /dev/null +++ b/site/content/en/latest/design/gatewayapi-translator.md @@ -0,0 +1,253 @@ +--- +title: "Gateway API Translator Design" +weight: 4 +--- + +The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate +Representation (IR). + +## Assumptions + +Initially target core conformance features only, to be followed by extended conformance features. + +## Inputs and Outputs + +The main inputs to the Gateway API translator are: + +- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. + +__Note:__ ReferenceGrant is not fully implemented as of v0.2. + +The outputs of the Gateway API translator are: + +- Xds and Infra Internal Representations (IRs). +- Status updates for GatewayClass, Gateways, HTTPRoutes + +## Listener Compatibility + +Envoy Gateway follows Gateway API listener compatibility spec: +> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. + +__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. + +### Listener Compatibility Examples + +#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +## Computing Status + +Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway +must compute the appropriate status fields and conditions for managed resources. + +Status is computed and set for: + +- The managed GatewayClass (`gatewayclass.status.conditions`). +- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the + Envoy Deployment and Service status are also included to calculate Gateway status. +- Listeners for each Gateway (`gateway.status.listeners`). +- The ParentRef for each Route (`route.status.parents`). + +The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to +the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and +updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to +update resource status on the Kubernetes API server. + +## Outline + +The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway +API resources. + +1. Process Gateway Listeners + - Validate unique hostnames, ports, and protocols. + - Validate and compute supported kinds. + - Validate allowed namespaces (validate selector if specified). + - Validate TLS fields if specified, including resolving referenced Secrets. + +2. Process HTTPRoutes + - foreach route rule: + - compute matches + - [core] path exact, path prefix + - [core] header exact + - [extended] query param exact + - [extended] HTTP method + - compute filters + - [core] request header modifier (set/add/remove) + - [core] request redirect (hostname, statuscode) + - [extended] request mirror + - compute backends + - [core] Kubernetes services + - foreach route parent ref: + - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) + - foreach matching listener: + - foreach hostname intersection with route: + - add each computed route rule to host + +## Context Structs + +To help store, access and manipulate information as it's processed during the translation process, a set of context +structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support +processing. + +`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. + +```go +type GatewayContext struct { + // The managed Gateway + *v1beta1.Gateway + + // A list of Gateway ListenerContexts. + listeners []*ListenerContext +} +``` + +`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on +the associated Gateway. + +```go +type ListenerContext struct { + // The Gateway listener. + *v1beta1.Listener + + // The Gateway this Listener belongs to. + gateway *v1beta1.Gateway + + // An index used for managing this listener in the list of Gateway listeners. + listenerStatusIdx int + + // Only Routes in namespaces selected by the selector may be attached + // to the Gateway this listener belongs to. + namespaceSelector labels.Selector + + // The TLS Secret for this Listener, if applicable. + tlsSecret *v1.Secret +} +``` + +`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. + +```go +type RouteContext interface { + client.Object + + // GetRouteType returns the Kind of the Route object, HTTPRoute, + // TLSRoute, TCPRoute, UDPRoute etc. + GetRouteType() string + + // GetHostnames returns the hosts targeted by the Route object. + GetHostnames() []string + + // GetParentReferences returns the ParentReference of the Route object. + GetParentReferences() []v1beta1.ParentReference + + // GetRouteParentContext returns RouteParentContext by using the Route + // objects' ParentReference. + GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext +} +``` + +[message bus]: ../watching/ diff --git a/site/content/en/latest/design/goals.md b/site/content/en/latest/design/goals.md new file mode 100644 index 00000000000..fd38b2004c6 --- /dev/null +++ b/site/content/en/latest/design/goals.md @@ -0,0 +1,91 @@ +--- +title: "Goals" +weight: 1 +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption +through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing +use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer +fundamental interactions. + +## Objectives + +### Expressive API + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +### Batteries included + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +### All environments + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API][]. +Additional goals include multi-cluster support and various runtime environments. + +### Extensibility + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +## Non-objectives + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +## Personas + +_In order of priority_ + +### 1. Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +### 2. Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/latest/design/local-envoy-gateway.md b/site/content/en/latest/design/local-envoy-gateway.md new file mode 100644 index 00000000000..aad0dc5f6f2 --- /dev/null +++ b/site/content/en/latest/design/local-envoy-gateway.md @@ -0,0 +1,52 @@ +--- +title: "Running Envoy Gateway locally" +--- + +## Overview + +Today, Envoy Gateway runs only on Kubernetes. This is an ideal solution +when the applications are running in Kubernetes. +However there might be cases when the applications are running on the host which would +require Envoy Gateway to run locally. + +## Goals + +* Define an API to allow Envoy Gateway to retrieve configuration while running locally. +* Define an API to allow Envoy Gateway to deploy the managed Envoy Proxy fleet on the host +machine. + +## Non Goals + +* Support multiple ways to retrieve configuration while running locally. +* Support multiple ways to deploy the Envoy Proxy fleet locally on the host. + +## API + +* The `provider` field within the `EnvoyGateway` configuration only supports +`Kubernetes` today which provides two features - the ability to retrieve +resources from the Kubernetes API Server as well as deploy the managed +Envoy Proxy fleet on Kubernetes. +* This document proposes adding a new top level `provider` type called `Custom` +with two fields called `resource` and `infrastructure` to allow the user to configure +the sub providers for providing resource configuration and an infrastructure to deploy +the Envoy Proxy data plane in. +* A `File` resource provider will be introduced to enable retrieveing configuration locally +by reading from the configuration from a file. +* A `Host` infrastructure provider will be introduced to allow Envoy Gateway to spawn a +Envoy Proxy child process on the host. + +Here is an example configuration + +``` +provider: + type: Custom + custom: + resource: + type: File + file: + paths: + - "config.yaml" + infrastructure: + type: Host + host: {} +``` diff --git a/site/content/en/latest/design/metrics.md b/site/content/en/latest/design/metrics.md new file mode 100644 index 00000000000..78b05eea98e --- /dev/null +++ b/site/content/en/latest/design/metrics.md @@ -0,0 +1,117 @@ +--- +title: "Observability: Metrics" +--- + +## Overview + +Envoy provide robust platform for metrics, Envoy support three different kinds of stats: counter, gauges, histograms. + +Envoy enables prometheus format output via the `/stats/prometheus` [admin endpoint][]. + +Envoy support different kinds of sinks, but EG will only support [Open Telemetry sink][]. + +Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels][] for implementers such as Envoy Gateway to expose features. Since metrics is not covered by `Core` or `Extended` APIs, EG should provide an easy to config metrics per `EnvoyProxy`. + +## Goals + +- Support expose metrics in prometheus way(reuse probe port). +- Support Open Telemetry stats sink. + +## Non-Goals + +- Support other stats sink. + +## Use-Cases + +- Enable prometheus metric by default +- Disable prometheus metric +- Push metrics via Open Telemetry Sink +- TODO: Customize histogram buckets of target metric +- TODO: Support stats matcher + +### ProxyMetric API Type + +```golang mdox-exec="sed '1,7d' api/v1alpha1/metric_types.go" +type ProxyMetrics struct { + // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. + Prometheus *PrometheusProvider `json:"prometheus,omitempty"` + // Sinks defines the metric sinks where metrics are sent to. + Sinks []MetricSink `json:"sinks,omitempty"` +} + +type MetricSinkType string + +const ( + MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" +) + +type MetricSink struct { + // Type defines the metric sink type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + OpenTelemetry *OpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type OpenTelemetrySink struct { + // Host define the service hostname. + Host string `json:"host"` + // Port defines the port the service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + + // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig +} + +type PrometheusProvider struct { + // Disable the Prometheus endpoint. + Disable bool `json:"disable,omitempty"` +} +``` + +### Example + +- The following is an example to disable prometheus metric. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/disable-prometheus.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: prometheus + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + prometheus: + disable: true +``` + +- The following is an example to send metric via Open Telemetry sink. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/otel-sink.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: otel-sink + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 +``` + +[admin endpoint]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin +[Open Telemetry sink]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels diff --git a/site/content/en/latest/design/pprof.md b/site/content/en/latest/design/pprof.md new file mode 100644 index 00000000000..40d75ea8f83 --- /dev/null +++ b/site/content/en/latest/design/pprof.md @@ -0,0 +1,70 @@ +--- +title: "Debug support in Envoy Gateway" +--- + +## Overview + +Envoy Gateway exposes endpoints at `localhost:19000/debug/pprof` to run Golang profiles to aid in live debugging. + +The endpoints are equivalent to those found in the http/pprof package. `/debug/pprof/` returns an HTML page listing the available profiles. + +## Goals + +* Add admin server to Envoy Gateway control plane, separated with admin server. +* Add pprof support to Envoy Gateway control plane. +* Define an API to allow Envoy Gateway to custom admin server configuration. +* Define an API to allow Envoy Gateway to open envoy gateway config dump in logs. + +The following are the different types of profiles end-user can run: + +PROFILE | FUNCTION +-- | -- +/debug/pprof/allocs | Returns a sampling of all past memory allocations. +/debug/pprof/block | Returns stack traces of goroutines that led to blocking on synchronization primitives. +/debug/pprof/cmdline | Returns the command line that was invoked by the current program. +/debug/pprof/goroutine | Returns stack traces of all current goroutines. +/debug/pprof/heap | Returns a sampling of memory allocations of live objects. +/debug/pprof/mutex | Returns stack traces of goroutines holding contended mutexes. +/debug/pprof/profile | Returns pprof-formatted cpu profile. You can specify the duration using the seconds GET parameter. The default duration is 30 seconds. +/debug/pprof/symbol | Returns the program counters listed in the request. +/debug/pprof/threadcreate | Returns stack traces that led to creation of new OS threads. +/debug/pprof/trace | Returns the execution trace in binary form. You can specify the duration using the seconds GET parameter. The default duration is 1 second. + +## Non Goals + +## API + +* Add `admin` field in EnvoyGateway config. +* Add `address` field under `admin` field. +* Add `port` and `host` under `address` field. +* Add `enableDumpConfig` field under `admin field. +* Add `enablePprof` field under `admin field. + +Here is an example configuration to open admin server and enable Pprof: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +gateway: + controllerName: "gateway.envoyproxy.io/gatewayclass-controller" +kind: EnvoyGateway +provider: + type: "Kubernetes" +admin: + enablePprof: true + address: + host: 127.0.0.1 + port: 19000 +``` + +Here is an example configuration to open envoy gateway config dump in logs: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +gateway: + controllerName: "gateway.envoyproxy.io/gatewayclass-controller" +kind: EnvoyGateway +provider: + type: "Kubernetes" +admin: + enableDumpConfig: true +``` diff --git a/site/content/en/latest/design/rate-limit.md b/site/content/en/latest/design/rate-limit.md new file mode 100644 index 00000000000..8dfda7680e8 --- /dev/null +++ b/site/content/en/latest/design/rate-limit.md @@ -0,0 +1,447 @@ +--- +title: "Rate Limit Design" +--- + +## Overview + +Rate limit is a feature that allows the user to limit the number of incoming requests +to a predefined value based on attributes within the traffic flow. + +Here are some reasons why a user may want to implement Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +## Scope Types + +The rate limit type here describes the scope of rate limits. + +* Global - In this case, the rate limit is common across all the instances of Envoy proxies +where its applied i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is +10 requests/second, this limit is common and will be hit if 5 requests pass through the first replica +and 5 requests pass through the second replica within the same second. + +* Local - In this case, the rate limits are specific to each instance/replica of Envoy running. +Note - This is not part of the initial design and will be added as a future enhancement. + +## Match Types + +### Rate limit a specific traffic flow + +* Here is an example of a ratelimit implemented by the application developer to limit a specific user +by matching on a custom `x-user-id` header with a value set to `one` + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-specific-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-specific-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit all traffic flows + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route to safeguard health of internal application components. In this case, no specific `headers` match +is specified, and the rate limit is applied to all traffic flows accepted by this `HTTPRoute`. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-all-requests +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - limit: + requests: 1000 + unit: Second +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-requests + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per distinct value + +* Here is an example of a rate limit implemented by the application developer to limit any unique user +by matching on a custom `x-user-id` header. Here, user A (recognised from the traffic flow using the header +`x-user-id` and value `a`) will be rate limited at 10 requests/hour and so will user B +(recognised from the traffic flow using the header `x-user-id` and value `b`). + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-per-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per source IP + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on source IP. In this case, requests from `x.x.x.x` will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-per-ip +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - sourceIP: x.x.x.x/32 + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit based on JWT claims + +* Here is an example of rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on the jwt claim. In this case, requests with jwt claim information of `{"name":"John Doe"}` +will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-example +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + claimToHeaders: + - claim: name + header: custom-request-header +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-specific-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: custom-request-header + value: John Doe + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: /foo +``` + + +## Multiple RateLimitFilters, rules and clientSelectors +* Users can create multiple `RateLimitFilter`s and apply it to the same `HTTPRoute`. In such a case each +`RateLimitFilter` will be applied to the route and matched (and limited) in a mutually exclusive way, independent of each other. +* Rate limits are applied for each `RateLimitFilter` `rule` when ALL the conditions under `clientSelectors` hold true. + +Here's an example highlighting this - + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-all-safeguard-app +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - limit: + requests: 100 + unit: Hour +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-per-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 100 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-safeguard-app + backendRefs: + - name: backend + port: 3000 +``` + +* The user has created two `RateLimitFilter`s and has attached it to a `HTTPRoute` - one(`ratelimit-all-safeguard-app`) to +ensure that the backend does not get overwhelmed with requests, any excess requests are rate limited irrespective of +the attributes within the traffic flow, and another(`ratelimit-per-user`) to rate limit each distinct user client +who can be differentiated using the `x-user-id` header, to ensure that each client does not make exessive requests to the backend. +* If user `baz` (identified with the header and value of `x-user-id: baz`) sends 90 requests within the first second, and +user `bar` sends 11 more requests during that same interval of 1 second, and user `bar` sends the 101th request within that second, +the rule defined in `ratelimit-all-safeguard-app` gets activated and Envoy Gateway will ratelimit the request sent by `bar` (and any other +request sent within that 1 second). After 1 second, the rate limit counter associated with the `ratelimit-all-safeguard-app` rule +is reset and again evaluated. +* If user `bar` also ends up sending 90 more requests within the hour, summing up `bar`'s total request count to 101, the rate limit rule +defined within `ratelimit-per-user` will get activated, and `bar`'s requests will be rate limited again until the hour interval ends. +* Within the same above hour, if `baz` sends 991 more requests, summing up `baz`'s total request count to 1001, the rate limit rule defined +within `ratelimit-per-user` will get activated for `baz`, and `baz`'s requests will also be rate limited until the hour interval ends. + +## Design Decisions + +* The initial design uses an Extension filter to apply the Rate Limit functionality on a specific [HTTPRoute][]. +This was preferred over the [PolicyAttachment][] extension mechanism, because it is unclear whether Rate Limit +will be required to be enforced or overridden by the platform administrator or not. +* The RateLimitFilter can only be applied as a filter to a [HTTPRouteRule][], applying it across all backends within a [HTTPRoute][] +and cannot be applied a filter within a [HTTPBackendRef][] for a specific backend. +* The [HTTPRoute][] API has a [matches][] field within each [rule][] to select a specific traffic flow to be routed to +the destination backend. The RateLimitFilter API that can be attached to an HTTPRoute via an [extensionRef][] filter, +also has a `clientSelectors` field within each `rule` to select attributes within the traffic flow to rate limit specific clients. +The two levels of selectors/matches allow for flexibility and aim to hold match information specific to its use, allowing the author/owner +of each configuration to be different. It also allows the `clientSelectors` field within the RateLimitFilter to be enhanced with other matchable +attribute such as [IP subnet][] in the future that are not relevant in the [HTTPRoute][] API. + +## Implementation Details + +### Global Rate limiting + +* [Global rate limiting][] in Envoy Proxy can be achieved using the following - + * [Actions][] can be configured per [xDS Route][]. + * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] + defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined + using a [rate limit filter][]. + * Based on information received by the rate limit service and its programmed configuration, a decision is computed, whether to rate limit + the HTTP Request or not, and is sent back to Envoy, which enforces this decision on the data plane. +* Envoy Gateway will leverage this Envoy Proxy feature by - + * Translating the user facing RateLimitFilter API into Rate limit [Actions][] as well as Rate limit service configuration to implement + the desired API intent. + * Envoy Gateway will use the existing [reference implementation][] of the rate limit service. + * The Infrastructure administrator will need to enable the rate limit service using new settings that will be defined in the [EnvoyGateway][] config API. + * The xDS IR will be enhanced to hold the user facing rate limit intent. + * The xDS Translator will be enhanced to translate the rate limit field within the xDS IR into Rate limit [Actions][] as well as instantiate the [rate limit filter][]. + * A new runner called `rate-limit` will be added that subscribes to the xDS IR messages and translates it into a new Rate Limit Infra IR which contains + the [rate limit service configuration][] as well as other information needed to deploy the rate limit service. + * The infrastructure service will be enhanced to subscribe to the Rate Limit Infra IR and deploy a provider specific rate limit service runnable entity. + * A Status field within the RateLimitFilter API will be added to reflect whether the specific configuration was programmed correctly in these multiple locations or not. + +[PolicyAttachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRoute +[HTTPRouteRule]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPBackendRef +[matches]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteMatch +[rule]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteMatch +[extensionRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilterType +[IP subnet]: https://en.wikipedia.org/wiki/Subnetwork +[Actions]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit-action +[descriptors]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter.html?highlight=descriptor#example-1 +[Global rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[xDS Route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-routeaction +[rate limit filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[rate limit service]: https://www.envoyproxy.io/docs/envoy/latest/configuration/other_features/rate_limit#config-rate-limit-service +[reference implementation]: https://github.com/envoyproxy/ratelimit +[EnvoyGateway]: https://github.com/envoyproxy/gateway/blob/main/api/v1alpha1/envoygateway_types.go +[rate limit service configuration]: https://github.com/envoyproxy/ratelimit#configuration diff --git a/site/content/en/latest/design/security-policy.md b/site/content/en/latest/design/security-policy.md new file mode 100644 index 00000000000..b39165feb03 --- /dev/null +++ b/site/content/en/latest/design/security-policy.md @@ -0,0 +1,115 @@ +--- +title: "SecurityPolicy " +--- + +## Overview + +This design document introduces the `SecurityPolicy` API allowing system administrators to configure +authentication and authorization policies to the traffic entering the gateway. + +## Goals +* Add an API definition to hold settings for configuring authentication and authorization rules +on the traffic entering the gateway. + +## Non Goals +* Define the API configuration fields in this API. + +## Implementation +`SecurityPolicy` is a [Policy Attachment][] type API that can be used to extend [Gateway API][] +to define authentication and authorization rules. + +### Example +Here is an example highlighting how a user can configure this API. + +``` +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: https + protocol: HTTPS + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-authn-policy + namespace: default +spec: + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default +``` + +## Features / API Fields +Here is a list of features that can be included in this API +* JWT based authentication +* OIDC Authentication +* External Authorization +* Basic Auth +* API Key Auth +* CORS + +## Design Decisions +* This API will only support a single `targetRef` and can bind to a `Gateway` resource or a `HTTPRoute` or `GRPCRoute`. +* This API resource MUST be part of same namespace as the targetRef resource +* There can be only be ONE policy resource attached to a specific targetRef e.g. a `Listener` (section) within a `Gateway` +* If the policy targets a resource but cannot attach to it, this information should be reflected +in the Policy Status field using the `Conflicted=True` condition. +* If multiple polices target the same resource, the oldest resource (based on creation timestamp) will +attach to the Gateway Listeners, the others will not. +* If Policy A has a `targetRef` that includes a `sectionName` i.e. +it targets a specific Listener within a `Gateway` and Policy B has a `targetRef` that targets the same +entire Gateway then + * Policy A will be applied/attached to the specific Listener defined in the `targetRef.SectionName` + * Policy B will be applied to the remaining Listeners within the Gateway. Policy B will have an additional + status condition `Overridden=True`. +* A Policy targeting the most specific scope wins over a policy targeting a lesser specific scope. + i.e. A Policy targeting a xRoute (`HTTPRoute` or `GRPCRoute`) overrides a Policy targeting a Listener that is +this route's parentRef which in turn overrides a Policy targeting the Gateway the listener/section is a part of. + +## Alternatives +* The project can indefinitely wait for these configuration parameters to be part of the [Gateway API][]. + +[Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/latest/design/system-design.md b/site/content/en/latest/design/system-design.md new file mode 100644 index 00000000000..c5375247534 --- /dev/null +++ b/site/content/en/latest/design/system-design.md @@ -0,0 +1,174 @@ +--- +title: "System Design" +weight: 2 +--- + +## Goals + +* Define the system components needed to satisfy the requirements of Envoy Gateway. + +## Non-Goals + +* Create a detailed design and interface specification for each system component. + +## Terminology + +* Control Plane- A collection of inter-related software components for providing application gateway and routing + functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. + These services are detailed in the [components](#components) section. +* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. + +## Architecture + +![Architecture](/img/architecture.png) + +## Configuration + +Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][gw_api] objects. + +### Static Configuration + +Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, +configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the +configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. + +### Dynamic Configuration + +Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using +reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is +defined as Kubernetes resources that provide the following services: + +* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is + expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be + referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, + e.g. expose Envoy network endpoints using a `ClusterIP` service instead of a `LoadBalancer` service. +* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP + requests for "www.example.com" to a backend service running a web server. This configuration is expressed through + [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. + Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] + reference. + +## Components + +Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described +in the [Watching Components Design][wcd]. + +### Provider + +A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve +services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap +via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A +provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). + +#### Kubernetes Provider + +* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the + [dynamic configuration](#dynamic-configuration). +* Manages the data plane through Kubernetes API CRUD operations. +* Uses Kubernetes for Service discovery. +* Uses etcd (via Kubernetes API) to persist data. + +#### File Provider + +* Uses a file watcher to watch files in a directory that define the data plane configuration. +* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. +* Uses the host's DNS for Service discovery. +* If needed, the local filesystem is used to persist data. + +### Resource Watcher + +The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The +mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes +provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator +as output. + +### Resource Translator + +The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate +Representation (IR). It is responsible for: + +* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. +* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. + +__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. + +### Intermediate Representation (IR) + +The Intermediate Representation defines internal data models that external resources are translated into. This allows +Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR +used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. + +* Infra IR- Used as the internal definition of the managed data plane infrastructure. +* xDS IR- Used as the internal definition of the managed data plane xDS configuration. + +### xDS Translator + +The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. + +### xDS Server + +The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server +Protocol and is responsible for using xDS to configure the data plane. + +### Infra Manager + +The Infra Manager is a provider-specific component responsible for managing the following infrastructure: + +* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, + Service, etc. resources to run Envoy in a Kubernetes cluster. +* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require + external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning + and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to + users through the [Custom Route Filters][crf] extension. + +The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. + +## Design Decisions + +* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with + `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy + Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. + `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy + infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. +* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. + * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. + * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners + in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are + met: + * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies + either the “HTTPS” or “TLS” Protocol. + * Each Listener within the group specifies a unique "Hostname". + * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no + other Listener matches. + * Envoy Gateway does __not__ merge listeners across multiple Gateways. +* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. + * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. +* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. + * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. +* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. + +The draft for this document is [here][draft_design]. + +[gw_api]: https://gateway-api.sigs.k8s.io +[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass +[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway +[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute +[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute +[go_cp]: https://github.com/envoyproxy/go-control-plane +[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[rls]: https://github.com/envoyproxy/ratelimit +[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[crf]: https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional +[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts +[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners +[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route +[be_ref]: https://gateway-api.sigs.k8s.io/api-types/httproute/#backendrefs-optional +[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster +[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[be]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendObjectReference +[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ +[wcd]: ../watching +[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 +[roadmap]: ../../contributions/roadmap/ diff --git a/site/content/en/latest/design/tcp-udp-design.md b/site/content/en/latest/design/tcp-udp-design.md new file mode 100644 index 00000000000..8dc29830164 --- /dev/null +++ b/site/content/en/latest/design/tcp-udp-design.md @@ -0,0 +1,52 @@ +--- +title: "TCP and UDP Proxy Design " +--- + +Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP +and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the +design decision. + +Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP][] + and [UDP][] +, so ideally, Envoy Gateway should also be able to work in these two modes: + +## Non-transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the +TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when +proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see Envoy's IP address and port. + +## Transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies +the TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address +when proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see the original downstream IP address and Envoy's mac address. + +Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward +the port number. + +## The Implications of Transparent Proxy Mode + +### Escalated Privilege +Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated +CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. + +### Routing +The upstream can see the original source IP, but the original port number won't be passed, so the return +traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back +to the right port number of the downstream, which requires routing at the upstream side to be set up. +In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. + +## The Design Decision (For Now) + +The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and +port of the deployed Envoy instance instead of the client. + +[TCP]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener +[UDP]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig diff --git a/site/content/en/latest/design/tracing.md b/site/content/en/latest/design/tracing.md new file mode 100644 index 00000000000..a2790690fa6 --- /dev/null +++ b/site/content/en/latest/design/tracing.md @@ -0,0 +1,166 @@ +--- +title: "Observability: Tracing" +--- + +## Overview + +Envoy supports extensible tracing to different sinks, Zipkin, OpenTelemetry etc. Overview of Envoy tracing can be found [here][tracing]. + +Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels][] for implementers such as Envoy Gateway to expose features. Since tracing is not covered by `Core` or `Extended` APIs, EG should provide an easy to config tracing per `EnvoyProxy`. + +Only OpenTelemetry sink can be configured currently, you can use [OpenTelemetry Collector][] to export to other tracing backends. + +## Goals + +- Support send tracing to `OpenTelemetry` backend +- Support configurable sampling rate +- Support propagate tag from `Literal`, `Environment` and `Request Header` + +## Non-Goals + +- Support other tracing backend, e.g. `Zipkin`, `Jaeger` + +## Use-Cases + +- Configure accesslog for a `EnvoyProxy` to `File` + +### ProxyAccessLog API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/tracing_types.go" +type ProxyTracing struct { + // SamplingRate controls the rate at which traffic will be + // selected for tracing if no prior sampling decision has been made. + // Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=100 + // +kubebuilder:default=100 + // +optional + SamplingRate *uint32 `json:"samplingRate,omitempty"` + // CustomTags defines the custom tags to add to each span. + // If provider is kubernetes, pod name and namespace are added by default. + CustomTags map[string]CustomTag `json:"customTags,omitempty"` + // Provider defines the tracing provider. + // Only OpenTelemetry is supported currently. + Provider TracingProvider `json:"provider"` +} + +type TracingProviderType string + +const ( + TracingProviderTypeOpenTelemetry TracingProviderType = "OpenTelemetry" +) + +type TracingProvider struct { + // Type defines the tracing provider type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type TracingProviderType `json:"type"` + // Host define the provider service hostname. + Host string `json:"host"` + // Port defines the port the provider service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +type CustomTagType string + +const ( + // CustomTagTypeLiteral adds hard-coded value to each span. + CustomTagTypeLiteral CustomTagType = "Literal" + // CustomTagTypeEnvironment adds value from environment variable to each span. + CustomTagTypeEnvironment CustomTagType = "Environment" + // CustomTagTypeRequestHeader adds value from request header to each span. + CustomTagTypeRequestHeader CustomTagType = "RequestHeader" +) + +type CustomTag struct { + // Type defines the type of custom tag. + // +kubebuilder:validation:Enum=Literal;Environment;RequestHeader + // +unionDiscriminator + // +kubebuilder:default=Literal + Type CustomTagType `json:"type"` + // Literal adds hard-coded value to each span. + // It's required when the type is "Literal". + Literal *LiteralCustomTag `json:"literal,omitempty"` + // Environment adds value from environment variable to each span. + // It's required when the type is "Environment". + Environment *EnvironmentCustomTag `json:"environment,omitempty"` + // RequestHeader adds value from request header to each span. + // It's required when the type is "RequestHeader". + RequestHeader *RequestHeaderCustomTag `json:"requestHeader,omitempty"` + + // TODO: add support for Metadata tags in the future. + // EG currently doesn't support metadata for route or cluster. +} + +// LiteralCustomTag adds hard-coded value to each span. +type LiteralCustomTag struct { + // Value defines the hard-coded value to add to each span. + Value string `json:"value"` +} + +// EnvironmentCustomTag adds value from environment variable to each span. +type EnvironmentCustomTag struct { + // Name defines the name of the environment variable which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the environment variable is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} + +// RequestHeaderCustomTag adds value from request header to each span. +type RequestHeaderCustomTag struct { + // Name defines the name of the request header which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the request header is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} +``` + +### Example + +1. The following is an example to config tracing. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/tracing/default.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: tracing + namespace: envoy-gateway-system +spec: + telemetry: + tracing: + # sample 100% of requests + samplingRate: 100 + provider: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + customTags: + # This is an example of using a literal as a tag value + key1: + type: Literal + literal: + value: "val1" + # This is an example of using an environment variable as a tag value + env1: + type: Environment + environment: + name: ENV1 + defaultValue: "-" + # This is an example of using a header value as a tag value + header1: + type: RequestHeader + requestHeader: + name: X-Header-1 + defaultValue: "-" +``` + +[tracing]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/observability/tracing +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels +[OpenTelemetry Collector]: https://opentelemetry.io/docs/collector/ diff --git a/site/content/en/latest/design/watching.md b/site/content/en/latest/design/watching.md new file mode 100644 index 00000000000..5eabad7b3f9 --- /dev/null +++ b/site/content/en/latest/design/watching.md @@ -0,0 +1,120 @@ +--- +title: "Watching Components Design" +weight: 3 +--- + +Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch +external resources, and "publish" what they see for other components to consume; others watch what another publishes and +act on it (such as the resource translator watches what the providers publish, and then publishes its own results that +are watched by another component). Some of these internally published results are consumed by multiple components. + +To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the +standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub +pattern. + +## Pub + +Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" +tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if +we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by +the IR translator: + + ```go + type ResourceTable struct { + // gateway classes are cluster-scoped; no namespace + GatewayClasses watchable.Map[string, *gwapiv1.GatewayClass] + + // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. + Gateways watchable.Map[types.NamespacedName, *gwapiv1.Gateway] + + HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1.HTTPRoute] + } + ``` + +The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; +updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` +method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher +doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just +`.Store` its data and `watchable` will do the right thing. + +## Sub + +Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or +`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: + + ```go + func(ctx context.Context) error { + for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { + fullState := irInput{ + GatewayClasses: k8sTable.GatewayClasses.LoadAll(), + Gateways: k8sTable.Gateways.LoadAll(), + HTTPRoutes: snapshot.State, + } + translate(irInput) + } + } + ``` + +Or, to watch multiple maps in the same loop: + + ```go + func worker(ctx context.Context) error { + classCh := k8sTable.GatewayClasses.Subscribe(ctx) + gwCh := k8sTable.Gateways.Subscribe(ctx) + routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) + for ctx.Err() == nil { + var arg irInput + select { + case snapshot := <-classCh: + arg.GatewayClasses = snapshot.State + case snapshot := <-gwCh: + arg.Gateways = snapshot.State + case snapshot := <-routeCh: + arg.Routes = snapshot.State + } + if arg.GateWayClasses == nil { + arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() + } + if arg.GateWays == nil { + arg.Gateways = k8sTable.GateWays.LoadAll() + } + if arg.HTTPRoutes == nil { + arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() + } + translate(irInput) + } + } + ``` + +From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; +but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a +handy way to know when to run, `.Load` and friends can be used without subscribing. + +There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but +it's probably wise to just have one publisher for each map. + +The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when +`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple +mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in +to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of +each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need +to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. + +If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` +entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you +must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to +help with this. + +## Other Notes + +The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the +map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb +`chan`. + +A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types +be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and +so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can +generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate +methods for. + +[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/site/content/en/latest/install/_index.md b/site/content/en/latest/install/_index.md new file mode 100644 index 00000000000..b4c6f79c6fd --- /dev/null +++ b/site/content/en/latest/install/_index.md @@ -0,0 +1,5 @@ +--- +title: "Installation" +description: This section includes installation related contents of Envoy Gateway. +weight: 70 +--- diff --git a/site/content/en/latest/install/api.md b/site/content/en/latest/install/api.md new file mode 100644 index 00000000000..9e2d9e91dcc --- /dev/null +++ b/site/content/en/latest/install/api.md @@ -0,0 +1,56 @@ ++++ +title = "gateway-helm" ++++ + + +![Version: v0.0.0-latest](https://img.shields.io/badge/Version-v0.0.0--latest-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square) + +The Helm chart for Envoy Gateway + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| envoy-gateway-steering-committee | | | +| envoy-gateway-maintainers | | | + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| certgen.job.annotations | object | `{}` | | +| certgen.job.ttlSecondsAfterFinished | int | `0` | | +| certgen.rbac.annotations | object | `{}` | | +| certgen.rbac.labels | object | `{}` | | +| config.envoyGateway.gateway.controllerName | string | `"gateway.envoyproxy.io/gatewayclass-controller"` | | +| config.envoyGateway.logging.level.default | string | `"info"` | | +| config.envoyGateway.provider.type | string | `"Kubernetes"` | | +| createNamespace | bool | `false` | | +| deployment.envoyGateway.image.repository | string | `"${ImageRepository}"` | | +| deployment.envoyGateway.image.tag | string | `"${ImageTag}"` | | +| deployment.envoyGateway.imagePullPolicy | string | `"Always"` | | +| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | +| deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | | +| deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | | +| deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | | +| deployment.pod.annotations | object | `{}` | | +| deployment.pod.labels | object | `{}` | | +| deployment.ports[0].name | string | `"grpc"` | | +| deployment.ports[0].port | int | `18000` | | +| deployment.ports[0].targetPort | int | `18000` | | +| deployment.ports[1].name | string | `"ratelimit"` | | +| deployment.ports[1].port | int | `18001` | | +| deployment.ports[1].targetPort | int | `18001` | | +| deployment.replicas | int | `1` | | +| envoyGatewayMetricsService.ports[0].name | string | `"http"` | | +| envoyGatewayMetricsService.ports[0].port | int | `19001` | | +| envoyGatewayMetricsService.ports[0].protocol | string | `"TCP"` | | +| envoyGatewayMetricsService.ports[0].targetPort | int | `19001` | | +| kubernetesClusterDomain | string | `"cluster.local"` | | + diff --git a/site/content/en/latest/install/install-egctl.md b/site/content/en/latest/install/install-egctl.md new file mode 100644 index 00000000000..18a593c445c --- /dev/null +++ b/site/content/en/latest/install/install-egctl.md @@ -0,0 +1,57 @@ +--- +title: "Install egctl" +weight: -80 +--- + +{{% alert title="What is egctl?" color="primary" %}} + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + +{{% /alert %}} + + +This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. + +### From The Envoy Gateway Project + +The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. + +### From the Binary Releases + +Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. + +1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) +2. Unpack it (tar -zxvf egctl_latest_linux_amd64.tar.gz) +3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) + +From there, you should be able to run: `egctl help`. + +### From Script + +`egctl` now has an installer script that will automatically grab the latest release version of egctl and install it locally. + +You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. + +```shell +curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh + +chmod +x get-egctl.sh + +# get help info of the +bash get-egctl.sh --help + +# install the latest development version of egctl +bash VERSION=latest get-egctl.sh +``` + +Yes, you can just use the below command if you want to live on the edge. + +```shell +curl -fsSL https://gateway.envoyproxy.io/get-egctl.sh | VERSION=latest bash +``` + +{{% alert title="Next Steps" color="warning" %}} + +You can refer to [User Guides](/latest/user/egctl) to more details about egctl. + +{{% /alert %}} diff --git a/site/content/en/latest/install/install-helm.md b/site/content/en/latest/install/install-helm.md new file mode 100644 index 00000000000..3f3c57e1db9 --- /dev/null +++ b/site/content/en/latest/install/install-helm.md @@ -0,0 +1,144 @@ ++++ +title = "Install with Helm" +weight = -100 ++++ + +[Helm](https://helm.sh) is a package manager for Kubernetes that automates the release and management of software on Kubernetes. + +Envoy Gateway can be installed via a Helm chart with a few simple steps, depending on if you are deploying for the first time, upgrading Envoy Gateway from an existing installation, or migrating from Envoy Gateway. + +## Before you begin + +{{% alert title="Compatibility Matrix" color="warning" %}} +Refer to the [Version Compatibility Matrix](/blog/2022/10/01/versions/) to learn more. +{{% /alert %}} + +The Envoy Gateway Helm chart is hosted by DockerHub. + +It is published at `oci://docker.io/envoyproxy/gateway-helm`. + +{{% alert title="Note" color="primary" %}} +We use `v0.0.0-latest` as the latest development version. + +You can visit [Envoy Gateway Helm Chart](https://hub.docker.com/r/envoyproxy/gateway-helm/tags) for more releases. +{{% /alert %}} + +## Install with Helm + +Envoy Gateway is typically deployed to Kubernetes from the command line. If you don't have Kubernetes, you should use `kind` to create one. + +{{% alert title="Developer Guide" color="primary" %}} +Refer to the [Developer Guide](/latest/contributions/develop) to learn more. +{{% /alert %}} + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml + +## Helm chart customizations + +Some of the quick ways of using the helm install command for envoy gateway installation are below. + +### Increase the replicas + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set deployment.replicas=2 +``` + +### Change the kubernetesClusterDomain name + +If you have installed your cluster with different domain name you can use below command. + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set kubernetesClusterDomain= +``` + +**Note**: Above are some of the ways we can directly use for customization of our installation. But if you are looking for more complex changes [values.yaml](https://helm.sh/docs/chart_template_guide/values_files/) comes to rescue. + +### Using values.yaml file for complex installation + +```yaml +deployment: + envoyGateway: + resources: + limits: + cpu: 700m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + ports: + - name: grpc + port: 18005 + targetPort: 18000 + - name: ratelimit + port: 18006 + targetPort: 18001 + +config: + envoyGateway: + logging: + level: + default: debug +``` + +Here we have made three changes to our values.yaml file. Increase the resources limit for cpu to `700m`, changed the port for grpc to `18005` and for ratelimit to `18006` and also updated the logging level to `debug`. + +You can use the below command to install the envoy gateway using values.yaml file. + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -f values.yaml +``` + +{{% alert title="Helm Chart Values" color="primary" %}} +If you want to know all the available fields inside the values.yaml file, please see the [Helm Chart Values](../api). +{{% /alert %}} + +## Open Ports + +These are the ports used by Envoy Gateway and the managed Envoy Proxy. + +### Envoy Gateway + +| Envoy Gateway | Address | Port | Configurable | +|:----------------------:|:---------:|:------:| :------: | +| Xds EnvoyProxy Server | 0.0.0.0 | 18000 | No | +| Xds RateLimit Server | 0.0.0.0 | 18001 | No | +| Admin Server | 127.0.0.1 | 19000 | Yes | +| Metrics Server | 0.0.0.0 | 19001 | No | +| Health Check | 127.0.0.1 | 8081 | No | + +### EnvoyProxy + +| Envoy Proxy | Address | Port | +|:---------------------------------:|:-----------:| :-----: | +| Admin Server | 127.0.0.1 | 19000 | +| Heath Check | 0.0.0.0 | 19001 | + +{{% alert title="Next Steps" color="warning" %}} +Envoy Gateway should now be successfully installed and running, but in order to experience more abilities of Envoy Gateway, you can refer to [User Guides](../../user). +{{% /alert %}} diff --git a/site/content/en/latest/install/install-yaml.md b/site/content/en/latest/install/install-yaml.md new file mode 100644 index 00000000000..4b13529f000 --- /dev/null +++ b/site/content/en/latest/install/install-yaml.md @@ -0,0 +1,39 @@ ++++ +title = "Install with Kubernetes YAML" +weight = -99 ++++ + +In this guide, we'll walk you through installing Envoy Gateway in your Kubernetes cluster. + +The manual install process does not allow for as much control over configuration +as the [Helm install method](../install-helm), so if you need more control over your Envoy Gateway +installation, it is recommended that you use helm. + +## Before you begin + +Envoy Gateway is designed to run in Kubernetes for production. The most essential requirements are: + +* Kubernetes 1.25 or later +* The `kubectl` command-line tool + +{{% alert title="Compatibility Matrix" color="warning" %}} +Refer to the [Version Compatibility Matrix](/blog/2022/10/01/versions/) to learn more. +{{% /alert %}} + +## Install with YAML + +Envoy Gateway is typically deployed to Kubernetes from the command line. If you don't have Kubernetes, you should use `kind` to create one. + +{{% alert title="Developer Guide" color="primary" %}} +Refer to the [Developer Guide](/latest/contributions/develop) to learn more. +{{% /alert %}} + +1. In your terminal, run the following command: + + ```shell + kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/install.yaml + ``` + +2. Next Steps + + Envoy Gateway should now be successfully installed and running, but in order to experience more abilities of Envoy Gateway, you can refer to [User Guides](/latest/user). diff --git a/site/content/en/latest/releases/_index.md b/site/content/en/latest/releases/_index.md new file mode 100644 index 00000000000..382eb1dd20d --- /dev/null +++ b/site/content/en/latest/releases/_index.md @@ -0,0 +1,5 @@ +--- +title: "Releases" +weight: 90 +description: This section includes Releases of Envoy Gateway. +--- diff --git a/site/content/en/latest/releases/v0.1.0.md b/site/content/en/latest/releases/v0.1.0.md new file mode 100644 index 00000000000..3d55118a846 --- /dev/null +++ b/site/content/en/latest/releases/v0.1.0.md @@ -0,0 +1,9 @@ +--- +title: "v0.1.0" +publishdate: 2022-05-16 +--- + +Date: May 16, 2022 + +## Documentation +- The initial open source release describing project goals and high-level design. diff --git a/site/content/en/latest/releases/v0.2.0-rc.1.md b/site/content/en/latest/releases/v0.2.0-rc.1.md new file mode 100644 index 00000000000..59da2015f9d --- /dev/null +++ b/site/content/en/latest/releases/v0.2.0-rc.1.md @@ -0,0 +1,37 @@ +--- +title: "v0.2.0-rc.1" +publishdate: 2022-08-31 +--- + +Date: August 31, 2022 + +## Documentation +- Added a quickstart guide for users to run and use Envoy Gateway. + +## API +- Added the EnvoyGateway API type for configuring Envoy Gateway. +- Added the EnvoyProxy API type for configuring managed Envoys. + +## CI +- Added tooling to build, run, etc. Envoy Gateway. + +## Providers +- Added the Kubernetes provider. + +## xDS +- Added xDS server to configure managed Envoys. + +## IR +- Added xds and infra IRs to decouple user-facing APIs from Envoy Gateway. +- Added IR validation. + +## Translator +- Added the gatewayapi translator to translate Gateway API and associated resources to the IR and manage +- Gateway API status. + +## Message Service +- Added infra and xds IR watchable map messages for inter-component communication. +- Added a Runner to each component to support pub/sub between components. + +## Infra Manager +- Added Kubernetes Infra Manager to manage Envoy infrastructure running in a Kubernetes cluster. diff --git a/site/content/en/latest/releases/v0.2.0-rc.2.md b/site/content/en/latest/releases/v0.2.0-rc.2.md new file mode 100644 index 00000000000..756ccfb18da --- /dev/null +++ b/site/content/en/latest/releases/v0.2.0-rc.2.md @@ -0,0 +1,34 @@ +--- +title: "v0.2.0-rc.2" +publishdate: 2022-09-29 +--- + +Date: September 29, 2022 + +## Documentation +- Updated and expanded developer documentation. +- Added `kube-demo` target to demonstrate Envoy Gateway functionality. +- Added developer debugging documentation. + +## CI +- Added Gateway API conformance tests. + +## Providers +- Added watchers for dependent resources of managed Envoy infrastructure. +- Added Gateway namespace/name labels to managed resources. +- Added support for finalizing the managed GatewayClass. + +## xDS +- Updated xds server and Envoy bootstrap config to use Delta xDS. +- Added initial support for mTLS between the xDS server and Envoy. + +## Translator +- Expanded support for Gateway API status. +- Added support for request modifier and redirect filters. +- Added support to return 500 responses for invalid backends. + +## Message Service +- Updated IRs to support managing multiple Envoy fleets. + +## Infra Manager +- Separate Envoy infrastructure is created per Gateway. diff --git a/site/content/en/latest/releases/v0.2.0.md b/site/content/en/latest/releases/v0.2.0.md new file mode 100644 index 00000000000..6ebad0cf8f2 --- /dev/null +++ b/site/content/en/latest/releases/v0.2.0.md @@ -0,0 +1,53 @@ +--- +title: "v0.2.0" +publishdate: 2022-10-19 +--- + +Date: October 19, 2022 + +## Documentation +- Added Config API, translator, roadmap, and message bus design documentation. +- Added documentation for releasing Envoy Gateway. +- Added user guides for configuring common tasks, e.g. HTTP request routing. +- Added support for the Sphinx documentation generator. + +## API +- Added the EnvoyGateway API type for configuring Envoy Gateway. +- Added the EnvoyProxy API type for configuring managed Envoys. + +## CI Tooling Testing +- Added tooling to build, run, etc. Envoy Gateway. +- Added Gateway API conformance tests. +- Added Make-based tooling to fetch all tools so checks (code lint, spellchecks) and tests can be run locally. +- Added support for releasing latest artifacts to GitHub. +- Added code coverage with a minimum 60% threshold. + +## IR +- Added xds and infra IRs to decouple user-facing APIs from Envoy Gateway. +- Added IR validation. + +## Translator +- Added the gatewayapi translator to translate Gateway API and associated resources to the IR and manage the status of Gateway API resources. +- Added the xDS translator to translate the xds IR to xDS resources. + +## Message Service +- Added infra and xds IR watchable map messages for inter-component communication. +- Added a Runner to each Envoy Gateway component to support pub/sub between components. +- Added support for managing multiple separate Envoy proxy fleets. + +## Infra Manager +- Added Kubernetes Infra Manager to manage Envoy infrastructure running in a Kubernetes cluster. +- Added support for managing a separate Envoy infrastructure per Gateway. + +## Providers +- Added the Kubernetes provider with support for managing GatewayClass, Gateway, HTTPRoute, ReferenceGrant, and TLSRoute resources. +- Due to Issue #539, a ReferenceGrant is not removed from the system when unreferenced. +- Due to Issue #577, TLSRoute is not being tested for Gateway API conformance. +- Added watchers for dependent resources of managed Envoy infrastructure to trigger reconciliation. +- Added support for labeling managed infrastructure using Gateway namespace/name labels. +- Added support for finalizing the managed GatewayClass. + +## xDS +- Added xDS server support to configure managed Envoys using Delta xDS. +- Added initial support for mTLS between the xDS server and managed Envoys. +- Due to envoyproxy/go-control-plane Issue #599, Envoy Gateway logs the private key of HTTPS listeners. diff --git a/site/content/en/latest/releases/v0.3.0-rc.1.md b/site/content/en/latest/releases/v0.3.0-rc.1.md new file mode 100644 index 00000000000..4c50801e6f3 --- /dev/null +++ b/site/content/en/latest/releases/v0.3.0-rc.1.md @@ -0,0 +1,64 @@ +--- +title: "v0.3.0-rc.1" +publishdate: 2023-02-02 +--- + +Date: February 02, 2023 + +## Documentation +- Added Support for Multiple Release Versions +- Added Support for Versioned Docs +- Added Release Details Docs +- Refactored Layout for User Docs + +## API +- Upgraded to v0.6.0 Gateway API +- Added Support for the TCPRoute API +- Added Support for the UDPRoute API +- Added Support for the GRPCRoute API (Add to the ListenerStatus.SupportedKinds Field until https://github.com/envoyproxy/gateway/issues/950 is fixed.) +- Added Support for HTTPRoute URLRewrite Filter +- Added Support for HTTPRoute RequestMirror Filter +- Added Support for HTTPRoute ResponseHeaderModifier Filter +- Added APIs to Manage Envoy Deployment +- Added Support for Request Authentication +- Added Support for Global Rate Limiting +- Added Support for Routes ReferenceGrant +- Added Support for Namespace Server Config Type + +## CI Tooling Testing +- Fixes Make Image Failed in Darwin +- Fixes Wait for Job Succeeded before conformance test +- Upgraded Echoserver Image Tag +- Added Support for User-Facing Version +- Added Support for Testing EG against Multiple Kubernetes Versions + +## Conformance +- Enabled HTTPRouteInvalidParentRefNotMatchingListenerPort conformance test +- Enabled GatewayInvalidTLSConfiguration conformance test +- Enabled GatewayInvalidRouteKind conformance test +- Enabled HTTPRoutePartiallyInvalidViaInvalidReferenceGrant conformance test +- Enabled HTTPRouteReferenceGrant conformance test +- Enabled HTTPRouteMethodMatching conformance test + +## IR +- Added TCP Listener per TLSRoute + +## Translator +- Fixes Remove Stale Listener Condition +- Added Support for Suffix Matches for Headers +- Added Support for HTTP Method Matching to HTTPRoute +- Added Support for Regex Match Type +- Added Support for HTTPQueryParamMatch + +## Providers +- Refactored Kubernetes Provider to Single Reconciler +- Upgraded Kube Provider Test Data Manifests to v0.6.0 +- Removed Duplicate Settings from Bootstrap Config +- Updated Certgen to Use EG Namespace Env +- Added EnvoyProxy to Translator and Kube Infra Manager +- Upgraded Envoyproxy Image to envoy-dev latest in Main +- Removed EG Logs Private Key + +## xDS +- Fixed Start xDS Server Watchable Map Panics +- Enabled Access Logging for xDS Components diff --git a/site/content/en/latest/releases/v0.3.0.md b/site/content/en/latest/releases/v0.3.0.md new file mode 100644 index 00000000000..4eacf8c45c8 --- /dev/null +++ b/site/content/en/latest/releases/v0.3.0.md @@ -0,0 +1,77 @@ +--- +title: "v0.3.0" +publishdate: 2023-02-09 +--- + +Date: February 09, 2023 + +## Documentation +- Added Global Rate Limit User Docs +- Added Request Authentication User Docs +- Added TCP Routing User Docs +- Added UDP Routing User Docs +- Added GRPC Routing User Docs +- Added HTTP Response Headers User Docs +- Added TCP and UDP Proxy Design Docs +- Added egctl Design Docs +- Added Rate Limit Design Docs +- Added Request Authentication Design Docs +- Added Support for Versioned Docs +- Added Support for Multiple Release Versions +- Added Release Details Docs +- Added API Docs Generating Tooling +- Refactored Layout for User Docs + +## API +- Upgraded to v0.6.1 Gateway API +- Added Support for the TCPRoute API +- Added Support for the UDPRoute API +- Added Support for the GRPCRoute API +- Added Support for HTTPRoute URLRewrite Filter +- Added Support for HTTPRoute RequestMirror Filter +- Added Support for HTTPRoute ResponseHeaderModifier Filter +- Added Support for Request Authentication +- Added Support for Global Rate Limiting +- Added Support for Routes ReferenceGrant +- Added Support for Namespace Server Config Type +- Added initial management of Envoy Proxy deployment via EnvoyProxy API + +## CI Tooling Testing +- Fixed Make Image Failed in Darwin +- Fixed Wait for Job Succeeded before conformance test +- Upgraded Echoserver Image Tag +- Added Support for User-Facing Version +- Added Support for Testing EG against Multiple Kubernetes Versions + +## Conformance +- Enabled GatewayClassObservedGenerationBump conformance test +- Enabled GatewayInvalidTLSConfiguration conformance test +- Enabled GatewayInvalidRouteKind conformance test +- Enabled HTTPRouteReferenceGrant conformance test +- Enabled HTTPRouteMethodMatching conformance test +- Enabled HTTPRoutePartiallyInvalidViaInvalidReferenceGrant conformance test +- Enabled HTTPRouteInvalidParentRefNotMatchingListenerPort conformance test +- (Currently EG passes all conformance tests except redirect and gateway/httproute ObservedGenerationBump tests. Redirect tests are failing due to a possible issue with the way upstream conformance tests have made assumptions. Skip them for now until below issues #992 #993 #994 are resolved) + +## IR +- Added TCP Listener per TLSRoute + +## Translator +- Fixes Remove Stale Listener Condition +- Added Support for Suffix Matches for Headers +- Added Support for HTTP Method Matching to HTTPRoute +- Added Support for Regex Match Type +- Added Support for HTTPQueryParamMatch + +## Providers +- Refactored Kubernetes Provider to Single Reconciler +- Upgraded Kube Provider Test Data Manifests to v0.6.1 +- Removed Duplicate Settings from Bootstrap Config +- Updated Certgen to Use EG Namespace Env +- Added EnvoyProxy to Translator and Kube Infra Manager +- Upgraded Envoyproxy Image to envoy-dev latest in Main +- Removed EG Logs Private Key + +## xDS +- Fixed Start xDS Server Watchable Map Panics +- Enabled Access Logging for xDS Components diff --git a/site/content/en/latest/releases/v0.4.0-rc.1.md b/site/content/en/latest/releases/v0.4.0-rc.1.md new file mode 100644 index 00000000000..927069f641f --- /dev/null +++ b/site/content/en/latest/releases/v0.4.0-rc.1.md @@ -0,0 +1,56 @@ +--- +title: "v0.4.0-rc.1" +publishdate: 2023-04-13 +--- + +Date: April 13, 2023 + +## Documentation +- Added Docs for Installing and Using egctl + +## Installation +- Added Helm Installation Support +- Added Support for Ratelimiting Based On IP Subnet +- Added Gateway API Support Doc + +## API +- Upgraded to Gateway API v0.6.2 +- Added Support for Custom Envoy Proxy Bootstrap Config +- Added Support for Configuring the Envoy Proxy Image and Service +- Added Support for Configuring Annotations, Resources, and Securitycontext Settings on Ratelimit Infra and Envoy Proxy +- Added Support for Using Multiple Certificates on a Single Fully Qualified Domain Name +- Gateway Status Address is now Populated for ClusterIP type Envoy Services +- Envoy Proxy Pod and Container SecurityContext is now Configurable +- Added Custom Envoy Gateway Extensions Framework +- Added Support for Service Method Match in GRPCRoute + +## CI Tooling Testing +- Fixed CI Flakes During Helm Install +- Added Test To Ensure Static xDS Cluster Has Same Field Values as Dynamic Cluster +- Added egctl to Build and Test CI Workflow +- Code Coverage Thresholds are now Enforced by CI +- Fixed latest-release-check CI Job Failures +- Added Auto Release Tooling for Charts + +## Conformance +- Enabled GatewayWithAttachedRoutes Test +- Enabled Enable HTTPRouteInvalidParentRefNotMatchingSectionName Test +- Enabled Enable HTTPRouteDisallowedKind Test +- Re-Enabled Gateway/HTTPRouteObservedGenerationBump Test + +## Translator +- Added Support for Dynamic GatewayControllerName in Route Status + +## Providers +- Update GatewayClass Status Based on EnvoyProxy Config Validation + +## xDS +- Added EDS Support +- Fixed PathSeparatedPrefix and Optimized Logic for Prefixes Ending With Trailing Slash +- Updated Deprecated RegexMatcher +- Refactored Authn and Ratelimit Features to Reuse buildXdsCluster + +## Cli +- Added egctl CLI Tool +- Added egctl Support for Dry Runs of Gateway API Config +- Added egctl Support for Dumping Envoy Proxy xDS Resources diff --git a/site/content/en/latest/releases/v0.4.0.md b/site/content/en/latest/releases/v0.4.0.md new file mode 100644 index 00000000000..12c40904088 --- /dev/null +++ b/site/content/en/latest/releases/v0.4.0.md @@ -0,0 +1,59 @@ +--- +title: "v0.4.0" +publishdate: 2023-04-24 +--- + +Date: April 24, 2023 + +## Documentation +- Added Docs for Installing and Using egctl + +## Installation +- Added Helm Installation Support +- Added Support for Ratelimiting Based On IP Subnet +- Added Gateway API Support Doc +- Added Namespace Resource to Helm Templates +- Updated Installation Yaml to Use the envoy-gateway-system Namespace + +## API +- Upgraded to Gateway API v0.6.2 +- Added Support for Custom Envoy Proxy Bootstrap Config +- Added Support for Configuring the Envoy Proxy Image and Service +- Added Support for Configuring Annotations, Resources, and Securitycontext Settings on Ratelimit Infra and Envoy Proxy +- Added Support for Using Multiple Certificates on a Single Fully Qualified Domain Name +- Gateway Status Address is now Populated for ClusterIP type Envoy Services +- Envoy Proxy Pod and Container SecurityContext is now Configurable +- Added Custom Envoy Gateway Extensions Framework +- Added Support for Service Method Match in GRPCRoute +- Fixed a Bug in the Extension Hooks for xDS Virtual Hosts and Routes + +## CI Tooling Testing +- Fixed CI Flakes During Helm Install +- Added Test To Ensure Static xDS Cluster Has Same Field Values as Dynamic Cluster +- Added egctl to Build and Test CI Workflow +- Code Coverage Thresholds are now Enforced by CI +- Fixed latest-release-check CI Job Failures +- Added Auto Release Tooling for Charts + +## Conformance +- Enabled GatewayWithAttachedRoutes Test +- Enabled Enable HTTPRouteInvalidParentRefNotMatchingSectionName Test +- Enabled Enable HTTPRouteDisallowedKind Test +- Re-Enabled Gateway/HTTPRouteObservedGenerationBump Test + +## Translator +- Added Support for Dynamic GatewayControllerName in Route Status + +## Providers +- Update GatewayClass Status Based on EnvoyProxy Config Validation + +## xDS +- Added EDS Support +- Fixed PathSeparatedPrefix and Optimized Logic for Prefixes Ending With Trailing Slash +- Updated Deprecated RegexMatcher +- Refactored Authn and Ratelimit Features to Reuse buildXdsCluster + +## Cli +- Added egctl CLI Tool +- Added egctl Support for Dry Runs of Gateway API Config +- Added egctl Support for Dumping Envoy Proxy xDS Resources diff --git a/site/content/en/latest/releases/v0.5.0-rc.1.md b/site/content/en/latest/releases/v0.5.0-rc.1.md new file mode 100644 index 00000000000..bda070995c8 --- /dev/null +++ b/site/content/en/latest/releases/v0.5.0-rc.1.md @@ -0,0 +1,71 @@ +--- +title: "v0.5.0-rc.1" +publishdate: 2023-07-26 +--- + +Date: July 26, 2023 + +## Documentation +- Added Docs for Installation page using Helm +- Added Docs for Cert Manager Integration +- Added Docs for Presentation Links +- Added Docs for configuring multiple TLS Certificates per Listener + +## Installation +- Added Support for configuring Envoy Gateway Label and Annotations using Helm +- Increased default Resource defaults for Envoy Gateway to 100m CPU and 256Mi Memory +- Fixes Helm values for EnvoyGateway startup configuration +- Added opt-in field to skip creating control plane TLS Certificates allowing users to bring their own certificates. + +## API +- Upgraded to Gateway API v0.7.1 +- Added Support for EnvoyPatchPolicy +- Added Support for EnvoyProxy Telemetry - Access Logging, Traces and Metrics +- Added Support for configuring EnvoyProxy Pod Labels +- Added Support for configuring EnvoyProxy Deployment Strategy Settings, Volumes and Volume Mounts +- Added Support for configuring EnvoyProxy as a NodePort Type Service +- Added Support for Distinct RateLimiting for IP Addresses +- Added Support for converting JWT Claims to Headers, to be used for RateLimiting +- Added Admin Server for Envoy Gateway +- Added Pprof Debug Support for Envoy Gateway +- Added Support to Watch for Resources in Select Namespaces +### Breaking Changes +- Renamed field in EnvoyGateway API from Extension to ExtensionManager + +## CI Tooling Testing +- Added Retest Github Action +- Added CherryPick Github Action +- Added E2E Step in Github CI Workflow +- Added RateLimit E2E Tests +- Added JWT Claim based RateLimit E2E Tests +- Added Access Logging E2E tests +- Added Metrics E2E tests +- Added Tracing E2E tests + +## Conformance +- Enabled GatewayWithAttachedRoutes Test +- Enabled HttpRouteRequestMirror Test +- Skipped HTTPRouteRedirectPortAndScheme Test + +## Translator +### Breaking changes +- Renamed IR resources from - to / +- which also affects generated Xds Resources + +## Providers +- Reconcile Node resources to be able to compute Status Addresses for Gateway +- Discard Status before publishing Provider resources to reduce memory consumption + +## xDS +- Fix Init Race in Xds Runner when starting Xds Server and receiving Xds Input +- Switched to Xds SOTW Server for RateLimit Service Configuration +- Added Control Plane TLS between EnvoyProxy and RateLimit Service +- Enabled adding RateLimit Headers when RateLimit is set +- Allowed GRPCRoute and HTTPRoute to be linked to the same HTTPS Listener +- Set ALPN in the Xds Listener with TLS enabled. +- Added Best Practices Default Edge Settings to Xds Resources +- Compute and Publish EnvoyPatchPolicy status from xds-translator runner + +## Cli +- Added egctl x translate Support to generate default missing Resources +- Added egctl x translate Support for AuthenticationFilter and EnvoyPatchPolicy diff --git a/site/content/en/latest/releases/v0.5.0.md b/site/content/en/latest/releases/v0.5.0.md new file mode 100644 index 00000000000..ce1bd6b9188 --- /dev/null +++ b/site/content/en/latest/releases/v0.5.0.md @@ -0,0 +1,71 @@ +--- +title: "v0.5.0" +publishdate: 2023-07-26 +--- + +Date: July 26, 2023 + +## Documentation +- Added Docs for Installation page using Helm +- Added Docs for Cert Manager Integration +- Added Docs for Presentation Links +- Added Docs for configuring multiple TLS Certificates per Listener + +## Installation +- Added Support for configuring Envoy Gateway Label and Annotations using Helm +- Increased default Resource defaults for Envoy Gateway to 100m CPU and 256Mi Memory +- Fixes Helm values for EnvoyGateway startup configuration +- Added opt-in field to skip creating control plane TLS Certificates allowing users to bring their own certificates. + +## API +- Upgraded to Gateway API v0.7.1 +- Added Support for EnvoyPatchPolicy +- Added Support for EnvoyProxy Telemetry - Access Logging, Traces and Metrics +- Added Support for configuring EnvoyProxy Pod Labels +- Added Support for configuring EnvoyProxy Deployment Strategy Settings, Volumes and Volume Mounts +- Added Support for configuring EnvoyProxy as a NodePort Type Service +- Added Support for Distinct RateLimiting for IP Addresses +- Added Support for converting JWT Claims to Headers, to be used for RateLimiting +- Added Admin Server for Envoy Gateway +- Added Pprof Debug Support for Envoy Gateway +- Added Support to Watch for Resources in Select Namespaces +### Breaking Changes +- Renamed field in EnvoyGateway API from Extension to ExtensionManager + +## CI Tooling Testing +- Added Retest Github Action +- Added CherryPick Github Action +- Added E2E Step in Github CI Workflow +- Added RateLimit E2E Tests +- Added JWT Claim based RateLimit E2E Tests +- Added Access Logging E2E tests +- Added Metrics E2E tests +- Added Tracing E2E tests + +## Conformance +- Enabled GatewayWithAttachedRoutes Test +- Enabled HttpRouteRequestMirror Test +- Skipped HTTPRouteRedirectPortAndScheme Test + +## Translator +### Breaking Changes +- Renamed IR resources from - to / + which also affects generated Xds Resources + +## Providers +- Reconcile Node resources to be able to compute Status Addresses for Gateway +- Discard Status before publishing Provider resources to reduce memory consumption + +## xDS +- Fix Init Race in Xds Runner when starting Xds Server and receiving Xds Input +- Switched to Xds SOTW Server for RateLimit Service Configuration +- Added Control Plane TLS between EnvoyProxy and RateLimit Service +- Enabled adding RateLimit Headers when RateLimit is set +- Allowed GRPCRoute and HTTPRoute to be linked to the same HTTPS Listener +- Set ALPN in the Xds Listener with TLS enabled. +- Added Best Practices Default Edge Settings to Xds Resources +- Compute and Publish EnvoyPatchPolicy status from xds-translator runner + +## Cli +- Added egctl x translate Support to generate default missing Resources +- Added egctl x translate Support for AuthenticationFilter and EnvoyPatchPolicy diff --git a/site/content/en/latest/releases/v0.6.0-rc.1.md b/site/content/en/latest/releases/v0.6.0-rc.1.md new file mode 100644 index 00000000000..5141bc27966 --- /dev/null +++ b/site/content/en/latest/releases/v0.6.0-rc.1.md @@ -0,0 +1,64 @@ +--- +title: "v0.6.0-rc.1" +publishdate: 2023-10-27 +--- + +Date: Oct 27, 2023 + +## Documentation +- Introduced a new website based on Hugo +- Added Grafana dashboards and integration docs for EnvoyProxy metrics +- Added Grafana integration docs for Gateway API metrics + +## Installation +- Added Support for configuring Envoy Gateway Label and Annotations using Helm +- Increased default Resource defaults for Envoy Gateway to 100m CPU and 256Mi Memory +- Fixes Helm values for EnvoyGateway startup configuration +- Added opt-in field to skip creating control plane TLS Certificates allowing users to bring their own certificates. + +## API +- Upgraded to Gateway API v1.0.0 +- Added the ClientTrafficPolicy CRD with Keep Alive Support +- Added the BackendTrafficPolicy CRD with RateLimit and LoadBalancer Support +- Added the SecurityPolicy CRD with CORS and JWT Support +- Added EnvoyGateway Metrics with Prometheus and OpenTelemetry support +- Added Support for InitContainers in EnvoyProxy CRD +- Added Support for LoadBalancerIP in EnvoyProxy CRD +- Added Support for AllocateLoadBalancerNodePorts in EnvoyProxy CRD +- Added Support for LoadBalancerClass in EnvoyProxy CRD +- Added Support for selecting EnvoyProxy stats to be generated +- Added Support for enabling EnvoyProxy Virtual Host metrics +- Added Support for Merging Gateway resources onto the same infrastructure + +### Breaking changes +- Removed the AuthenticationFilter CRD +- Removed the RateLimitFilter CRD +- Enabled EnvoyProxy Prometheus Endpoint by default with an option to disable it +- Updated the Bootstrap field within the EnvoyProxy CRD with an additional value +- field to specify bootstrap config + +## watchable +- Improved caching of resource by implementing a compare function agnostic of resource order + +## Translator +### Breaking changes +- Added support for routing to EndpointSlice endpoints +- Added support for HTTPRoute Timeouts +- Added support for multiple RequestMirror filters per HTTPRoute rule +- Use / instead of - in IR Route Names +- Added Support to ignore ports in Host header + +## Providers +- Added the generationChangedPredicate to most resources to limit resource reconiliation +- Improved reconiliation by using the same enqueue request for all resources +- Added support for reconciling ServiceImport CRD +- Added support for selectively watching resources based on Namespace Selector + +## XDS +- Fixed Layered Runtime warnings +- Upgraded to the latest version of go-control-plane that fixed xDS Resource ordering issues for ADS. +- Added HTTP2 Keep Alives to the xds connection + +## Cli +- Added Support for egctl stats command + diff --git a/site/content/en/latest/releases/v0.6.0.md b/site/content/en/latest/releases/v0.6.0.md new file mode 100644 index 00000000000..2b8714030b0 --- /dev/null +++ b/site/content/en/latest/releases/v0.6.0.md @@ -0,0 +1,70 @@ +--- +title: "v0.6.0" +publishdate: 2023-11-01 +--- + +Date: Nov 1, 2023 + +## Documentation +- Introduced a new website based on Hugo +- Added Grafana dashboards and integration docs for EnvoyProxy metrics +- Added Grafana integration docs for Gateway API metrics + +## Installation +- Updated EnvoyProxy image to be a distroless variant. +- Removed resources around kube-rbac-proxy + +## API +- Upgraded to Gateway API v1.0.0 +- Added the ClientTrafficPolicy CRD with Keep Alive Support +- Added the BackendTrafficPolicy CRD with RateLimit and LoadBalancer Support +- Added the SecurityPolicy CRD with CORS and JWT Support +- Added EnvoyGateway Metrics with Prometheus and OpenTelemetry support +- Added Support for InitContainers in EnvoyProxy CRD +- Added Support for LoadBalancerIP in EnvoyProxy CRD +- Added Support for AllocateLoadBalancerNodePorts in EnvoyProxy CRD +- Added Support for LoadBalancerClass in EnvoyProxy CRD +- Added Support for selecting EnvoyProxy stats to be generated +- Added Support for enabling EnvoyProxy Virtual Host metrics +- Added Support for Merging Gateway resources onto the same infrastructure + +### Breaking changes +- Removed the AuthenticationFilter CRD +- Removed the RateLimitFilter CRD +- Moved EnvoyProxy CRD from `config.gateway.envoyproxy.io` to `gateway.envoyproxy.io` +- Enabled EnvoyProxy Prometheus Endpoint by default with an option to disable it +- Updated the Bootstrap field within the EnvoyProxy CRD with an additional value +- field to specify bootstrap config + +## Conformance +- Added Support for HTTPRouteBackendProtocolH2C Test +- Added Support for HTTPRouteBackendProtocolWebSocket Test +- Added Support for HTTPRouteRequestMultipleMirrors Test +- Added Support for HTTPRouteTimeoutRequest Test +- Added Support for HTTPRouteTimeoutBackendRequest Test +- Added Support for HTTPRouteRedirectPortAndScheme Test + +## Watchable +- Improved caching of resource by implementing a compare function agnostic of resource order + +## Translator +- Added support for routing to EndpointSlice endpoints +- Added support for HTTPRoute Timeouts +- Added support for multiple RequestMirror filters per HTTPRoute rule +- Use / instead of - in IR Route Names +- Added Support to ignore ports in Host header + +## Providers +- Added the generationChangedPredicate to most resources to limit resource reconiliation +- Improved reconiliation by using the same enqueue request for all resources +- Added support for reconciling ServiceImport CRD +- Added support for selectively watching resources based on Namespace Selector + + +## XDS +- Fixed Layered Runtime warnings +- Upgraded to the latest version of go-control-plane that fixed xDS Resource ordering issues for ADS. +- Added HTTP2 Keep Alives to the xds connection + +## Cli +- Added Support for egctl stats command diff --git a/site/content/en/latest/user/_index.md b/site/content/en/latest/user/_index.md new file mode 100644 index 00000000000..e413578a6ca --- /dev/null +++ b/site/content/en/latest/user/_index.md @@ -0,0 +1,5 @@ +--- +title: "User Guides" +weight: 2 +description: This section includes User Guides of Envoy Gateway. +--- diff --git a/site/content/en/latest/user/basic-auth.md b/site/content/en/latest/user/basic-auth.md new file mode 100644 index 00000000000..2b96ddfe4bc --- /dev/null +++ b/site/content/en/latest/user/basic-auth.md @@ -0,0 +1,140 @@ +--- +title: "Basic Authentication" +--- + +This guide provides instructions for configuring [HTTP Basic authentication][http Basic authentication]. +HTTP Basic authentication checks if an incoming request has a valid username and password before routing the request to +a backend service. + +Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure HTTP Basic +authentication. +This instantiated resource can be linked to a [Gateway][Gateway], [HTTPRoute][HTTPRoute] or [GRPCRoute][GRPCRoute] resource. + +## Prerequisites + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Configuration + +Envoy Gateway uses [.htpasswd][.htpasswd] format to store the username-password pairs for authentication. +The file must be stored in a kubernetes secret and referenced in the [SecurityPolicy][SecurityPolicy] configuration. +The secret is an Opaque secret, and the username-password pairs must be stored in the key ".htpasswd". + +### Create a .htpasswd file +First, create a [.htpasswd][.htpasswd] file with the username and password you want to use for authentication. + +The input password won't be saved, instead, a hash will be generated and saved in the output file. When a request +tries to access protected resources, the password in the "Authorization" HTTP header will be hashed and compared with the +saved hash. + +Note: only SHA hash algorithm is supported for now. + +```shell +$ htpasswd -cbs .htpasswd foo bar +Adding password for user foo +``` + +You can also add more users to the file: + +```shell +$ htpasswd -bs .htpasswd foo1 bar1 +``` + +### Create a kubernetes secret + +Next, create a kubernetes secret with the generated .htpasswd file in the previous step. + +```shell +$ kubectl create secret generic basic-auth --from-file=.htpasswd +secret "basic-auth" created +``` + +### Create a SecurityPolicy + +The below example defines a SecurityPolicy that authenticates requests against the user list in the kubernetes +secret generated in the previous step. + +```shell +cat < GET /get HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/8.1.2 +> Accept: */* +> +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Fri, 01 Dec 2023 10:17:04 GMT +< content-length: 507 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/8.1.2" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "172.18.0.2" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "4d0d33e8-d611-41f0-9da0-6458eec20fa5" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-58d58f745-2zwvn" +* Connection #0 to host 172.18.255.202 left intact +}* Found bundle for host: 0x7fb9f5204ea0 [serially] +* Can not multiplex, even if we wanted to +* Re-using existing connection #0 with host 172.18.255.202 +> GET /headers HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/8.1.2 +> Accept: */* +> +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Fri, 01 Dec 2023 10:17:04 GMT +< content-length: 511 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/headers", + "host": "www.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/8.1.2" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "172.18.0.2" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "9a8874c0-c117-481c-9b04-933571732ca5" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-58d58f745-2zwvn" +* Connection #0 to host 172.18.255.202 left intact +} +``` + +You can see keepalive connection marked by the output in: + +```shell +* Connection #0 to host 172.18.255.202 left intact +* Re-using existing connection #0 with host 172.18.255.202 +``` + +### Enable Proxy Protocol for downstream client + +This example configures Proxy Protocol for downstream clients. + +```shell +cat < GET /get HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/8.1.2 +> Accept: */* +> +* Recv failure: Connection reset by peer +* Closing connection 0 +curl: (56) Recv failure: Connection reset by peer +``` + +Curl the example app through Envoy proxy once again, now sending HAProxy PROXY protocol header at the beginning of the connection with --haproxy-protocol flag: + +```shell +curl --verbose --haproxy-protocol --header "Host: www.example.com" http://$GATEWAY_HOST/get +``` + +You should now expect 200 response status and also see that source IP was preserved in the X-Forwarded-For header. + +```shell +* Trying 172.18.255.202:80... +* Connected to 172.18.255.202 (172.18.255.202) port 80 (#0) +> GET /get HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/8.1.2 +> Accept: */* +> +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Mon, 04 Dec 2023 21:11:43 GMT +< content-length: 510 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/8.1.2" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "192.168.255.6" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "290e4b61-44b7-4e5c-a39c-0ec76784e897" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-58d58f745-2zwvn" +* Connection #0 to host 172.18.255.202 left intact +} +``` + +[ClientTrafficPolicy]: ../../api/extension_types#clienttrafficpolicy +[BackendTrafficPolicy]: ../../api/extension_types#backendtrafficpolicy diff --git a/site/content/en/latest/user/cors.md b/site/content/en/latest/user/cors.md new file mode 100644 index 00000000000..d8867ccb8d2 --- /dev/null +++ b/site/content/en/latest/user/cors.md @@ -0,0 +1,121 @@ +--- +title: "CORS" +--- + +This guide provides instructions for configuring [Cross-Origin Resource Sharing (CORS)][cors] on Envoy Gateway. +CORS defines a way for client web applications that are loaded in one domain to interact with resources in a different +domain. + +Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure CORS. +This instantiated resource can be linked to a [Gateway][Gateway], [HTTPRoute][HTTPRoute] or [GRPCRoute][GRPCRoute] resource. + +## Prerequisites + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Configuration + +The below example defines a SecurityPolicy that allows CORS requests from `www.foo.com`. + +```shell +cat < /dev/null +``` + +You should see the below response, indicating that the request from `http://www.foo.com` is allowed: + +```shell +< access-control-allow-origin: http://www.foo.com +< access-control-allow-methods: GET, POST +< access-control-allow-headers: x-header-1, x-header-2 +< access-control-max-age: 86400 +< access-control-expose-headers: x-header-3, x-header-4 +``` + +If you try to send a request from `http://www.bar.com`, you should see the below response: + +```shell +curl -H "Origin: http://www.bar.com" \ + -H "Host: www.example.com" \ + -H "Access-Control-Request-Method: GET" \ + -X OPTIONS -v -s \ + http://$GATEWAY_HOST \ + 1> /dev/null +``` + +You won't see any CORS headers in the response, indicating that the request from `http://www.bar.com` was not allowed. + +Note: CORS specification requires that the browsers to send a preflight request to the server to ask if it's allowed +to access the limited resource in another domains. The browsers are supposed to follow the response from the server to +determine whether to send the actual request or not. The CORS filter only response to the preflight requests according to +its configuration. It won't deny any requests. The browsers are responsible for enforcing the CORS policy. + + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the SecurityPolicy: + +```shell +kubectl delete securitypolicy/cors-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[SecurityPolicy]: ../../design/security-policy/ +[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/api-types/grpcroute diff --git a/site/content/en/latest/user/customize-envoyproxy.md b/site/content/en/latest/user/customize-envoyproxy.md new file mode 100644 index 00000000000..6565d08b0dc --- /dev/null +++ b/site/content/en/latest/user/customize-envoyproxy.md @@ -0,0 +1,354 @@ +--- +title: "Customize EnvoyProxy" +--- + +Envoy Gateway provides an [EnvoyProxy][] CRD that can be linked to the ParametersRef +in GatewayClass, allowing cluster admins to customize the managed EnvoyProxy Deployment and +Service. To learn more about GatewayClass and ParametersRef, please refer to [Gateway API documentation][]. + +## Installation + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Add GatewayClass ParametersRef + +First, you need to add ParametersRef in GatewayClass, and refer to EnvoyProxy Config: + +```shell +cat < Envoy Gateway has provided two initial `env` `ENVOY_GATEWAY_NAMESPACE` and `ENVOY_POD_NAME` for envoyproxy container. + +After applying the config, you can get the envoyproxy deployment, and see resources has been changed. + +## Customize EnvoyProxy Deployment Volumes or VolumeMounts + +You can customize the EnvoyProxy Deployment Volumes or VolumeMounts via EnvoyProxy Config like: + +```shell +cat < GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8888 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:19:42 GMT +< content-length: 521 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.marketing.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.157" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "c637977c-458a-48ae-92b3-f8c429849322" + ] + }, + "namespace": "marketing", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-bcs8f" +* Connection #0 to host localhost left intact +``` + +* Lets deploy Envoy Gateway in the `product` namespace and also watch resources only in this namespace. + +``` +helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller --set config.envoyGateway.provider.kubernetes.watch.namespaces={product} eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n product --create-namespace +``` + +Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. + +```shell +cat < GET /get HTTP/1.1 +> Host: www.product.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:20:17 GMT +< content-length: 517 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.product.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.156" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39196453-2250-4331-b756-54003b2853c2" + ] + }, + "namespace": "product", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-64fjs" +* Connection #0 to host localhost left intact +``` + +With the below command you can ensure that you are no able to access the marketing team's backend exposed using the `www.marketing.example.com` hostname +and the product team's data plane. + +```shell +curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get +``` + +``` +* Trying 127.0.0.1:8889... +* Connected to localhost (127.0.0.1) port 8889 (#0) +> GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 404 Not Found +< date: Thu, 20 Apr 2023 19:22:13 GMT +< server: envoy +< content-length: 0 +< +* Connection #0 to host localhost left intact +``` + +[GatewayClass]: https://gateway-api.sigs.k8s.io/api-types/gatewayclass/ +[issue1231]: https://github.com/envoyproxy/gateway/issues/1231 +[issue1117]: https://github.com/envoyproxy/gateway/issues/1117 diff --git a/site/content/en/latest/user/egctl.md b/site/content/en/latest/user/egctl.md new file mode 100644 index 00000000000..739a137d131 --- /dev/null +++ b/site/content/en/latest/user/egctl.md @@ -0,0 +1,744 @@ +--- +title: "Use egctl" +--- + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + + + +## egctl experimental translate + +This subcommand allows users to translate from an input configuration type to an output configuration type. + +In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS +resources. + +```shell +cat <// + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/local_reply_config" + value: + mappers: + - filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + runtime_key: key_b + status_code: 406 + body: + inline_string: "could not find what you are looking for" +EOF +``` + +* Lets edit the HTTPRoute resource from the Quickstart to only match on paths with value `/get` + +``` +kubectl patch httproute backend --type=json --patch '[{ + "op": "add", + "path": "/spec/rules/0/matches/0/path/value", + "value": "/get", +}]' +``` + +* Lets test it out by specifying a path apart from `/get` + +``` +$ curl --header "Host: www.example.com" http://localhost:8888/find +Handling connection for 8888 +could not find what you are looking for +``` + +## Debugging + +### Runtime + +* The `Status` subresource should have information about the status of the resource. Make sure +`Accepted=True` and `Programmed=True` conditions are set to ensure that the policy has been +applied to Envoy Proxy. + +``` +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"gateway.envoyproxy.io/v1alpha1","kind":"EnvoyPatchPolicy","metadata":{"annotations":{},"name":"custom-response-patch-policy","namespace":"default"},"spec":{"jsonPatches":[{"name":"default/eg/http","operation":{"op":"add","path":"/default_filter_chain/filters/0/typed_config/local_reply_config","value":{"mappers":[{"body":{"inline_string":"could not find what you are looking for"},"filter":{"status_code_filter":{"comparison":{"op":"EQ","value":{"default_value":404}}}}}]}},"type":"type.googleapis.com/envoy.config.listener.v3.Listener"}],"priority":0,"targetRef":{"group":"gateway.networking.k8s.io","kind":"Gateway","name":"eg","namespace":"default"},"type":"JSONPatch"}} + creationTimestamp: "2023-07-31T21:47:53Z" + generation: 1 + name: custom-response-patch-policy + namespace: default + resourceVersion: "10265" + uid: a35bda6e-a0cc-46d7-a63a-cee765174bc3 +spec: + jsonPatches: + - name: default/eg/http + operation: + op: add + path: /default_filter_chain/filters/0/typed_config/local_reply_config + value: + mappers: + - body: + inline_string: could not find what you are looking for + filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + type: type.googleapis.com/envoy.config.listener.v3.Listener + priority: 0 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch +status: + conditions: + - lastTransitionTime: "2023-07-31T21:48:19Z" + message: EnvoyPatchPolicy has been accepted. + observedGeneration: 1 + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2023-07-31T21:48:19Z" + message: successfully applied patches. + reason: Programmed + status: "True" + type: Programmed +``` + +### Offline + +* You can use [egctl x translate][] to validate the translated xds output. + +## Caveats + +This API will always be an unstable API and the same outcome cannot be garunteed +across versions for these reasons +* The Envoy Proxy API might deprecate and remove API fields +* Envoy Gateway might alter the xDS translation creating a different xDS output +such as changing the `name` field of resources. + +[EnvoyPatchPolicy]: ../../api/extension_types#envoypatchpolicy +[EnvoyGateway]: ../../api/extension_types#envoygateway +[JSON Patch]: https://datatracker.ietf.org/doc/html/rfc6902 +[xDS]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/dynamic_configuration +[Local Reply Modification]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/local_reply +[egctl x translate]: ../egctl#egctl-experimental-translate diff --git a/site/content/en/latest/user/gateway-address.md b/site/content/en/latest/user/gateway-address.md new file mode 100644 index 00000000000..95aeb3aa4a3 --- /dev/null +++ b/site/content/en/latest/user/gateway-address.md @@ -0,0 +1,69 @@ +--- +title: "Gateway Address" +--- + +The Gateway API provides an optional [Addresses][] field through which Envoy Gateway can set addresses for Envoy Proxy Service. The currently supported addresses are: + +- [External IPs](#External-IPs) + +## Installation + +Install Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## External IPs + +Using the addresses in `Gateway.Spec.Addresses` as the [External IPs][] of Envoy Proxy Service, this will __require__ the address to be of type `IPAddress`. + +Install the GatewayClass, Gateway from quickstart: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default +``` + +Set the address of the Gateway, the address settings here are for reference only: + +```shell +kubectl patch gateway eg --type=json --patch '[{ + "op": "add", + "path": "/spec/addresses", + "value": [{ + "type": "IPAddress", + "value": "1.2.3.4" + }] +}]' +``` + +Verify the Gateway status: + +```shell +kubectl get gateway + +NAME CLASS ADDRESS PROGRAMMED AGE +eg eg 1.2.3.4 True 14m +``` + +Verify the Envoy Proxy Service status: + +```shell +kubectl get service -n envoy-gateway-system + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +envoy-default-eg-64656661 LoadBalancer 10.96.236.219 1.2.3.4 80:31017/TCP 15m +envoy-gateway ClusterIP 10.96.192.76 18000/TCP 15m +envoy-gateway-metrics-service ClusterIP 10.96.124.73 8443/TCP 15m +``` + +__Note:__ If the `Gateway.Spec.Addresses` is explicitly set, it will be the only addresses that populates the Gateway status. + +[Addresses]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.GatewayAddress +[External IPs]: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips diff --git a/site/content/en/latest/user/gateway-api-metrics.md b/site/content/en/latest/user/gateway-api-metrics.md new file mode 100644 index 00000000000..a7764edbfc1 --- /dev/null +++ b/site/content/en/latest/user/gateway-api-metrics.md @@ -0,0 +1,59 @@ +--- +title: "Gateway API Metrics" +--- + +Resource metrics for Gateway API objects are available using the [Gateway API State Metrics][gasm] project. +The project also provides example dashboard for visualising the metrics using Grafana, and example alerts using Prometheus & Alertmanager. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +Run the following commands to install the metrics stack, with the Gateway API State Metrics configuration, on your kubernetes cluster: + +```shell +kubectl apply --server-side -f https://raw.githubusercontent.com/Kuadrant/gateway-api-state-metrics/main/config/examples/kube-prometheus/bundle_crd.yaml +kubectl apply -f https://raw.githubusercontent.com/Kuadrant/gateway-api-state-metrics/main/config/examples/kube-prometheus/bundle.yaml +``` + +## Metrics and Alerts + +To access the Prometheus UI, wait for the statefulset to be ready, then use the port-forward command: + +```shell +# This first command may fail if the statefulset has not been created yet. +# In that case, try again until you get a message like 'Waiting for 2 pods to be ready...' +# or 'statefulset rolling update complete 2 pods...' +kubectl -n monitoring rollout status --watch --timeout=5m statefulset/prometheus-k8s +kubectl -n monitoring port-forward service/prometheus-k8s 9090:9090 > /dev/null & +``` + +Navigate to [http://localhost:9090](http://localhost:9090). +Metrics can be queried from the 'Graph' tab e.g. `gatewayapi_gateway_created` +See the [Gateway API State Metrics README][gasm-readme] for the full list of Gateway API metrics available. + +Alerts can be seen in the 'Alerts' tab. +Gateway API specific alerts will be grouped under the 'gateway-api.rules' heading. + +***Note:*** Alerts are defined in a PrometheusRules custom resource in the 'monitoring' namespace. You can modify the alert rules by updating this resource. + +## Dashboards + +To view the dashboards in Grafana, wait for the deployment to be ready, then use the port-forward command: + +```shell +kubectl -n monitoring wait --timeout=5m deployment/grafana --for=condition=Available +kubectl -n monitoring port-forward service/grafana 3000:3000 > /dev/null & +``` + +Navigate to [http://localhost:3000](http://localhost:3000) and sign in with admin/admin. +The Gateway API State dashboards will be available in the 'Default' folder and tagged with 'gateway-api'. +See the [Gateway API State Metrics README][gasm-dashboards] for further information on available dashboards. + +***Note:*** Dashboards are loaded from configmaps. You can modify the dashboards in the Grafana UI, however you will need to export them from the UI and update the json in the configmaps to persist changes. + + +[gasm]: https://github.com/Kuadrant/gateway-api-state-metrics +[gasm-readme]: https://github.com/Kuadrant/gateway-api-state-metrics/tree/main#metrics +[gasm-dashboards]: https://github.com/Kuadrant/gateway-api-state-metrics/tree/main#dashboards diff --git a/site/content/en/latest/user/gatewayapi-support.md b/site/content/en/latest/user/gatewayapi-support.md new file mode 100644 index 00000000000..a8d27dd6a36 --- /dev/null +++ b/site/content/en/latest/user/gatewayapi-support.md @@ -0,0 +1,119 @@ +--- +title: "Gateway API Support" +--- + +As mentioned in the [system design][] document, Envoy Gateway's managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][] objects. Envoy Gateway supports configuration using the following Gateway API resources. + +## GatewayClass + +A [GatewayClass][] represents a "class" of gateways, i.e. which Gateways should be managed by Envoy Gateway. +Envoy Gateway supports managing __a single__ GatewayClass resource that matches its configured `controllerName` and +follows Gateway API guidelines for [resolving conflicts][] when multiple GatewayClasses exist with a matching +`controllerName`. + +__Note:__ If specifying GatewayClass [parameters reference][], it must refer to an [EnvoyProxy][] resource. + +## Gateway + +When a [Gateway][] resource is created that references the managed GatewayClass, Envoy Gateway will create and manage a +new Envoy Proxy deployment. Gateway API resources that reference this Gateway will configure this managed Envoy Proxy +deployment. + +## HTTPRoute + +A [HTTPRoute][] configures routing of HTTP traffic through one or more Gateways. The following HTTPRoute filters are +supported by Envoy Gateway: + +- `requestHeaderModifier`: [RequestHeaderModifiers][http-filter] + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers][http-filter] + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors][http-filter] + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. +- `requestRedirect`: [RequestRedirects][http-filter] + configure policied for how requests that match the HTTPRoute should be modified and then redirected. +- `urlRewrite`: [UrlRewrites][http-filter] + allow for modification of the request's hostname and path before it is proxied to its destination. +- `extensionRef`: [ExtensionRefs][] are used by Envoy Gateway to implement extended filters. Currently, Envoy Gateway + supports rate limiting and request authentication filters. For more information about these filters, refer to the + [rate limiting][] and [request authentication][] documentation. + +__Notes:__ +- The only [BackendRef][] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other destinations such + as arbitrary URLs is not possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TCPRoute + +A [TCPRoute][] configures routing of raw TCP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a TCP port number. + +__Note:__ A TCPRoute only supports proxying in non-transparent mode, i.e. the backend will see the source IP and port of +the Envoy Proxy instance instead of the client. + +## UDPRoute + +A [UDPRoute][] configures routing of raw UDP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a UDP port number. + +__Note:__ Similar to TCPRoutes, UDPRoutes only support proxying in non-transparent mode i.e. the backend will see the +source IP and port of the Envoy Proxy instance instead of the client. + +## GRPCRoute + +A [GRPCRoute][] configures routing of [gRPC][] requests through one or more Gateways. They offer request matching by +hostname, gRPC service, gRPC method, or HTTP/2 Header. Envoy Gateway supports the following filters on GRPCRoutes to +provide additional traffic processing: + +- `requestHeaderModifier`: [RequestHeaderModifiers][grpc-filter] + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers][grpc-filter] + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors][grpc-filter] + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. + +__Notes:__ +- The only [BackendRef][grpc-filter] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other + destinations such as arbitrary URLs is not currently possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TLSRoute + +A [TLSRoute][] configures routing of TCP traffic through one or more Gateways. However, unlike TCPRoutes, TLSRoutes +can match against TLS-specific metadata. + +## ReferenceGrant + +A [ReferenceGrant][] is used to allow a resource to reference another resource in a different namespace. Normally an +HTTPRoute created in namespace `foo` is not allowed to reference a Service in namespace `bar`. A ReferenceGrant permits +these types of cross-namespace references. Envoy Gateway supports the following ReferenceGrant use-cases: + +- Allowing an HTTPRoute, GRPCRoute, TLSRoute, UDPRoute, or TCPRoute to reference a Service in a different namespace. +- Allowing an HTTPRoute's `requestMirror` filter to include a BackendRef that references a Service in a different + namespace. +- Allowing a Gateway's [SecretObjectReference][] to reference a secret in a different namespace. + +[system design]: ../../design/system-design/ +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[GatewayClass]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.GatewayClass +[parameters reference]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParametersReference +[Gateway]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRoute +[Service]: https://kubernetes.io/docs/concepts/services-networking/service/ +[BackendRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendRef +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPBackendRef +[TCPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute +[UDPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[gRPC]: https://grpc.io/ +[TLSRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute +[ReferenceGrant]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.ReferenceGrant +[SecretObjectReference]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.SecretObjectReference +[rate limiting]: ../rate-limit/ +[request authentication]: ../jwt-authentication/ +[EnvoyProxy]: ../../api/extension_types#envoyproxy +[resolving conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/?h=conflict#conflicts +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilterType +[grpc-filter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter +[http-filter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter diff --git a/site/content/en/latest/user/grafana-integration.md b/site/content/en/latest/user/grafana-integration.md new file mode 100644 index 00000000000..3c1aa0a8b6d --- /dev/null +++ b/site/content/en/latest/user/grafana-integration.md @@ -0,0 +1,65 @@ +--- +title: "Visualising metrics using Grafana" +--- + +Envoy Gateway provides support for exposing Envoy Proxy metrics to a Prometheus instance. +This guide shows you how to visualise the metrics exposed to prometheus using grafana. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +Follow the steps from the [Proxy Observability](../proxy-observability#Metrics) to enable prometheus metrics. + +[Prometheus](https://prometheus.io) is used to scrape metrics from the Envoy Proxy instances. Install Prometheus: + +```shell +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +helm upgrade --install prometheus prometheus-community/prometheus -n monitoring --create-namespace +``` + +[Grafana](https://grafana.com/grafana/) is used to visualise the metrics exposed by the envoy proxy instances. +Install Grafana: + +```shell +helm repo add grafana https://grafana.github.io/helm-charts +helm repo update +helm upgrade --install grafana grafana/grafana -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/grafana/helm-values.yaml -n monitoring --create-namespace +``` + +Expose endpoints: + +```shell +GRAFANA_IP=$(kubectl get svc grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +## Connecting Grafana with Prometheus datasource + +To visualise metrics from Prometheus, we have to connect Grafana with Prometheus. If you installed Grafana from the command +from prerequisites sections, the prometheus datasource should be already configured. + +You can also add the data source manually by following the instructions from [Grafana Docs](https://grafana.com/docs/grafana/latest/datasources/prometheus/configure-prometheus-data-source/). + +## Accessing Grafana + +You can access the Grafana instance by visiting `http://{GRAFANA_IP}`, derived in prerequisites. + +To log in to Grafana, use the credentials `admin:admin`. + +Envoy Gateway has examples of dashboard for you to get started: + +### [Envoy Global](https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/grafana/dashboards/envoy-global.json) + +![Envoy Global](/img/envoy-global-dashboard.png) + +### [Envoy Clusters](https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/grafana/dashboards/envoy-clusters.json) + +![Envoy Clusters](/img/envoy-clusters-dashboard.png) + +### [Envoy Pod Resources](https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/grafana/dashboards/envoy-pod-resource.json) + +![Envoy Pod Resources](/img/envoy-pod-resources-dashboard.png) + +You can load the above dashboards in your Grafana to get started. Please refer to Grafana docs for [importing dashboards](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#import-a-dashboard). diff --git a/site/content/en/latest/user/grpc-routing.md b/site/content/en/latest/user/grpc-routing.md new file mode 100644 index 00000000000..5dca26744bc --- /dev/null +++ b/site/content/en/latest/user/grpc-routing.md @@ -0,0 +1,204 @@ +--- +title: "GRPC Routing" +--- + +The [GRPCRoute][] resource allows users to configure gRPC routing by matching HTTP/2 traffic and forwarding it to backend gRPC servers. +To learn more about gRPC routing, refer to the [Gateway API documentation][]. + +## Prerequisites + +Install Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## Installation + +Install the gRPC routing example resources: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/grpc-routing.yaml +``` + +The manifest installs a [GatewayClass][], [Gateway][], a Deployment, a Service, and a GRPCRoute resource. +The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. + +__Note:__ Envoy Gateway is configured by default to manage a GatewayClass with +`controllerName: gateway.envoyproxy.io/gatewayclass-controller`. + +## Verification + +Check the status of the GatewayClass: + +```shell +kubectl get gc --selector=example=grpc-routing +``` + +The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass. + +A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is +provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this +Gateway. Check the status of the Gateway: + +```shell +kubectl get gateways --selector=example=grpc-routing +``` + +The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also +provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend +services. + +Check the status of the GRPCRoute: + +```shell +kubectl get grpcroutes --selector=example=grpc-routing -o yaml +``` + +The status for the GRPCRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway. +The `example-route` matches any traffic for "grpc-example.com" and forwards it to the "yages" Service. + +## Testing the Configuration + +Before testing GRPC routing to the `yages` backend, get the Gateway's address. + +```shell +export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}') +``` + +Test GRPC routing to the `yages` backend using the [grpcurl][] command. + +```shell +grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +{ + "text": "pong" +} +``` + +Envoy Gateway also supports [gRPC-Web][] requests for this configuration. The below `curl` command can be used to send a grpc-Web request with over HTTP/2. You should receive the same response seen in the previous command. + +The data in the body `AAAAAAA=` is a base64 encoded representation of an empty message (data length 0) that the Ping RPC accepts. + +```shell +curl --http2-prior-knowledge -s ${GATEWAY_HOST}:80/yages.Echo/Ping -H 'Host: grpc-example.com' -H 'Content-Type: application/grpc-web-text' -H 'Accept: application/grpc-web-text' -XPOST -d'AAAAAAA=' | base64 -d +``` + +## GRPCRoute Match +The `matches` field can be used to restrict the route to a specific set of requests based on GRPC's service and/or method names. +It supports two match types: `Exact` and `RegularExpression`. + +### Exact + +`Exact` match is the default match type. + +The following example shows how to match a request based on the service and method names for `grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo`, +as well as a match for all services with a method name `Ping` which matches `yages.Echo/Ping` in our deployment. + +```shell +cat < HTTPS + +Listeners expose the TLS setting on a per domain or subdomain basis. TLS settings of a listener are applied to all domains that satisfy the hostname criteria. + +Create a root certificate and private key to sign certificates: + +```shell +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/CN=example.com' -keyout CA.key -out CA.crt +openssl req -out example.com.csr -newkey rsa:2048 -nodes -keyout tls.key -subj "/CN=example.com" +``` + +Generate a self-signed wildcard certificate for `example.com` with `*.example.com` extension + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something", + "foo" + ], +... +``` + +## Setting Request Headers + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + "headers": { + "Accept": [ + "*/*" + ], + "Set-Header": [ + "foo" + ], +... +``` + +## Removing Request Headers + +Headers can be removed from a request by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something" + ], +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +Check the logs of the pods and you will see that the original deployment and the new deployment each got a request: + +```shell +$ kubectl logs deploy/backend && kubectl logs deploy/backend-2 +... +Starting server, listening on port 3000 (http) +Echoing back request made to /get to client (10.42.0.10:41566) +Starting server, listening on port 3000 (http) +Echoing back request made to /get to client (10.42.0.10:45096) +``` + +## Multiple BackendRefs + +When an `HTTPRoute` has multiple `backendRefs` and an `HTTPRequestMirrorFilter`, traffic splitting will still behave the same as it normally would for the main `backendRefs` while the `backendRef` of the `HTTPRequestMirrorFilter` will continue receiving mirrored copies of the incoming requests. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: X-Foo: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< x-foo: value1 +< add-header: foo +< +... + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "X-Foo: value1" + ] +... +``` + +## Setting Response Headers + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: set-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< set-header: foo +< + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "set-header": value1" + ] +... +``` + +## Removing Response Headers + +Headers can be removed from a response by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: remove-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "remove-header": value1" + ] +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +## Multiple backendRefs + +If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is +configured. + +First, create a second instance of the example app from the quickstart: + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-75bcd4c969-lsxpz" +... +``` + +## Weighted backendRefs + +If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` +field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is +assumed to be `1`. + +The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of +requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the +HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. + +The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the +backend-2 service. + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< server: envoy +< content-length: 0 +< +``` + +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[backendRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendRef diff --git a/site/content/en/latest/user/http-urlrewrite.md b/site/content/en/latest/user/http-urlrewrite.md new file mode 100644 index 00000000000..945a24a7a44 --- /dev/null +++ b/site/content/en/latest/user/http-urlrewrite.md @@ -0,0 +1,297 @@ +--- +title: "HTTP URL Rewrite" +--- + +[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be +used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Rewrite URL Prefix Path + +You can configure to rewrite the prefix in the url like below. In this example, any curls to +`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. + +```shell +cat < GET /get/origin/path HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> + +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:03:28 GMT +< content-length: 503 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/replace/origin/path", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "fd84b842-9937-4fb5-83c7-61470d854b90" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. + +## Rewrite URL Full Path + +You can configure to rewrite the fullpath in the url like below. In this example, any request sent to +`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to +`http://${GATEWAY_HOST}/force/replace/fullpath`. + +```shell +cat < GET /get/origin/path/extra HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:09:31 GMT +< content-length: 512 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/force/replace/fullpath", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path/extra" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "8ab774d6-9ffa-4faa-abbb-f45b0db00895" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is +`/force/replace/fullpath`. + +## Rewrite Host Name + +You can configure to rewrite the hostname like below. In this example, any requests sent to +`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. + +```shell +cat < GET /get HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:15:15 GMT +< content-length: 481 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "envoygateway.io", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Forwarded-Host": [ + "path.rewrite.example" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39aa447c-97b9-45a3-a675-9fb266ab1af0" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. + +[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPURLRewriteFilter diff --git a/site/content/en/latest/user/jwt-authentication.md b/site/content/en/latest/user/jwt-authentication.md new file mode 100644 index 00000000000..368b6af9d09 --- /dev/null +++ b/site/content/en/latest/user/jwt-authentication.md @@ -0,0 +1,170 @@ +--- +title: "JWT Authentication" +--- + +This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks +if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only +supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. + +Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure JWT authentication. +This instantiated resource can be linked to a [Gateway][Gateway], [HTTPRoute][HTTPRoute] or [GRPCRoute][GRPCRoute] resource. + +## Prerequisites + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +For GRPC - follow the steps from the [GRPC Routing](../grpc-routing) example. +Before proceeding, you should be able to query the example backend using HTTP or GRPC. + +## Configuration + +Allow requests with a valid JWT by creating an [SecurityPolicy][SecurityPolicy] and attaching it to the example +HTTPRoute or GRPCRoute. + +### HTTPRoute + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/jwt/jwt.yaml +``` + +Two HTTPRoute has been created, one for `/foo` and another for `/bar`. A SecurityPolicy has been created and targeted +HTTPRoute foo to authenticate requests for `/foo`. The HTTPRoute bar is not targeted by the SecurityPolicy and will allow +unauthenticated requests to `/bar`. + +Verify the HTTPRoute configuration and status: + +```shell +kubectl get httproute/foo -o yaml +kubectl get httproute/bar -o yaml +``` + +The SecurityPolicy is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the SecurityPolicy configuration: + +```shell +kubectl get securitypolicy/jwt-example -o yaml +``` + +### GRPCRoute + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/jwt/grpc-jwt.yaml +``` + +A SecurityPolicy has been created and targeted GRPCRoute yages to authenticate all requests for `yages` service.. + +Verify the GRPCRoute configuration and status: + +```shell +kubectl get grpcroute/yages -o yaml +``` + +The SecurityPolicy is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the SecurityPolicy configuration: + +```shell +kubectl get securitypolicy/jwt-example -o yaml +``` + +## Testing + +Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](../quickstart) guide is set. If not, follow the +Quickstart instructions to set the variable. + +```shell +echo $GATEWAY_HOST +``` + +### HTTPRoute + +Verify that requests to `/foo` are denied without a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `401` HTTP response code should be returned. + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `/foo` with a valid JWT is allowed: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `200` HTTP response code should be returned. + +Verify that requests to `/bar` are allowed __without__ a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar +``` + +### GRPCRoute + +Verify that requests to `yages`service are denied without a JWT: + +```shell +grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +Error invoking method "yages.Echo/Ping": rpc error: code = Unauthenticated desc = failed to query for service descriptor "yages.Echo": Jwt is missing +``` + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `yages` service with a valid JWT is allowed: + +```shell +grpcurl -plaintext -H "authorization: Bearer $TOKEN" -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +{ + "text": "pong" +} +``` + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the SecurityPolicy: + +```shell +kubectl delete securitypolicy/jwt-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[SecurityPolicy]: ../../design/security-policy/ +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/api-types/grpcroute diff --git a/site/content/en/latest/user/multicluster-service.md b/site/content/en/latest/user/multicluster-service.md new file mode 100644 index 00000000000..d0fd7a83fb1 --- /dev/null +++ b/site/content/en/latest/user/multicluster-service.md @@ -0,0 +1,86 @@ +--- +title: "Multicluster Service Routing" +--- + +The Multicluster Service API ServiceImport object can be used as part of the GatewayAPI backendRef for configuring routes. For more information about multicluster service API follow [sig documentation](https://multicluster.sigs.k8s.io/concepts/multicluster-services-api/). + +We will use [Submariner project](https://github.com/submariner-io/submariner) for setting up the multicluster environment for exporting the service to be routed from peer clusters. + +## Setting KIND clusters and installing Submariner. + +- We will be using KIND clusters to demonstrate this example. + +```shell +git clone https://github.com/submariner-io/submariner-operator +cd submariner-operator +make clusters +``` + +Note: remain in submariner-operator directory for the rest of the steps in this section + +- Install subctl: + +```shell +curl -Ls https://get.submariner.io | VERSION=v0.14.6 bash +``` + +- Set up multicluster service API and submariner for cross cluster traffic using ServiceImport + +```shell +subctl deploy-broker --kubeconfig output/kubeconfigs/kind-config-cluster1 --globalnet +subctl join --kubeconfig output/kubeconfigs/kind-config-cluster1 broker-info.subm --clusterid cluster1 --natt=false +subctl join --kubeconfig output/kubeconfigs/kind-config-cluster2 broker-info.subm --clusterid cluster2 --natt=false +``` + +Once the above steps are done and all the pods are up in both the clusters. We are ready for installing envoy gateway. + +## Install EnvoyGateway + +Install the Gateway API CRDs and Envoy Gateway in cluster1: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --kubeconfig output/kubeconfigs/kind-config-cluster1 +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available --kubeconfig output/kubeconfigs/kind-config-cluster1 +``` + +## Install Application + +Install the backend application in cluster2 and export it through subctl command. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/application.yaml --kubeconfig output/kubeconfigs/kind-config-cluster2 +subctl export service backend --namespace default --kubeconfig output/kubeconfigs/kind-config-cluster2 +``` + +## Create Gateway API Objects + +Create the Gateway API objects GatewayClass, Gateway and HTTPRoute in cluster1 to set up the routing. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/multicluster-service.yaml --kubeconfig output/kubeconfigs/kind-config-cluster1 +``` + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` diff --git a/site/content/en/latest/user/oidc.md b/site/content/en/latest/user/oidc.md new file mode 100644 index 00000000000..3983d1530fa --- /dev/null +++ b/site/content/en/latest/user/oidc.md @@ -0,0 +1,124 @@ +--- +title: "OIDC Authentication" +--- + +This guide provides instructions for configuring [OpenID Connect (OIDC)][oidc] authentication. +OpenID Connect (OIDC) is an authentication standard built on top of OAuth 2.0. +It enables client applications to rely on authentication that is performed by an OpenID Connect Provider (OP) +to verify the identity of a user. + +Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure OIDC +authentication. +This instantiated resource can be linked to a [Gateway][Gateway] and [HTTPRoute][HTTPRoute] resource. + +## Prerequisites + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +OIDC authentication requires the redirect URL to be HTTPS. Follow the [Secure Gateways](../secure-gateways) guide + to generate the TLS certificates and update the Gateway configuration to add an HTTPS listener. + +Verify the Gateway status: + +```shell +kubectl get gateway/teg -o yaml +``` + +## Configuration + +This guide uses Google as the OIDC provider to demonstrate the configuration of OIDC. However, EG works with any OIDC +providers, including Auth0, Azure AD, Keycloak, Okta, OneLogin, Salesforce, UAA, etc. + +### Register an OIDC application + +Follow the steps in the [Google OIDC documentation][google-oidc] to register an OIDC application. Please use +`https://www.example.com/oauth2/callback` as the redirect URL when registering the application. `oauth2/callback` is the +default callback path used by Envoy Gateway. + +After registering the application, you should have the following information: +* Client ID: The client ID of the OIDC application. +* Client Secret: The client secret of the OIDC application. + +### Create a kubernetes secret + +Next, create a kubernetes secret with the Client Secret created in the previous step. The secret is an Opaque secret, +and the Client Secret must be stored in the key "client-secret". + +Note: please replace the ${CLIENT_SECRET} with the actual Client Secret that you got from the previous step. + +```shell +$ kubectl create secret generic my-app-client-secret --from-literal=client-secret=${CLIENT_SECRET} +secret "my-app-client-secret" created +``` + +### Create a SecurityPolicy + +Note: please replace the ${CLIENT_ID} with the actual Client ID that you got from the previous step. + +```shell +cat <" | jq +``` diff --git a/site/content/en/latest/user/quickstart.md b/site/content/en/latest/user/quickstart.md new file mode 100644 index 00000000000..c0850751c75 --- /dev/null +++ b/site/content/en/latest/user/quickstart.md @@ -0,0 +1,103 @@ +--- +title: "Quickstart" +weight: 1 +--- + +This guide will help you get started with Envoy Gateway in a few simple steps. + +## Prerequisites + +A Kubernetes cluster. + +__Note:__ Refer to the [Compatibility Matrix](/blog/2022/10/01/versions/) for supported Kubernetes versions. + +__Note:__ In case your Kubernetes cluster, does not have a LoadBalancer implementation, we recommend installing one +so the `Gateway` resource has an Address associated with it. We recommend using [MetalLB](https://metallb.universe.tf/installation/). + +## Installation + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` + +### External LoadBalancer Support + +You can also test the same functionality by sending traffic to the External IP. To get the external IP of the +Envoy service, run: + +```shell +export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace +`ip` in the above command with `hostname`. + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get +``` + +## Clean-Up + +Use the steps in this section to uninstall everything from the quickstart guide. + +Delete the GatewayClass, Gateway, HTTPRoute and Example App: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml --ignore-not-found=true +``` + +Delete the Gateway API CRDs and Envoy Gateway: + +```shell +helm uninstall eg -n envoy-gateway-system +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. diff --git a/site/content/en/latest/user/rate-limit.md b/site/content/en/latest/user/rate-limit.md new file mode 100644 index 00000000000..f74fbc4f6a2 --- /dev/null +++ b/site/content/en/latest/user/rate-limit.md @@ -0,0 +1,826 @@ +--- +title: "Rate Limit" +--- + +Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + +Here are some reasons why you may want to implement Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied +i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit +if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. + +Envoy Gateway introduces a new CRD called [BackendTrafficPolicy][] that allows the user to describe their rate limit intent. This instantiated resource +can be linked to a [Gateway][], [HTTPRoute][] or [GRPCRoute][] resource. + +## Prerequisites + +### Install Envoy Gateway + +* Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the HTTPRoute example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +### Install Redis + +* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. +Lets install a Redis deployment in the `redis-system` namespce. + +```shell +cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com +; (1 server found) +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 +;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 +;; WARNING: recursion requested but not available + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +; COOKIE: 24fb86eba96ebf62 (echoed) +;; QUESTION SECTION: +;foo.bar.com. IN A + +;; ADDITIONAL SECTION: +foo.bar.com. 0 IN A 10.244.0.19 +_udp.foo.bar.com. 0 IN SRV 0 0 42376 . + +;; Query time: 1 msec +;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) +;; WHEN: Fri Jan 13 10:20:34 UTC 2023 +;; MSG SIZE rcvd: 114 +``` + +## Clean-Up + +Follow the steps from the [Quickstart Guide](../quickstart) to uninstall Envoy Gateway. + +Delete the CoreDNS example manifest and the UDPRoute: + +```shell +kubectl delete deploy/coredns +kubectl delete service/coredns +kubectl delete cm/coredns +kubectl delete udproute/coredns +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/udp_filters/udp_proxy diff --git a/site/content/en/search.md b/site/content/en/search.md new file mode 100644 index 00000000000..394feea5fdb --- /dev/null +++ b/site/content/en/search.md @@ -0,0 +1,4 @@ +--- +title: Search Results +layout: search +--- diff --git a/site/content/en/v0.2.0/_index.md b/site/content/en/v0.2.0/_index.md new file mode 100644 index 00000000000..53d07c8aae3 --- /dev/null +++ b/site/content/en/v0.2.0/_index.md @@ -0,0 +1,21 @@ ++++ +title = "Welcome to Envoy Gateway" +description = "Envoy Gateway Documents" +linktitle = "Documentation" + +[[cascade]] +type = "docs" ++++ + +{{% alert title="Note" color="primary" %}} + +This project is under **active** development. Many features are not complete. We would love for you to [Get Involved](contributions/)! + +{{% /alert %}} + +Envoy Gateway is an open source project for managing [Envoy Proxy](https://www.envoyproxy.io/) as a standalone or Kubernetes-based application +gateway. [Gateway API](https://gateway-api.sigs.k8s.io/) resources are used to dynamically provision and configure the managed Envoy Proxies. + +![architecture](/img/traffic.png) + +## Ready to get started? diff --git a/site/content/en/v0.2.0/contributions/CODEOWNERS.md b/site/content/en/v0.2.0/contributions/CODEOWNERS.md new file mode 100644 index 00000000000..63b751abde5 --- /dev/null +++ b/site/content/en/v0.2.0/contributions/CODEOWNERS.md @@ -0,0 +1,19 @@ +--- +title: "Maintainers" +description: "This section includes Maintainers of Envoy Gateway." +--- + +## The following maintainers, listed in alphabetical order, own everything + +- @AliceProxy +- @arkodg +- @Xunzhuo +- @zirain +- @qicz + +## Emeritus Maintainers + +- @danehans +- @alexgervais +- @skriss +- @youngnick diff --git a/site/content/en/v0.2.0/contributions/CODE_OF_CONDUCT.md b/site/content/en/v0.2.0/contributions/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..e19da050dff --- /dev/null +++ b/site/content/en/v0.2.0/contributions/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ +--- +title: "Code of Conduct" +description: "This section includes Code of Conduct of Envoy Gateway." +--- + +Envoy Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/site/content/en/v0.2.0/contributions/CONTRIBUTING.md b/site/content/en/v0.2.0/contributions/CONTRIBUTING.md new file mode 100644 index 00000000000..f94b2c940e9 --- /dev/null +++ b/site/content/en/v0.2.0/contributions/CONTRIBUTING.md @@ -0,0 +1,190 @@ +--- +title: "Contributing" +description: "This section tells how to contribute to Envoy Gateway." +weight: 3 +--- + +We welcome contributions from the community. Please carefully review the [project goals](/about) +and following guidelines to streamline your contributions. + +## Communication + +* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no + one else is working on it and ask you to open a GitHub issue. +* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or + changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to + agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process + for major features is also important so that [affiliations with commit access](../codeowners) can + come to agreement on the design. If it's appropriate to write a design document, the document must + be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable + location. +* Small patches and bug fixes don't need prior communication. + +## Inclusivity + +The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere +to the following guidelines for all code, APIs, and documentation: + +* The following words and phrases are not allowed: + * *Whitelist*: use allowlist instead. + * *Blacklist*: use denylist or blocklist instead. + * *Master*: use primary instead. + * *Slave*: use secondary or replica instead. +* Documentation should be written in an inclusive style. The [Google developer + documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent + reference on this topic. +* The above policy is not considered definitive and may be amended in the future as industry best + practices evolve. Additional comments on this topic may be provided by maintainers during code + review. + +## Submitting a PR + +* Fork the repo. +* Hack +* DCO sign-off each commit. This can be done with `git commit -s`. +* Submit your PR. +* Tests will automatically run for you. +* We will **not** merge any PR that is not passing tests. +* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage + build. If your PR cannot have 100% coverage for some reason please clearly explain why when you + open it. +* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/site) folder of the repo as + well as the [changelog](/blog/releases). +* All code comments and documentation are expected to have proper English grammar and punctuation. + If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try + to find some help but there are no guarantees. +* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary + and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. + Examples: + * "docs: fix grammar error" + * "feat(translator): add new feature" + * "fix: fix xx bug" + * "chore: change ci & build tools etc" +* Your PR commit message will be used as the commit message when your PR is merged. You should + update this field if your PR diverges during review. +* Your PR description should have details on what the PR does. If it fixes an existing issue it + should end with "Fixes #XXX". +* If your PR is co-authored or based on an earlier PR from another contributor, + please attribute them with `Co-authored-by: name `. See + GitHub's [multiple author + guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) + for further details. +* When all tests are passing and all other conditions described herein are satisfied, a maintainer + will be assigned to review and merge the PR. +* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits + are new commits and/or merges. We squash and merge so the number of commits you have in the PR + doesn't matter. +* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. + We reserve the right to close PRs that are not making progress. This is generally defined as no + changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. + Closing stale PRs helps us to keep on top of all the work currently in flight. + +## Maintainer PR Review Policy + +* See [CODEOWNERS.md](../codeowners) for the current list of maintainers. +* A maintainer representing a different affiliation from the PR owner is required to review and + approve the PR. +* When the project matures, it is expected that a "domain expert" for the code the PR touches should + review the PR. This person does not require commit access, just domain knowledge. +* The above rules may be waived for PRs which only update docs or comments, or trivial changes to + tests and tools (where trivial is decided by the maintainer in question). +* If there is a question on who should review a PR please discuss in Slack. +* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. +* Please make sure that the PR title, commit message, and description are updated if the PR changes + significantly during review. +* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge + title with the original title, and the commit body with every individual commit from the PR. + The maintainer doing the merge should make sure the title follows the guidelines above and should + overwrite the body with the original commit message from the PR (cleaning it up if necessary) + while preserving the PR author's final DCO sign-off. + +## Decision making + +This is a new and complex project, and we need to make a lot of decisions very quickly. +To this end, we've settled on this process for making (possibly contentious) decisions: + +* For decisions that need a record, we create an issue. +* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. +* Maintainers can cast binding votes on that comment by reacting or replying in another comment. +* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. +* Voting will be resolved by simple majority. +* In the event of deadlocks, the question will be put to steering instead. + +## DCO: Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign-off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +```bash +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` + +## Fixing DCO + +If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best +practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +the commit history to a single commit, append the DCO sign-off as described above, and [force +push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in +your history: + +```bash +git rebase -i HEAD^^ +(interactive squash + DCO append) +git push origin -f +``` + +Note, that in general rewriting history in this way is a hindrance to the review process and this +should only be done to correct a DCO mistake. diff --git a/site/content/en/v0.2.0/contributions/DEVELOP.md b/site/content/en/v0.2.0/contributions/DEVELOP.md new file mode 100644 index 00000000000..6f82c4a411f --- /dev/null +++ b/site/content/en/v0.2.0/contributions/DEVELOP.md @@ -0,0 +1,163 @@ +--- +title: "Developer Guide" +description: "This section tells how to develop Envoy Gateway." +weight: 2 +--- + +Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. + +## Prerequisites + +### go + +* Version: 1.20 +* Installation Guide: https://go.dev/doc/install + +### make + +* Recommended Version: 4.0 or later +* Installation Guide: https://www.gnu.org/software/make + +### docker + +* Optional when you want to build a Docker image or run `make` inside Docker. +* Recommended Version: 20.10.16 +* Installation Guide: https://docs.docker.com/engine/install + +### python3 + +* Need a `python3` program +* Must have a functioning `venv` module; this is part of the standard + library, but some distributions (such as Debian and Ubuntu) replace + it with a stub and require you to install a `python3-venv` package + separately. + +## Quickstart + +* Run `make help` to see all the available targets to build, test and run Envoy Gateway. + +### Building + +* Run `make build` to build all the binaries. +* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. +* Run `make build BINS="egctl"` to build the egctl binary. + +__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. + +### Testing + +* Run `make test` to run the golang tests. + +* Run `make testdata` to generate the golden YAML testdata files. + +### Running Linters + +* Run `make lint` to make sure your code passes all the linter checks. +__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). + +### Building and Pushing the Image + +* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. +* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. + +__Note:__ Replace `IMAGE` with your registry's image name. + +### Deploying Envoy Gateway for Test/Dev + +* Run `make create-cluster` to create a [Kind][] cluster. + +#### Option 1: Use the Latest [gateway-dev][] Image + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` + to use a different image tag. + +#### Option 2: Use a Custom Image + +* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. +* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. + +### Deploying Envoy Gateway in Kubernetes + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to + the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or + tag. +* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. + +__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. + +### Demo Setup + +* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource +(similar to steps outlined in the [Quickstart][] docs) and test the configuration. +* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. + +### Run Gateway API Conformance Tests + +The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the +Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run +`TAG=latest make run-conformance` to run the conformance tests. + +#### On a Linux Host + +* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] + image, and run Gateway API conformance tests. + +#### On a Mac Host + +Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following +workarounds to run conformance tests: + +* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run + `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image + to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to + uninstall Envoy Gateway. +* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. + +__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` +is unspecified, the short SHA of your current branch is used. + +### Debugging the Envoy Config + +An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port +(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. + +Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: + +```shell +export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward the admin interface port: + +```shell +kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 +``` + +Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. + +There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. + +### JWT Testing + +An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] +user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs +verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching +settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure +and is hosted in the repo. + +[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md +[make]: https://www.gnu.org/software/make/ +[Github Actions]: https://docs.github.com/en/actions +[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows +[Kind]: https://kind.sigs.k8s.io/ +[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ +[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ +[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ +[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags +[mac_connect]: https://github.com/chipmk/docker-mac-net-connect +[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[JWT Debugger]: https://jwt.io/ +[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/site/content/en/v0.2.0/contributions/DOCS.md b/site/content/en/v0.2.0/contributions/DOCS.md new file mode 100644 index 00000000000..8266c9e9a2b --- /dev/null +++ b/site/content/en/v0.2.0/contributions/DOCS.md @@ -0,0 +1,67 @@ +--- +title: "Working on Envoy Gateway Docs" +description: "This section tells the development of + Envoy Gateway Documents." +--- + +The documentation for the Envoy Gateway lives in the `docs/` directory. Any +individual document can be written using either [reStructuredText] or [Markdown], +you can choose the format that you're most comfortable with when working on the +documentation. + +## Documentation Structure + +We supported the versioned Docs now, the directory name under docs represents +the version of docs. The root of the latest site is in `docs/latest/index.rst`. +This is probably where to start if you're trying to understand how things fit together. + +Note that the new contents should be added to `docs/latest` and will be cut off at +the next release. The contents under `docs/v0.2.0` are auto-generated, +and usually do not need to make changes to them, unless if you find the current release pages have +some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` +and `docs/v0.2.0`. + +It's important to note that a given document _must_ have a reference in some +`.. toctree::` section for the document to be reachable. Not everything needs +to be in `docs/index.rst`'s `toctree` though. + +You can access the website which represents the current release in default, +and you can access the website which contains the latest version changes in +[Here][latest-website] or at the footer of the pages. + +## Documentation Workflow + +To work with the docs, just edit reStructuredText or Markdown files in `docs`, +then run + +```bash +make docs +``` + +This will create `docs/html` with the built HTML pages. You can view the docs +either simply by pointing a web browser at the `file://` path to your +`docs/html`, or by firing up a static webserver from that directory, e.g. + +``` shell +make docs-serve +``` + +If you want to generate a new release version of the docs, like `v0.3.0`, then run + +```bash +make docs-release TAG=v0.3.0 +``` + +This will update the VERSION file at the project root, which records current release version, +and it will be used in the pages version context and binary version output. Also, this will generate +new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` +in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. + +## Publishing Docs + +Whenever docs are pushed to `main`, CI will publish the built docs to GitHub +Pages. For more details, see `.github/workflows/docs.yaml`. + +[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html +[Markdown]: https://daringfireball.net/projects/markdown/syntax +[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/site/content/en/v0.2.0/contributions/RELEASING.md b/site/content/en/v0.2.0/contributions/RELEASING.md new file mode 100644 index 00000000000..013707d4569 --- /dev/null +++ b/site/content/en/v0.2.0/contributions/RELEASING.md @@ -0,0 +1,231 @@ +--- +title: "Release Process" +description: "This section tells the release process of Envoy Gateway." +--- + +This document guides maintainers through the process of creating an Envoy Gateway release. + +- [Release Candidate](#release-candidate) +- [Minor Release](#minor-release) +- [Announce the Release](#announce-the-release) + +## Release Candidate + +The following steps should be used for creating a release candidate. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export RELEASE_CANDIDATE_NUMBER=1 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and + the [Build and Test][] has successfully completed. +5. Create a new release branch from `main`. The release branch should be named + `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. Push the branch to the Envoy Gateway repo. + + ```shell + git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] + for additional details on updating the image tag. +8. Sign, commit, and push your changes to your fork. +9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not + proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. +10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' + ``` + +11. Push the tag to the Envoy Gateway repository. + + ```shell + git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} + ``` + +12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +13. Confirm that the [release workflow][] completed successfully. +14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +15. Confirm that the [release][] was created. +16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test + the quickstart steps using the release candidate by manually updating the links. +17. [Generate][] the GitHub changelog. +18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. +19. If you find any bugs in this process, please create an issue. + +### Setup cherry picker action + +After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. + +Configuration looks like following: + +```yaml + cherry_pick_release_v0_4: + runs-on: ubuntu-latest + name: Cherry pick into release-v0.4 + if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cherry pick into release/v0.4 + uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 + with: + branch: release/v0.4 + title: "[release/v0.4] {old_title}" + body: "Cherry picking #{old_pull_request_id} onto release/v0.4" + labels: | + cherrypick/release-v0.4 + # put release manager here + reviewers: | + AliceProxy +``` + +Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. + +## Minor Release + +The following steps should be used for creating a minor release. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. +- A release branch that has been cut from the corresponding release candidate. Refer to the + [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. + + 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release + notes should be an accumulation of the release candidate release notes and any changes since the release + candidate. + 2. Create a release announcement. Refer to [PR #635] as an example release announcement. + 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. + 4. Generate the versioned release docs: + + ``` shell + make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged + and the [Build and Test][] has completed for your final PR. + +5. Checkout the release branch. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. If the tip of the release branch does not match the tip of `main`, perform the following: + + 1. Create a topic branch from the release branch. + 2. Cherry-pick the commits from `main` that differ from the release branch. + 3. Run tests locally, e.g. `make lint`. + 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. + 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. + 6. Do not proceed until the PR has merged and CI passes for the merged PR. + 7. If you are still on your topic branch, change to the release branch: + + ```shell + git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + + 8. Ensure your local release branch is up-to-date: + + ```shell + git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Tag the head of your release branch with the release tag. For example: + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' + ``` + + __Note:__ The tag version differs from the release branch by including the `.0` patch version. + +8. Push the tag to the Envoy Gateway repository. + + ```shell + git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +10. Confirm that the [release workflow][] completed successfully. +11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +12. Confirm that the [release][] was created. +13. Confirm that the steps in the [Quickstart Guide][] work as expected. +14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: + + ```console + # Release Announcement + + Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] + (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. + ``` + +If you find any bugs in this process, please create an issue. + +## Announce the Release + +It's important that the world knows about the release. Use the following steps to announce the release. + +1. Set the release information in the Envoy Gateway Slack channel. For example: + + ```shell + Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +2. Send a message to the Envoy Gateway Slack channel. For example: + + ```shell + On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway + v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. + Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs + to start using Envoy Gateway. + ... + ``` + + Link to the GitHub release and release announcement page that highlights the release. + +[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes +[Pull Request]: https://github.com/envoyproxy/gateway/pulls +[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md +[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml +[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml +[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml +[image]: https://hub.docker.com/r/envoyproxy/gateway/tags +[release]: https://github.com/envoyproxy/gateway/releases +[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +[PR #635]: https://github.com/envoyproxy/gateway/pull/635 +[PR #958]: https://github.com/envoyproxy/gateway/pull/958 +[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 +[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/site/content/en/v0.2.0/contributions/_index.md b/site/content/en/v0.2.0/contributions/_index.md new file mode 100644 index 00000000000..3255d996472 --- /dev/null +++ b/site/content/en/v0.2.0/contributions/_index.md @@ -0,0 +1,5 @@ +--- +title: Get Involved +description: "This section includes contents related to **Contributions**" +weight: 100 +--- diff --git a/site/content/en/v0.2.0/contributions/roadmap.md b/site/content/en/v0.2.0/contributions/roadmap.md new file mode 100644 index 00000000000..0763d482627 --- /dev/null +++ b/site/content/en/v0.2.0/contributions/roadmap.md @@ -0,0 +1,64 @@ +--- +title: "Roadmap" +weight: -1 +description: "This section records the roadmap of Envoy Gateway." +--- + +This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of +the project. + +## Contributing to the Roadmap + +- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use + case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update + this document accordingly. +- To help with an existing roadmap item, comment on or assign yourself to the associated issue. +- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A + maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be + discussed in an issue or a community meeting before implementing it. + +If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. +Look for issues with the `help wanted` label to get started. + +## Details + +Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific +roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by +contributing to the project. + +`Last Updated: October 2022` + +### [v0.2.0][v0.2.0]: Establish a Solid Foundation + +- Complete the core Envoy Gateway implementation- [Issue #60][60]. +- Establish initial testing, e2e, integration, etc- [Issue #64][64]. +- Establish user and developer project documentation- [Issue #17][17]. +- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. +- Setup a CI/CD pipeline- [Issue #63][63]. + +### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms + +- Global Rate Limiting +- AuthN/AuthZ- [Issue #336][336]. +- Lets Encrypt Integration + +### [v0.4.0][v0.4.0]: Manageability and Scale + +- Tooling for devs/infra admins to aid in managing/maintaining EG +- Support advanced provisioning use cases (e.g. multi-cluster, serverless, etc.) +- Perf testing (EG specifically) +- Support for Chaos engineering? + +[issue]: https://github.com/envoyproxy/gateway/issues +[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing +[pr]: https://github.com/envoyproxy/gateway/compare +[milestones]: https://github.com/envoyproxy/gateway/milestones +[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 +[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 +[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 +[60]: https://github.com/envoyproxy/gateway/issues/60 +[64]: https://github.com/envoyproxy/gateway/issues/64 +[17]: https://github.com/envoyproxy/gateway/issues/17 +[65]: https://github.com/envoyproxy/gateway/issues/65 +[63]: https://github.com/envoyproxy/gateway/issues/63 +[336]: https://github.com/envoyproxy/gateway/issues/336 diff --git a/site/content/en/v0.2.0/design/_index.md b/site/content/en/v0.2.0/design/_index.md new file mode 100644 index 00000000000..21650809f7d --- /dev/null +++ b/site/content/en/v0.2.0/design/_index.md @@ -0,0 +1,6 @@ +--- +title: "Design" +weight: 1 +description: This section includes Designs of Envoy Gateway. +--- + diff --git a/site/content/en/v0.2.0/design/config-api.md b/site/content/en/v0.2.0/design/config-api.md new file mode 100644 index 00000000000..466b84d8f35 --- /dev/null +++ b/site/content/en/v0.2.0/design/config-api.md @@ -0,0 +1,352 @@ +--- +title: "Configuration API Design" +--- + +## Motivation + +[Issue 51][issue_51] specifies the need to design an API for configuring Envoy Gateway. The control plane is configured +statically at startup and the data plane is configured dynamically through Kubernetes resources, primarily +[Gateway API][gw_api] objects. Refer to the Envoy Gateway [design doc][design_doc] for additional details regarding +Envoy Gateway terminology and configuration. + +## Goals + +* Define an __initial__ API to configure Envoy Gateway at startup. +* Define an __initial__ API for configuring the managed data plane, e.g. Envoy proxies. + +## Non-Goals + +* Implementation of the configuration APIs. +* Define the `status` subresource of the configuration APIs. +* Define a __complete__ set of APIs for configuring Envoy Gateway. As stated in the [Goals](#goals), this document + defines the initial configuration APIs. +* Define an API for deploying/provisioning/operating Envoy Gateway. If needed, a future Envoy Gateway operator would be + responsible for designing and implementing this type of API. +* Specify tooling for managing the API, e.g. generate protos, CRDs, controller RBAC, etc. + +## Control Plane API + +The `EnvoyGateway` API defines the control plane configuration, e.g. Envoy Gateway. Key points of this API are: + +* It will define Envoy Gateway's startup configuration file. If the file does not exist, Envoy Gateway will start up + with default configuration parameters. +* EnvoyGateway inlines the `TypeMeta` API. This allows EnvoyGateway to be versioned and managed as a GroupVersionKind + scheme. +* EnvoyGateway does not contain a metadata field since it's currently represented as a static configuration file instead of + a Kubernetes resource. +* Since EnvoyGateway does not surface status, EnvoyGatewaySpec is inlined. +* If data plane static configuration is required in the future, Envoy Gateway will use a separate file for this purpose. + +The `v1alpha1` version and `config.gateway.envoyproxy.io` API group get generated: + +```go +// gateway/api/config/v1alpha1/doc.go + +// Package v1alpha1 contains API Schema definitions for the config.gateway.envoyproxy.io API group. +// +// +groupName=config.gateway.envoyproxy.io +package v1alpha1 +``` + +The initial `EnvoyGateway` API: + +```go +// gateway/api/config/v1alpha1/envoygateway.go + +package valpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyGateway is the Schema for the envoygateways API +type EnvoyGateway struct { + metav1.TypeMeta `json:",inline"` + + // EnvoyGatewaySpec defines the desired state of Envoy Gateway. + EnvoyGatewaySpec `json:",inline"` +} + +// EnvoyGatewaySpec defines the desired state of Envoy Gateway configuration. +type EnvoyGatewaySpec struct { + // Gateway defines Gateway-API specific configuration. If unset, default + // configuration parameters will apply. + // + // +optional + Gateway *Gateway `json:"gateway,omitempty"` + + // Provider defines the desired provider configuration. If unspecified, + // the Kubernetes provider is used with default parameters. + // + // +optional + Provider *Provider `json:"provider,omitempty"` +} + +// Gateway defines desired Gateway API configuration of Envoy Gateway. +type Gateway struct { + // ControllerName defines the name of the Gateway API controller. If unspecified, + // defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following + // for additional details: + // + // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass + // + // +optional + ControllerName string `json:"controllerName,omitempty"` +} + +// Provider defines the desired configuration of a provider. +// +union +type Provider struct { + // Type is the type of provider to use. If unset, the Kubernetes provider is used. + // + // +unionDiscriminator + Type ProviderType `json:"type,omitempty"` + // Kubernetes defines the configuration of the Kubernetes provider. Kubernetes + // provides runtime configuration via the Kubernetes API. + // + // +optional + Kubernetes *KubernetesProvider `json:"kubernetes,omitempty"` + + // File defines the configuration of the File provider. File provides runtime + // configuration defined by one or more files. + // + // +optional + File *FileProvider `json:"file,omitempty"` +} + +// ProviderType defines the types of providers supported by Envoy Gateway. +type ProviderType string + +const ( + // KubernetesProviderType defines the "Kubernetes" provider. + KubernetesProviderType ProviderType = "Kubernetes" + + // FileProviderType defines the "File" provider. + FileProviderType ProviderType = "File" +) + +// KubernetesProvider defines configuration for the Kubernetes provider. +type KubernetesProvider struct { + // TODO: Add config as use cases are better understood. +} + +// FileProvider defines configuration for the File provider. +type FileProvider struct { + // TODO: Add config as use cases are better understood. +} +``` + +__Note:__ Provider-specific configuration is defined in the `{$PROVIDER_NAME}Provider` API. + +### Gateway + +Gateway defines desired configuration of [Gateway API][gw_api] controllers that reconcile and translate Gateway API +resources into the Intermediate Representation (IR). Refer to the Envoy Gateway [design doc][design_doc] for additional +details. + +### Provider + +Provider defines the desired configuration of an Envoy Gateway provider. A provider is an infrastructure component that +Envoy Gateway calls to establish its runtime configuration. Provider is a [union type][union]. Therefore, Envoy Gateway +can be configured with only one provider based on the `type` discriminator field. Refer to the Envoy Gateway +[design doc][design_doc] for additional details. + +### Control Plane Configuration + +The configuration file is defined by the EnvoyGateway API type. At startup, Envoy Gateway searches for the configuration +at "/etc/envoy-gateway/config.yaml". + +Start Envoy Gateway: + +```shell +$ ./envoy-gateway +``` + +Since the configuration file does not exist, Envoy Gateway will start with default configuration parameters. + +The Kubernetes provider can be configured explicitly using `provider.kubernetes`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: {} +EOF +``` + +This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. + +The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: + foo: bar +EOF +``` + +__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for +illustration purposes only. + +The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the +File provider: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: File + file: + foo: bar +EOF +``` + +__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration +purposes only. + +Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use +default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to +manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: foo +``` + +With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: + +```shell +$ ./envoy-gateway +``` + +## Data Plane API + +The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. +Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through +`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure +configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: + +* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane + infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. +* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: + > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the + > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are + > not propagated down to existing Gateways. + +The initial `EnvoyProxy` API: + +```go +// gateway/api/config/v1alpha1/envoyproxy.go + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyProxy is the Schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvoyProxySpec `json:"spec,omitempty"` + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure +// configuration. +type EnvoyProxySpec struct { + // Undefined by this design spec. +} + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. +type EnvoyProxyStatus struct { + // Undefined by this design spec. +} +``` + +The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use +cases are better understood. + +### Data Plane Configuration + +GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is +running with the Kubernetes provider. + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration +parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening +on port 80. + +The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer +service: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + name: example-config + group: config.gateway.envoyproxy.io + kind: EnvoyProxy +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: example-config +spec: + networkPublishing: + type: ClusterIPService +``` + +__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. + +[issue_51]: https://github.com/envoyproxy/gateway/issues/51 +[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md +[gw_api]: https://gateway-api.sigs.k8s.io/ +[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/site/content/en/v0.2.0/design/gatewayapi-translator.md b/site/content/en/v0.2.0/design/gatewayapi-translator.md new file mode 100644 index 00000000000..b83ed82588d --- /dev/null +++ b/site/content/en/v0.2.0/design/gatewayapi-translator.md @@ -0,0 +1,253 @@ +--- +title: "Gateway API Translator Design" +weight: 4 +--- + +The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate +Representation (IR). + +## Assumptions + +Initially target core conformance features only, to be followed by extended conformance features. + +## Inputs and Outputs + +The main inputs to the Gateway API translator are: + +- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. + +__Note:__ ReferenceGrant is not fully implemented as of v0.2. + +The outputs of the Gateway API translator are: + +- Xds and Infra Internal Representations (IRs). +- Status updates for GatewayClass, Gateways, HTTPRoutes + +## Listener Compatibility + +Envoy Gateway follows Gateway API listener compatibility spec: +> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. + +__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. + +### Listener Compatibility Examples + +#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +## Computing Status + +Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway +must compute the appropriate status fields and conditions for managed resources. + +Status is computed and set for: + +- The managed GatewayClass (`gatewayclass.status.conditions`). +- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the + Envoy Deployment and Service status are also included to calculate Gateway status. +- Listeners for each Gateway (`gateway.status.listeners`). +- The ParentRef for each Route (`route.status.parents`). + +The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to +the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and +updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to +update resource status on the Kubernetes API server. + +## Outline + +The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway +API resources. + +1. Process Gateway Listeners + - Validate unique hostnames, ports, and protocols. + - Validate and compute supported kinds. + - Validate allowed namespaces (validate selector if specified). + - Validate TLS fields if specified, including resolving referenced Secrets. + +2. Process HTTPRoutes + - foreach route rule: + - compute matches + - [core] path exact, path prefix + - [core] header exact + - [extended] query param exact + - [extended] HTTP method + - compute filters + - [core] request header modifier (set/add/remove) + - [core] request redirect (hostname, statuscode) + - [extended] request mirror + - compute backends + - [core] Kubernetes services + - foreach route parent ref: + - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) + - foreach matching listener: + - foreach hostname intersection with route: + - add each computed route rule to host + +## Context Structs + +To help store, access and manipulate information as it's processed during the translation process, a set of context +structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support +processing. + +`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. + +```go +type GatewayContext struct { + // The managed Gateway + *v1beta1.Gateway + + // A list of Gateway ListenerContexts. + listeners []*ListenerContext +} +``` + +`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on +the associated Gateway. + +```go +type ListenerContext struct { + // The Gateway listener. + *v1beta1.Listener + + // The Gateway this Listener belongs to. + gateway *v1beta1.Gateway + + // An index used for managing this listener in the list of Gateway listeners. + listenerStatusIdx int + + // Only Routes in namespaces selected by the selector may be attached + // to the Gateway this listener belongs to. + namespaceSelector labels.Selector + + // The TLS Secret for this Listener, if applicable. + tlsSecret *v1.Secret +} +``` + +`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. + +```go +type RouteContext interface { + client.Object + + // GetRouteType returns the Kind of the Route object, HTTPRoute, + // TLSRoute, TCPRoute, UDPRoute etc. + GetRouteType() string + + // GetHostnames returns the hosts targeted by the Route object. + GetHostnames() []string + + // GetParentReferences returns the ParentReference of the Route object. + GetParentReferences() []v1beta1.ParentReference + + // GetRouteParentContext returns RouteParentContext by using the Route + // objects' ParentReference. + GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext +} +``` + +[message bus]: watching.md diff --git a/site/content/en/v0.2.0/design/goals.md b/site/content/en/v0.2.0/design/goals.md new file mode 100644 index 00000000000..fd38b2004c6 --- /dev/null +++ b/site/content/en/v0.2.0/design/goals.md @@ -0,0 +1,91 @@ +--- +title: "Goals" +weight: 1 +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption +through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing +use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer +fundamental interactions. + +## Objectives + +### Expressive API + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +### Batteries included + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +### All environments + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API][]. +Additional goals include multi-cluster support and various runtime environments. + +### Extensibility + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +## Non-objectives + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +## Personas + +_In order of priority_ + +### 1. Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +### 2. Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/v0.2.0/design/system-design.md b/site/content/en/v0.2.0/design/system-design.md new file mode 100644 index 00000000000..72c0a98ecda --- /dev/null +++ b/site/content/en/v0.2.0/design/system-design.md @@ -0,0 +1,174 @@ +--- +title: "System Design" +weight: 2 +--- + +## Goals + +* Define the system components needed to satisfy the requirements of Envoy Gateway. + +## Non-Goals + +* Create a detailed design and interface specification for each system component. + +## Terminology + +* Control Plane- A collection of inter-related software components for providing application gateway and routing + functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. + These services are detailed in the [components](#components) section. +* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. + +## Architecture + +![Architecture](/img/architecture.png) + +## Configuration + +Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][gw_api] objects. + +### Static Configuration + +Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, +configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the +configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. + +### Dynamic Configuration + +Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using +reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is +defined as Kubernetes resources that provide the following services: + +* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is + expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be + referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, + e.g. expose Envoy network endpoints using a NodePort service instead of a LoadBalancer service. +* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP + requests for "www.example.com" to a backend service running a web server. This configuration is expressed through + [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. + Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] + reference. + +## Components + +Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described +in the [Watching Components Design][wcd]. + +### Provider + +A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve +services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap +via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A +provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). + +#### Kubernetes Provider + +* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the + [dynamic configuration](#dynamic-configuration). +* Manages the data plane through Kubernetes API CRUD operations. +* Uses Kubernetes for Service discovery. +* Uses etcd (via Kubernetes API) to persist data. + +#### File Provider + +* Uses a file watcher to watch files in a directory that define the data plane configuration. +* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. +* Uses the host's DNS for Service discovery. +* If needed, the local filesystem is used to persist data. + +### Resource Watcher + +The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The +mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes +provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator +as output. + +### Resource Translator + +The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate +Representation (IR). It is responsible for: + +* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. +* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. + +__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. + +### Intermediate Representation (IR) + +The Intermediate Representation defines internal data models that external resources are translated into. This allows +Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR +used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. + +* Infra IR- Used as the internal definition of the managed data plane infrastructure. +* xDS IR- Used as the internal definition of the managed data plane xDS configuration. + +### xDS Translator + +The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. + +### xDS Server + +The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server +Protocol and is responsible for using xDS to configure the data plane. + +### Infra Manager + +The Infra Manager is a provider-specific component responsible for managing the following infrastructure: + +* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, + Service, etc. resources to run Envoy in a Kubernetes cluster. +* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require + external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning + and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to + users through the [Custom Route Filters][crf] extension. + +The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. + +## Design Decisions + +* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with + `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy + Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. + `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy + infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. +* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. + * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. + * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners + in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are + met: + * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies + either the “HTTPS” or “TLS” Protocol. + * Each Listener within the group specifies a unique "Hostname". + * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no + other Listener matches. + * Envoy Gateway does __not__ merge listeners across multiple Gateways. +* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. + * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. +* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. + * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. +* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. + +The draft for this document is [here][draft_design]. + +[gw_api]: https://gateway-api.sigs.k8s.io +[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass +[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway +[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute +[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute +[go_cp]: https://github.com/envoyproxy/go-control-plane +[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[rls]: https://github.com/envoyproxy/ratelimit +[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional +[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts +[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners +[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route +[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional +[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster +[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference +[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ +[ wcd ]: ./watching.md +[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 +[roadmap]: roadmap.md diff --git a/site/content/en/v0.2.0/design/watching.md b/site/content/en/v0.2.0/design/watching.md new file mode 100644 index 00000000000..72a955043e0 --- /dev/null +++ b/site/content/en/v0.2.0/design/watching.md @@ -0,0 +1,120 @@ +--- +title: "Watching Components Design" +weight: 3 +--- + +Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch +external resources, and "publish" what they see for other components to consume; others watch what another publishes and +act on it (such as the resource translator watches what the providers publish, and then publishes its own results that +are watched by another component). Some of these internally published results are consumed by multiple components. + +To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the +standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub +pattern. + +## Pub + +Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" +tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if +we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by +the IR translator: + + ```go + type ResourceTable struct { + // gateway classes are cluster-scoped; no namespace + GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] + + // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. + Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] + + HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] + } + ``` + +The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; +updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` +method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher +doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just +`.Store` its data and `watchable` will do the right thing. + +## Sub + +Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or +`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: + + ```go + func(ctx context.Context) error { + for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { + fullState := irInput{ + GatewayClasses: k8sTable.GatewayClasses.LoadAll(), + Gateways: k8sTable.Gateways.LoadAll(), + HTTPRoutes: snapshot.State, + } + translate(irInput) + } + } + ``` + +Or, to watch multiple maps in the same loop: + + ```go + func worker(ctx context.Context) error { + classCh := k8sTable.GatewayClasses.Subscribe(ctx) + gwCh := k8sTable.Gateways.Subscribe(ctx) + routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) + for ctx.Err() == nil { + var arg irInput + select { + case snapshot := <-classCh: + arg.GatewayClasses = snapshot.State + case snapshot := <-gwCh: + arg.Gateways = snapshot.State + case snapshot := <-routeCh: + arg.Routes = snapshot.State + } + if arg.GateWayClasses == nil { + arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() + } + if arg.GateWays == nil { + arg.Gateways = k8sTable.GateWays.LoadAll() + } + if arg.HTTPRoutes == nil { + arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() + } + translate(irInput) + } + } + ``` + +From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; +but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a +handy way to know when to run, `.Load` and friends can be used without subscribing. + +There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but +it's probably wise to just have one publisher for each map. + +The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when +`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple +mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in +to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of +each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need +to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. + +If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` +entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you +must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to +help with this. + +## Other Notes + +The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the +map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb +`chan`. + +A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types +be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and +so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can +generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate +methods for. + +[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/site/content/en/v0.2.0/user/_index.md b/site/content/en/v0.2.0/user/_index.md new file mode 100644 index 00000000000..2f23014d867 --- /dev/null +++ b/site/content/en/v0.2.0/user/_index.md @@ -0,0 +1,6 @@ +--- +title: "User Guides" +weight: 2 +description: This section includes User Guides of Envoy Gateway. +--- + diff --git a/site/content/en/v0.2.0/user/http-redirect.md b/site/content/en/v0.2.0/user/http-redirect.md new file mode 100644 index 00000000000..245039c74b5 --- /dev/null +++ b/site/content/en/v0.2.0/user/http-redirect.md @@ -0,0 +1,129 @@ +--- +title: "HTTP Redirects" +--- + +The [HTTPRoute][] resource can issue redirects to clients or rewrite paths sent upstream using filters. Note that +HTTPRoute rules cannot use both filter types at once. Currently, Envoy Gateway only supports __core__ +[HTTPRoute filters][] which consist of `RequestRedirect` and `RequestHeaderModifier` at the time of this writing. To +learn more about HTTP routing, refer to the [Gateway API documentation][]. + +## Prerequisites + +Follow the steps from the [Secure Gateways](secure-gateways.md) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTPS. + +## Redirects + +Redirects return HTTP 3XX responses to a client, instructing it to retrieve a different resource. A +[`RequestRedirect` filter][req_filter] instructs Gateways to emit a redirect response to requests that match the rule. +For example, to issue a permanent redirect (301) from HTTP to HTTPS, configure `requestRedirect.statusCode=301` and +`requestRedirect.scheme="https"`: + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something", + "foo" + ], +... +``` + +## Setting Request Headers + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + "headers": { + "Accept": [ + "*/*" + ], + "Set-Header": [ + "foo" + ], +... +``` + +## Removing Request Headers + +Headers can be removed from a request by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something" + ], +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +## Multiple backendRefs + +If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is +configured. + +First, create a second instance of the example app from the quickstart: + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-75bcd4c969-lsxpz" +... +``` + +## Weighted backendRefs + +If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` +field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is +assumed to be `1`. + +The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of +requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the +HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. + +The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the +backend-2 service. + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< server: envoy +< content-length: 0 +< +``` + +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/site/content/en/v0.2.0/user/quickstart.md b/site/content/en/v0.2.0/user/quickstart.md new file mode 100644 index 00000000000..291480b1747 --- /dev/null +++ b/site/content/en/v0.2.0/user/quickstart.md @@ -0,0 +1,90 @@ +--- +title: "Quickstart" +weight: 1 +--- + +This guide will help you get started with Envoy Gateway in a few simple steps. + +## Prerequisites + +A Kubernetes cluster. + +__Note:__ Refer to the [Compatibility Matrix](/blog/2022/10/01/versions/) for supported Kubernetes versions. + +## Installation + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/install.yaml +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/quickstart.yaml +``` + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:8080 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` + +### External LoadBalancer Support + +You can also test the same functionality by sending traffic to the External IP. To get the external IP of the +Envoy service, run: + +```shell +export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace +`ip` in the above command with `hostname`. + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST:8080/get +``` + +## Clean-Up + +Use the steps in this section to uninstall everything from the quickstart guide. + +Delete the GatewayClass, Gateway, HTTPRoute and Example App: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/quickstart.yaml --ignore-not-found=true +``` + +Delete the Gateway API CRDs and Envoy Gateway: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/install.yaml --ignore-not-found=true +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. diff --git a/site/content/en/v0.2.0/user/secure-gateways.md b/site/content/en/v0.2.0/user/secure-gateways.md new file mode 100644 index 00000000000..21f845ebf3c --- /dev/null +++ b/site/content/en/v0.2.0/user/secure-gateways.md @@ -0,0 +1,258 @@ +--- +title: "Secure Gateways" +--- + +This guide will help you get started using secure Gateways. The guide uses a self-signed CA, so it should be used for +testing and demonstration purposes only. + +## Prerequisites + +- OpenSSL to generate TLS assets. + +## Installation + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## TLS Certificates + +Generate the certificates and keys used by the Gateway to terminate client TLS connections. + +Create a root certificate and private key to sign certificates: + +```shell +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt +``` + +Create a certificate and a private key for `www.example.com`: + +```shell +openssl req -out www.example.com.csr -newkey rsa:2048 -nodes -keyout www.example.com.key -subj "/CN=www.example.com/O=example organization" +openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in www.example.com.csr -out www.example.com.crt +``` + +Store the cert/key in a Secret: + +```shell +kubectl create secret tls example-cert --key=www.example.com.key --cert=www.example.com.crt +``` + +Update the Gateway from the Quickstart guide to include an HTTPS listener that listens on port `8443` and references the +`example-cert` Secret: + +```shell +kubectl patch gateway eg --type=json --patch '[{ + "op": "add", + "path": "/spec/listeners/-", + "value": { + "name": "https", + "protocol": "HTTPS", + "port": 8443, + "tls": { + "mode": "Terminate", + "certificateRefs": [{ + "kind": "Secret", + "group": "", + "name": "example-cert", + }], + }, + }, +}]' +``` + +Verify the Gateway status: + +```shell +kubectl get gateway/eg -o yaml +``` + +## Testing + +### Clusters without External LoadBalancer Support + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8043:8443 & +``` + +Query the example app through Envoy proxy: + +```shell +curl -v -HHost:www.example.com --resolve "www.example.com:8043:127.0.0.1" \ +--cacert example.com.crt https://www.example.com:8043/get +``` + +### Clusters with External LoadBalancer Support + +Get the External IP of the Gateway: + +```shell +export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}') +``` + +Query the example app through the Gateway: + +```shell +curl -v -HHost:www.example.com --resolve "www.example.com:8443:${GATEWAY_HOST}" \ +--cacert example.com.crt https://www.example.com:8443/get +``` + +## Multiple HTTPS Listeners + +Create a TLS cert/key for the additional HTTPS listener: + +```shell +openssl req -out foo.example.com.csr -newkey rsa:2048 -nodes -keyout foo.example.com.key -subj "/CN=foo.example.com/O=example organization" +openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in foo.example.com.csr -out foo.example.com.crt +``` + +Store the cert/key in a Secret: + +```shell +kubectl create secret tls foo-cert --key=foo.example.com.key --cert=foo.example.com.crt +``` + +Create another HTTPS listener on the example Gateway: + +```shell +kubectl patch gateway eg --type=json --patch '[{ + "op": "add", + "path": "/spec/listeners/-", + "value": { + "name": "https-foo", + "protocol": "HTTPS", + "port": 8443, + "hostname": "foo.example.com", + "tls": { + "mode": "Terminate", + "certificateRefs": [{ + "kind": "Secret", + "group": "", + "name": "foo-cert", + }], + }, + }, +}]' +``` + +Update the HTTPRoute to route traffic for hostname `foo.example.com` to the example backend service: + +```shell +kubectl patch httproute backend --type=json --patch '[{ + "op": "add", + "path": "/spec/hostnames/-", + "value": "foo.example.com", +}]' +``` + +Verify the Gateway status: + +```shell +kubectl get gateway/eg -o yaml +``` + +Follow the steps in the [Testing section](#testing) to test connectivity to the backend app through both Gateway +listeners. Replace `www.example.com` with `foo.example.com` to test the new HTTPS listener. + +## Cross Namespace Certificate References + +A Gateway can be configured to reference a certificate in a different namespace. This is allowed by a [ReferenceGrant][] +created in the target namespace. Without the ReferenceGrant, a cross-namespace reference is invalid. + +Before proceeding, ensure you can query the HTTPS backend service from the [Testing section](#testing). + +To demonstrate cross namespace certificate references, create a ReferenceGrant that allows Gateways from the "default" +namespace to reference Secrets in the "envoy-gateway-system" namespace: + +```console +$ cat < 100 LOC altered (not including tests), or + changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to + agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process + for major features is also important so that [affiliations with commit access](../codeowners) can + come to agreement on the design. If it's appropriate to write a design document, the document must + be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable + location. +* Small patches and bug fixes don't need prior communication. + +## Inclusivity + +The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere +to the following guidelines for all code, APIs, and documentation: + +* The following words and phrases are not allowed: + * *Whitelist*: use allowlist instead. + * *Blacklist*: use denylist or blocklist instead. + * *Master*: use primary instead. + * *Slave*: use secondary or replica instead. +* Documentation should be written in an inclusive style. The [Google developer + documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent + reference on this topic. +* The above policy is not considered definitive and may be amended in the future as industry best + practices evolve. Additional comments on this topic may be provided by maintainers during code + review. + +## Submitting a PR + +* Fork the repo. +* Hack +* DCO sign-off each commit. This can be done with `git commit -s`. +* Submit your PR. +* Tests will automatically run for you. +* We will **not** merge any PR that is not passing tests. +* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage + build. If your PR cannot have 100% coverage for some reason please clearly explain why when you + open it. +* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/site) folder of the repo as + well as the [changelog](/blog/releases). +* All code comments and documentation are expected to have proper English grammar and punctuation. + If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try + to find some help but there are no guarantees. +* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary + and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. + Examples: + * "docs: fix grammar error" + * "feat(translator): add new feature" + * "fix: fix xx bug" + * "chore: change ci & build tools etc" +* Your PR commit message will be used as the commit message when your PR is merged. You should + update this field if your PR diverges during review. +* Your PR description should have details on what the PR does. If it fixes an existing issue it + should end with "Fixes #XXX". +* If your PR is co-authored or based on an earlier PR from another contributor, + please attribute them with `Co-authored-by: name `. See + GitHub's [multiple author + guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) + for further details. +* When all tests are passing and all other conditions described herein are satisfied, a maintainer + will be assigned to review and merge the PR. +* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits + are new commits and/or merges. We squash and merge so the number of commits you have in the PR + doesn't matter. +* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. + We reserve the right to close PRs that are not making progress. This is generally defined as no + changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. + Closing stale PRs helps us to keep on top of all the work currently in flight. + +## Maintainer PR Review Policy + +* See [CODEOWNERS.md](../codeowners) for the current list of maintainers. +* A maintainer representing a different affiliation from the PR owner is required to review and + approve the PR. +* When the project matures, it is expected that a "domain expert" for the code the PR touches should + review the PR. This person does not require commit access, just domain knowledge. +* The above rules may be waived for PRs which only update docs or comments, or trivial changes to + tests and tools (where trivial is decided by the maintainer in question). +* If there is a question on who should review a PR please discuss in Slack. +* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. +* Please make sure that the PR title, commit message, and description are updated if the PR changes + significantly during review. +* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge + title with the original title, and the commit body with every individual commit from the PR. + The maintainer doing the merge should make sure the title follows the guidelines above and should + overwrite the body with the original commit message from the PR (cleaning it up if necessary) + while preserving the PR author's final DCO sign-off. + +## Decision making + +This is a new and complex project, and we need to make a lot of decisions very quickly. +To this end, we've settled on this process for making (possibly contentious) decisions: + +* For decisions that need a record, we create an issue. +* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. +* Maintainers can cast binding votes on that comment by reacting or replying in another comment. +* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. +* Voting will be resolved by simple majority. +* In the event of deadlocks, the question will be put to steering instead. + +## DCO: Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign-off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +```bash +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` + +## Fixing DCO + +If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best +practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +the commit history to a single commit, append the DCO sign-off as described above, and [force +push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in +your history: + +```bash +git rebase -i HEAD^^ +(interactive squash + DCO append) +git push origin -f +``` + +Note, that in general rewriting history in this way is a hindrance to the review process and this +should only be done to correct a DCO mistake. diff --git a/site/content/en/v0.3.0/contributions/DEVELOP.md b/site/content/en/v0.3.0/contributions/DEVELOP.md new file mode 100644 index 00000000000..6f82c4a411f --- /dev/null +++ b/site/content/en/v0.3.0/contributions/DEVELOP.md @@ -0,0 +1,163 @@ +--- +title: "Developer Guide" +description: "This section tells how to develop Envoy Gateway." +weight: 2 +--- + +Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. + +## Prerequisites + +### go + +* Version: 1.20 +* Installation Guide: https://go.dev/doc/install + +### make + +* Recommended Version: 4.0 or later +* Installation Guide: https://www.gnu.org/software/make + +### docker + +* Optional when you want to build a Docker image or run `make` inside Docker. +* Recommended Version: 20.10.16 +* Installation Guide: https://docs.docker.com/engine/install + +### python3 + +* Need a `python3` program +* Must have a functioning `venv` module; this is part of the standard + library, but some distributions (such as Debian and Ubuntu) replace + it with a stub and require you to install a `python3-venv` package + separately. + +## Quickstart + +* Run `make help` to see all the available targets to build, test and run Envoy Gateway. + +### Building + +* Run `make build` to build all the binaries. +* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. +* Run `make build BINS="egctl"` to build the egctl binary. + +__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. + +### Testing + +* Run `make test` to run the golang tests. + +* Run `make testdata` to generate the golden YAML testdata files. + +### Running Linters + +* Run `make lint` to make sure your code passes all the linter checks. +__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). + +### Building and Pushing the Image + +* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. +* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. + +__Note:__ Replace `IMAGE` with your registry's image name. + +### Deploying Envoy Gateway for Test/Dev + +* Run `make create-cluster` to create a [Kind][] cluster. + +#### Option 1: Use the Latest [gateway-dev][] Image + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` + to use a different image tag. + +#### Option 2: Use a Custom Image + +* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. +* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. + +### Deploying Envoy Gateway in Kubernetes + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to + the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or + tag. +* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. + +__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. + +### Demo Setup + +* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource +(similar to steps outlined in the [Quickstart][] docs) and test the configuration. +* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. + +### Run Gateway API Conformance Tests + +The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the +Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run +`TAG=latest make run-conformance` to run the conformance tests. + +#### On a Linux Host + +* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] + image, and run Gateway API conformance tests. + +#### On a Mac Host + +Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following +workarounds to run conformance tests: + +* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run + `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image + to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to + uninstall Envoy Gateway. +* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. + +__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` +is unspecified, the short SHA of your current branch is used. + +### Debugging the Envoy Config + +An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port +(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. + +Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: + +```shell +export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward the admin interface port: + +```shell +kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 +``` + +Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. + +There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. + +### JWT Testing + +An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] +user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs +verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching +settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure +and is hosted in the repo. + +[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md +[make]: https://www.gnu.org/software/make/ +[Github Actions]: https://docs.github.com/en/actions +[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows +[Kind]: https://kind.sigs.k8s.io/ +[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ +[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ +[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ +[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags +[mac_connect]: https://github.com/chipmk/docker-mac-net-connect +[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[JWT Debugger]: https://jwt.io/ +[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/site/content/en/v0.3.0/contributions/DOCS.md b/site/content/en/v0.3.0/contributions/DOCS.md new file mode 100644 index 00000000000..8266c9e9a2b --- /dev/null +++ b/site/content/en/v0.3.0/contributions/DOCS.md @@ -0,0 +1,67 @@ +--- +title: "Working on Envoy Gateway Docs" +description: "This section tells the development of + Envoy Gateway Documents." +--- + +The documentation for the Envoy Gateway lives in the `docs/` directory. Any +individual document can be written using either [reStructuredText] or [Markdown], +you can choose the format that you're most comfortable with when working on the +documentation. + +## Documentation Structure + +We supported the versioned Docs now, the directory name under docs represents +the version of docs. The root of the latest site is in `docs/latest/index.rst`. +This is probably where to start if you're trying to understand how things fit together. + +Note that the new contents should be added to `docs/latest` and will be cut off at +the next release. The contents under `docs/v0.2.0` are auto-generated, +and usually do not need to make changes to them, unless if you find the current release pages have +some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` +and `docs/v0.2.0`. + +It's important to note that a given document _must_ have a reference in some +`.. toctree::` section for the document to be reachable. Not everything needs +to be in `docs/index.rst`'s `toctree` though. + +You can access the website which represents the current release in default, +and you can access the website which contains the latest version changes in +[Here][latest-website] or at the footer of the pages. + +## Documentation Workflow + +To work with the docs, just edit reStructuredText or Markdown files in `docs`, +then run + +```bash +make docs +``` + +This will create `docs/html` with the built HTML pages. You can view the docs +either simply by pointing a web browser at the `file://` path to your +`docs/html`, or by firing up a static webserver from that directory, e.g. + +``` shell +make docs-serve +``` + +If you want to generate a new release version of the docs, like `v0.3.0`, then run + +```bash +make docs-release TAG=v0.3.0 +``` + +This will update the VERSION file at the project root, which records current release version, +and it will be used in the pages version context and binary version output. Also, this will generate +new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` +in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. + +## Publishing Docs + +Whenever docs are pushed to `main`, CI will publish the built docs to GitHub +Pages. For more details, see `.github/workflows/docs.yaml`. + +[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html +[Markdown]: https://daringfireball.net/projects/markdown/syntax +[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/site/content/en/v0.3.0/contributions/RELEASING.md b/site/content/en/v0.3.0/contributions/RELEASING.md new file mode 100644 index 00000000000..013707d4569 --- /dev/null +++ b/site/content/en/v0.3.0/contributions/RELEASING.md @@ -0,0 +1,231 @@ +--- +title: "Release Process" +description: "This section tells the release process of Envoy Gateway." +--- + +This document guides maintainers through the process of creating an Envoy Gateway release. + +- [Release Candidate](#release-candidate) +- [Minor Release](#minor-release) +- [Announce the Release](#announce-the-release) + +## Release Candidate + +The following steps should be used for creating a release candidate. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export RELEASE_CANDIDATE_NUMBER=1 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and + the [Build and Test][] has successfully completed. +5. Create a new release branch from `main`. The release branch should be named + `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. Push the branch to the Envoy Gateway repo. + + ```shell + git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] + for additional details on updating the image tag. +8. Sign, commit, and push your changes to your fork. +9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not + proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. +10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' + ``` + +11. Push the tag to the Envoy Gateway repository. + + ```shell + git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} + ``` + +12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +13. Confirm that the [release workflow][] completed successfully. +14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +15. Confirm that the [release][] was created. +16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test + the quickstart steps using the release candidate by manually updating the links. +17. [Generate][] the GitHub changelog. +18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. +19. If you find any bugs in this process, please create an issue. + +### Setup cherry picker action + +After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. + +Configuration looks like following: + +```yaml + cherry_pick_release_v0_4: + runs-on: ubuntu-latest + name: Cherry pick into release-v0.4 + if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cherry pick into release/v0.4 + uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 + with: + branch: release/v0.4 + title: "[release/v0.4] {old_title}" + body: "Cherry picking #{old_pull_request_id} onto release/v0.4" + labels: | + cherrypick/release-v0.4 + # put release manager here + reviewers: | + AliceProxy +``` + +Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. + +## Minor Release + +The following steps should be used for creating a minor release. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. +- A release branch that has been cut from the corresponding release candidate. Refer to the + [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. + + 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release + notes should be an accumulation of the release candidate release notes and any changes since the release + candidate. + 2. Create a release announcement. Refer to [PR #635] as an example release announcement. + 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. + 4. Generate the versioned release docs: + + ``` shell + make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged + and the [Build and Test][] has completed for your final PR. + +5. Checkout the release branch. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. If the tip of the release branch does not match the tip of `main`, perform the following: + + 1. Create a topic branch from the release branch. + 2. Cherry-pick the commits from `main` that differ from the release branch. + 3. Run tests locally, e.g. `make lint`. + 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. + 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. + 6. Do not proceed until the PR has merged and CI passes for the merged PR. + 7. If you are still on your topic branch, change to the release branch: + + ```shell + git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + + 8. Ensure your local release branch is up-to-date: + + ```shell + git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Tag the head of your release branch with the release tag. For example: + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' + ``` + + __Note:__ The tag version differs from the release branch by including the `.0` patch version. + +8. Push the tag to the Envoy Gateway repository. + + ```shell + git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +10. Confirm that the [release workflow][] completed successfully. +11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +12. Confirm that the [release][] was created. +13. Confirm that the steps in the [Quickstart Guide][] work as expected. +14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: + + ```console + # Release Announcement + + Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] + (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. + ``` + +If you find any bugs in this process, please create an issue. + +## Announce the Release + +It's important that the world knows about the release. Use the following steps to announce the release. + +1. Set the release information in the Envoy Gateway Slack channel. For example: + + ```shell + Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +2. Send a message to the Envoy Gateway Slack channel. For example: + + ```shell + On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway + v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. + Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs + to start using Envoy Gateway. + ... + ``` + + Link to the GitHub release and release announcement page that highlights the release. + +[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes +[Pull Request]: https://github.com/envoyproxy/gateway/pulls +[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md +[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml +[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml +[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml +[image]: https://hub.docker.com/r/envoyproxy/gateway/tags +[release]: https://github.com/envoyproxy/gateway/releases +[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +[PR #635]: https://github.com/envoyproxy/gateway/pull/635 +[PR #958]: https://github.com/envoyproxy/gateway/pull/958 +[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 +[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/site/content/en/v0.3.0/contributions/_index.md b/site/content/en/v0.3.0/contributions/_index.md new file mode 100644 index 00000000000..3255d996472 --- /dev/null +++ b/site/content/en/v0.3.0/contributions/_index.md @@ -0,0 +1,5 @@ +--- +title: Get Involved +description: "This section includes contents related to **Contributions**" +weight: 100 +--- diff --git a/site/content/en/v0.3.0/contributions/roadmap.md b/site/content/en/v0.3.0/contributions/roadmap.md new file mode 100644 index 00000000000..986043c9382 --- /dev/null +++ b/site/content/en/v0.3.0/contributions/roadmap.md @@ -0,0 +1,84 @@ +--- +title: "Roadmap" +weight: -1 +description: "This section records the roadmap of Envoy Gateway." +--- + +This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of +the project. + +## Contributing to the Roadmap + +- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use + case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update + this document accordingly. +- To help with an existing roadmap item, comment on or assign yourself to the associated issue. +- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A + maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be + discussed in an issue or a community meeting before implementing it. + +If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. +Look for issues with the `help wanted` label to get started. + +## Details + +Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific +roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by +contributing to the project. + +`Last Updated: November 2022` + +### [v0.2.0][v0.2.0]: Establish a Solid Foundation + +- Complete the core Envoy Gateway implementation- [Issue #60][60]. +- Establish initial testing, e2e, integration, etc- [Issue #64][64]. +- Establish user and developer project documentation- [Issue #17][17]. +- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. +- Setup a CI/CD pipeline- [Issue #63][63]. + +### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms + +- Support extended Gateway API fields [Issue #707][707]. +- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. +- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. +- Rate Limiting [Issue #670][670]. +- Authentication [Issue #336][336]. + +### [v0.4.0][v0.4.0]: Customizing Envoy Gateway + +- Extending Envoy Gateway control plane [Issue #20][20] +- Helm based installation for Envoy Gateway [Issue #650][650] +- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] +- Configuring xDS Resources [Issue #24][24] and [Issue #31][31]. + + +### [v0.5.0][v0.5.0]: Observability and Scale + +- Observability for control plane and data plane [Issue #701][701]. + +[issue]: https://github.com/envoyproxy/gateway/issues +[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing +[pr]: https://github.com/envoyproxy/gateway/compare +[milestones]: https://github.com/envoyproxy/gateway/milestones +[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 +[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 +[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 +[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 +[17]: https://github.com/envoyproxy/gateway/issues/17 +[20]: https://github.com/envoyproxy/gateway/issues/20 +[24]: https://github.com/envoyproxy/gateway/issues/24 +[31]: https://github.com/envoyproxy/gateway/issues/31 +[60]: https://github.com/envoyproxy/gateway/issues/60 +[63]: https://github.com/envoyproxy/gateway/issues/63 +[64]: https://github.com/envoyproxy/gateway/issues/64 +[65]: https://github.com/envoyproxy/gateway/issues/65 +[336]: https://github.com/envoyproxy/gateway/issues/336 +[641]: https://github.com/envoyproxy/gateway/issues/641 +[642]: https://github.com/envoyproxy/gateway/issues/642 +[648]: https://github.com/envoyproxy/gateway/issues/648 +[650]: https://github.com/envoyproxy/gateway/issues/650 +[643]: https://github.com/envoyproxy/gateway/issues/643 +[670]: https://github.com/envoyproxy/gateway/issues/670 +[675]: https://github.com/envoyproxy/gateway/issues/675 +[701]: https://github.com/envoyproxy/gateway/issues/701 +[707]: https://github.com/envoyproxy/gateway/issues/707 diff --git a/site/content/en/v0.3.0/design/_index.md b/site/content/en/v0.3.0/design/_index.md new file mode 100644 index 00000000000..21650809f7d --- /dev/null +++ b/site/content/en/v0.3.0/design/_index.md @@ -0,0 +1,6 @@ +--- +title: "Design" +weight: 1 +description: This section includes Designs of Envoy Gateway. +--- + diff --git a/site/content/en/v0.3.0/design/config-api.md b/site/content/en/v0.3.0/design/config-api.md new file mode 100644 index 00000000000..466b84d8f35 --- /dev/null +++ b/site/content/en/v0.3.0/design/config-api.md @@ -0,0 +1,352 @@ +--- +title: "Configuration API Design" +--- + +## Motivation + +[Issue 51][issue_51] specifies the need to design an API for configuring Envoy Gateway. The control plane is configured +statically at startup and the data plane is configured dynamically through Kubernetes resources, primarily +[Gateway API][gw_api] objects. Refer to the Envoy Gateway [design doc][design_doc] for additional details regarding +Envoy Gateway terminology and configuration. + +## Goals + +* Define an __initial__ API to configure Envoy Gateway at startup. +* Define an __initial__ API for configuring the managed data plane, e.g. Envoy proxies. + +## Non-Goals + +* Implementation of the configuration APIs. +* Define the `status` subresource of the configuration APIs. +* Define a __complete__ set of APIs for configuring Envoy Gateway. As stated in the [Goals](#goals), this document + defines the initial configuration APIs. +* Define an API for deploying/provisioning/operating Envoy Gateway. If needed, a future Envoy Gateway operator would be + responsible for designing and implementing this type of API. +* Specify tooling for managing the API, e.g. generate protos, CRDs, controller RBAC, etc. + +## Control Plane API + +The `EnvoyGateway` API defines the control plane configuration, e.g. Envoy Gateway. Key points of this API are: + +* It will define Envoy Gateway's startup configuration file. If the file does not exist, Envoy Gateway will start up + with default configuration parameters. +* EnvoyGateway inlines the `TypeMeta` API. This allows EnvoyGateway to be versioned and managed as a GroupVersionKind + scheme. +* EnvoyGateway does not contain a metadata field since it's currently represented as a static configuration file instead of + a Kubernetes resource. +* Since EnvoyGateway does not surface status, EnvoyGatewaySpec is inlined. +* If data plane static configuration is required in the future, Envoy Gateway will use a separate file for this purpose. + +The `v1alpha1` version and `config.gateway.envoyproxy.io` API group get generated: + +```go +// gateway/api/config/v1alpha1/doc.go + +// Package v1alpha1 contains API Schema definitions for the config.gateway.envoyproxy.io API group. +// +// +groupName=config.gateway.envoyproxy.io +package v1alpha1 +``` + +The initial `EnvoyGateway` API: + +```go +// gateway/api/config/v1alpha1/envoygateway.go + +package valpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyGateway is the Schema for the envoygateways API +type EnvoyGateway struct { + metav1.TypeMeta `json:",inline"` + + // EnvoyGatewaySpec defines the desired state of Envoy Gateway. + EnvoyGatewaySpec `json:",inline"` +} + +// EnvoyGatewaySpec defines the desired state of Envoy Gateway configuration. +type EnvoyGatewaySpec struct { + // Gateway defines Gateway-API specific configuration. If unset, default + // configuration parameters will apply. + // + // +optional + Gateway *Gateway `json:"gateway,omitempty"` + + // Provider defines the desired provider configuration. If unspecified, + // the Kubernetes provider is used with default parameters. + // + // +optional + Provider *Provider `json:"provider,omitempty"` +} + +// Gateway defines desired Gateway API configuration of Envoy Gateway. +type Gateway struct { + // ControllerName defines the name of the Gateway API controller. If unspecified, + // defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following + // for additional details: + // + // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass + // + // +optional + ControllerName string `json:"controllerName,omitempty"` +} + +// Provider defines the desired configuration of a provider. +// +union +type Provider struct { + // Type is the type of provider to use. If unset, the Kubernetes provider is used. + // + // +unionDiscriminator + Type ProviderType `json:"type,omitempty"` + // Kubernetes defines the configuration of the Kubernetes provider. Kubernetes + // provides runtime configuration via the Kubernetes API. + // + // +optional + Kubernetes *KubernetesProvider `json:"kubernetes,omitempty"` + + // File defines the configuration of the File provider. File provides runtime + // configuration defined by one or more files. + // + // +optional + File *FileProvider `json:"file,omitempty"` +} + +// ProviderType defines the types of providers supported by Envoy Gateway. +type ProviderType string + +const ( + // KubernetesProviderType defines the "Kubernetes" provider. + KubernetesProviderType ProviderType = "Kubernetes" + + // FileProviderType defines the "File" provider. + FileProviderType ProviderType = "File" +) + +// KubernetesProvider defines configuration for the Kubernetes provider. +type KubernetesProvider struct { + // TODO: Add config as use cases are better understood. +} + +// FileProvider defines configuration for the File provider. +type FileProvider struct { + // TODO: Add config as use cases are better understood. +} +``` + +__Note:__ Provider-specific configuration is defined in the `{$PROVIDER_NAME}Provider` API. + +### Gateway + +Gateway defines desired configuration of [Gateway API][gw_api] controllers that reconcile and translate Gateway API +resources into the Intermediate Representation (IR). Refer to the Envoy Gateway [design doc][design_doc] for additional +details. + +### Provider + +Provider defines the desired configuration of an Envoy Gateway provider. A provider is an infrastructure component that +Envoy Gateway calls to establish its runtime configuration. Provider is a [union type][union]. Therefore, Envoy Gateway +can be configured with only one provider based on the `type` discriminator field. Refer to the Envoy Gateway +[design doc][design_doc] for additional details. + +### Control Plane Configuration + +The configuration file is defined by the EnvoyGateway API type. At startup, Envoy Gateway searches for the configuration +at "/etc/envoy-gateway/config.yaml". + +Start Envoy Gateway: + +```shell +$ ./envoy-gateway +``` + +Since the configuration file does not exist, Envoy Gateway will start with default configuration parameters. + +The Kubernetes provider can be configured explicitly using `provider.kubernetes`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: {} +EOF +``` + +This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. + +The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: + foo: bar +EOF +``` + +__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for +illustration purposes only. + +The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the +File provider: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: File + file: + foo: bar +EOF +``` + +__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration +purposes only. + +Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use +default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to +manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: foo +``` + +With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: + +```shell +$ ./envoy-gateway +``` + +## Data Plane API + +The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. +Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through +`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure +configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: + +* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane + infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. +* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: + > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the + > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are + > not propagated down to existing Gateways. + +The initial `EnvoyProxy` API: + +```go +// gateway/api/config/v1alpha1/envoyproxy.go + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyProxy is the Schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvoyProxySpec `json:"spec,omitempty"` + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure +// configuration. +type EnvoyProxySpec struct { + // Undefined by this design spec. +} + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. +type EnvoyProxyStatus struct { + // Undefined by this design spec. +} +``` + +The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use +cases are better understood. + +### Data Plane Configuration + +GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is +running with the Kubernetes provider. + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration +parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening +on port 80. + +The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer +service: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + name: example-config + group: config.gateway.envoyproxy.io + kind: EnvoyProxy +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: example-config +spec: + networkPublishing: + type: ClusterIPService +``` + +__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. + +[issue_51]: https://github.com/envoyproxy/gateway/issues/51 +[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md +[gw_api]: https://gateway-api.sigs.k8s.io/ +[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/site/content/en/v0.3.0/design/egctl.md b/site/content/en/v0.3.0/design/egctl.md new file mode 100644 index 00000000000..c9db36e1b68 --- /dev/null +++ b/site/content/en/v0.3.0/design/egctl.md @@ -0,0 +1,58 @@ +--- +title: "Introduce egctl" +--- + +## Motivation + +EG should provide a command line tool with following capabilities: + +- Collect configuration from envoy proxy and gateway +- Analyse system configuration to diagnose any issues in envoy gateway + +This tool is named `egctl`. + +## Syntax + +Use the following syntax to run `egctl` commands from your terminal window: + +```console +egctl [command] [entity] [name] [flags] +``` + +where `command`, `name`, and `flags` are: + +* `command`: Specifies the operation that you want to perform on one or more resources, + for example `config`, `version`. + +* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. + +* `name`: Specifies the name of the specified instance. + +* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. + +If you need help, run `egctl help` from the terminal window. + +## Operation + +The following table includes short descriptions and the general syntax for all the `egctl` operations: + +| Operation | Syntax | Description | +| --------- | -------------------------------- | --------------------------------------------------------------------------- | +| `version` | `egctl version` | Prints out build version information. | +| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | +| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | + +## Examples + +Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: + +```console +# Retrieve all information about proxy configuration from envoy +egctl config envoy-proxy all + +# Retrieve listener information about proxy configuration from envoy +egctl config envoy-proxy listener + +# Retrieve information about envoy gateway +egctl config envoy-gateway +``` diff --git a/site/content/en/v0.3.0/design/gatewayapi-support.md b/site/content/en/v0.3.0/design/gatewayapi-support.md new file mode 100644 index 00000000000..67eaf05bb4a --- /dev/null +++ b/site/content/en/v0.3.0/design/gatewayapi-support.md @@ -0,0 +1,121 @@ +--- +title: "Gateway API Support" +--- + +As mentioned in the [system design][] document, Envoy Gateway's managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][] objects. Envoy Gateway supports configuration using the following Gateway API resources. + +## GatewayClass + +A [GatewayClass][] represents a "class" of gateways, i.e. which Gateways should be managed by Envoy Gateway. +Envoy Gateway supports managing __a single__ GatewayClass resource that matches its configured `controllerName` and +follows Gateway API guidelines for [resolving conflicts][] when multiple GatewayClasses exist with a matching +`controllerName`. + +__Note:__ If specifying GatewayClass [parameters reference][], it must refer to an [EnvoyProxy][] resource. + +## Gateway + +When a [Gateway][] resource is created that references the managed GatewayClass, Envoy Gateway will create and manage a +new Envoy Proxy deployment. Gateway API resources that reference this Gateway will configure this managed Envoy Proxy +deployment. + +__Note:__ Envoy Gateway does not support multiple certificate references or specifying an [address][] for the Gateway. + +## HTTPRoute + +An [HTTPRoute][] configures routing of HTTP traffic through one or more Gateways. The following HTTPRoute filters are +supported by Envoy Gateway: + +- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. +- `requestRedirect`: [RequestRedirects](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + configure policied for how requests that match the HTTPRoute should be modified and then redirected. +- `urlRewrite`: [UrlRewrites](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + allow for modification of the request's hostname and path before it is proxied to its destination. +- `extensionRef`: [ExtensionRefs][] are used by Envoy Gateway to implement extended filters. Currently, Envoy Gateway + supports rate limiting and request authentication filters. For more information about these filters, refer to the + [rate limiting][] and [request authentication][] documentation. + +__Notes:__ +- The only [BackendRef][] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other destinations such + as arbitrary URLs is not possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TCPRoute + +A [TCPRoute][] configures routing of raw TCP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a TCP port number. + +__Note:__ A TCPRoute only supports proxying in non-transparent mode, i.e. the backend will see the source IP and port of +the Envoy Proxy instance instead of the client. + +## UDPRoute + +A [UDPRoute][] configures routing of raw UDP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a UDP port number. + +__Note:__ Similar to TCPRoutes, UDPRoutes only support proxying in non-transparent mode i.e. the backend will see the +source IP and port of the Envoy Proxy instance instead of the client. + +## GRPCRoute + +A [GRPCRoute][] configures routing of [gRPC][] requests through one or more Gateways. They offer request matching by +hostname, gRPC service, gRPC method, or HTTP/2 Header. Envoy Gateway supports the following filters on GRPCRoutes to +provide additional traffic processing: + +- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. + +__Notes:__ +- The only [BackendRef][grpc-filter] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other + destinations such as arbitrary URLs is not currently possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TLSRoute + +A [TLSRoute][] configures routing of TCP traffic through one or more Gateways. However, unlike TCPRoutes, TLSRoutes +can match against TLS-specific metadata. + +## ReferenceGrant + +A [ReferenceGrant][] is used to allow a resource to reference another resource in a different namespace. Normally an +HTTPRoute created in namespace `foo` is not allowed to reference a Service in namespace `bar`. A ReferenceGrant permits +these types of cross-namespace references. Envoy Gateway supports the following ReferenceGrant use-cases: + +- Allowing an HTTPRoute, GRPCRoute, TLSRoute, UDPRoute, or TCPRoute to reference a Service in a different namespace. +- Allowing an HTTPRoute's `requestMirror` filter to include a BackendRef that references a Service in a different + namespace. +- Allowing a Gateway's [SecretObjectReference][] to reference a secret in a different namespace. + +[system design]: https://gateway.envoyproxy.io/latest/design/system-design.html +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[GatewayClass]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayClass +[parameters reference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.ParametersReference +[Gateway]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway +[address]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayAddress +[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute +[Service]: https://kubernetes.io/docs/concepts/services-networking/service/ +[BackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPBackendRef +[TCPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute +[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[gRPC]: https://grpc.io/ +[TLSRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute +[ReferenceGrant]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.ReferenceGrant +[SecretObjectReference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference +[rate limiting]: https://gateway.envoyproxy.io/latest/user/rate-limit.html +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[EnvoyProxy]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoyproxy +[resolving conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/?h=conflict#conflicts +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType +[grpc-filter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter diff --git a/site/content/en/v0.3.0/design/gatewayapi-translator.md b/site/content/en/v0.3.0/design/gatewayapi-translator.md new file mode 100644 index 00000000000..b83ed82588d --- /dev/null +++ b/site/content/en/v0.3.0/design/gatewayapi-translator.md @@ -0,0 +1,253 @@ +--- +title: "Gateway API Translator Design" +weight: 4 +--- + +The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate +Representation (IR). + +## Assumptions + +Initially target core conformance features only, to be followed by extended conformance features. + +## Inputs and Outputs + +The main inputs to the Gateway API translator are: + +- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. + +__Note:__ ReferenceGrant is not fully implemented as of v0.2. + +The outputs of the Gateway API translator are: + +- Xds and Infra Internal Representations (IRs). +- Status updates for GatewayClass, Gateways, HTTPRoutes + +## Listener Compatibility + +Envoy Gateway follows Gateway API listener compatibility spec: +> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. + +__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. + +### Listener Compatibility Examples + +#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +## Computing Status + +Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway +must compute the appropriate status fields and conditions for managed resources. + +Status is computed and set for: + +- The managed GatewayClass (`gatewayclass.status.conditions`). +- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the + Envoy Deployment and Service status are also included to calculate Gateway status. +- Listeners for each Gateway (`gateway.status.listeners`). +- The ParentRef for each Route (`route.status.parents`). + +The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to +the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and +updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to +update resource status on the Kubernetes API server. + +## Outline + +The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway +API resources. + +1. Process Gateway Listeners + - Validate unique hostnames, ports, and protocols. + - Validate and compute supported kinds. + - Validate allowed namespaces (validate selector if specified). + - Validate TLS fields if specified, including resolving referenced Secrets. + +2. Process HTTPRoutes + - foreach route rule: + - compute matches + - [core] path exact, path prefix + - [core] header exact + - [extended] query param exact + - [extended] HTTP method + - compute filters + - [core] request header modifier (set/add/remove) + - [core] request redirect (hostname, statuscode) + - [extended] request mirror + - compute backends + - [core] Kubernetes services + - foreach route parent ref: + - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) + - foreach matching listener: + - foreach hostname intersection with route: + - add each computed route rule to host + +## Context Structs + +To help store, access and manipulate information as it's processed during the translation process, a set of context +structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support +processing. + +`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. + +```go +type GatewayContext struct { + // The managed Gateway + *v1beta1.Gateway + + // A list of Gateway ListenerContexts. + listeners []*ListenerContext +} +``` + +`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on +the associated Gateway. + +```go +type ListenerContext struct { + // The Gateway listener. + *v1beta1.Listener + + // The Gateway this Listener belongs to. + gateway *v1beta1.Gateway + + // An index used for managing this listener in the list of Gateway listeners. + listenerStatusIdx int + + // Only Routes in namespaces selected by the selector may be attached + // to the Gateway this listener belongs to. + namespaceSelector labels.Selector + + // The TLS Secret for this Listener, if applicable. + tlsSecret *v1.Secret +} +``` + +`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. + +```go +type RouteContext interface { + client.Object + + // GetRouteType returns the Kind of the Route object, HTTPRoute, + // TLSRoute, TCPRoute, UDPRoute etc. + GetRouteType() string + + // GetHostnames returns the hosts targeted by the Route object. + GetHostnames() []string + + // GetParentReferences returns the ParentReference of the Route object. + GetParentReferences() []v1beta1.ParentReference + + // GetRouteParentContext returns RouteParentContext by using the Route + // objects' ParentReference. + GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext +} +``` + +[message bus]: watching.md diff --git a/site/content/en/v0.3.0/design/goals.md b/site/content/en/v0.3.0/design/goals.md new file mode 100644 index 00000000000..fd38b2004c6 --- /dev/null +++ b/site/content/en/v0.3.0/design/goals.md @@ -0,0 +1,91 @@ +--- +title: "Goals" +weight: 1 +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption +through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing +use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer +fundamental interactions. + +## Objectives + +### Expressive API + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +### Batteries included + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +### All environments + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API][]. +Additional goals include multi-cluster support and various runtime environments. + +### Extensibility + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +## Non-objectives + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +## Personas + +_In order of priority_ + +### 1. Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +### 2. Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/docs/v0.3.0/design/ratelimit.md b/site/content/en/v0.3.0/design/ratelimit.md similarity index 99% rename from docs/v0.3.0/design/ratelimit.md rename to site/content/en/v0.3.0/design/ratelimit.md index a88021f7c71..5d080e4d08c 100644 --- a/docs/v0.3.0/design/ratelimit.md +++ b/site/content/en/v0.3.0/design/ratelimit.md @@ -1,4 +1,6 @@ -# Rate Limit Design +--- +title: "Rate Limit Design" +--- ## Overview @@ -265,7 +267,7 @@ attribute such as [IP subnet][] in the future that are not relevant in the [HTTP ### Global Rate limiting * [Global rate limiting][] in Envoy Proxy can be achieved using the following - - * [Actions][] can be conifgured per [xDS Route][]. + * [Actions][] can be configured per [xDS Route][]. * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined using a [rate limit filter][]. diff --git a/site/content/en/v0.3.0/design/request-authentication.md b/site/content/en/v0.3.0/design/request-authentication.md new file mode 100644 index 00000000000..a88f7392b1f --- /dev/null +++ b/site/content/en/v0.3.0/design/request-authentication.md @@ -0,0 +1,515 @@ +--- +title: "Request Authentication" +--- + +## Overview + +[Issue 336][] specifies the need for exposing a user-facing API to configure request authentication. Request +authentication is defined as an authentication mechanism to be enforced by Envoy on a per-request basis. A connection +will be rejected if it contains invalid authentication information, based on the `AuthenticationFilter` API type +proposed in this design document. + +Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and +implementation-specific API [support levels][] for implementers such as Envoy Gateway to expose features. Since +implementing request authentication is not covered by `Core` or `Extended` APIs, an `Implementation-specific` API will +be created for this purpose. + +## Goals + +* Define an API for configuring request authentication. +* Implement [JWT] as the first supported authentication type. +* Allow users that manage routes, e.g. [HTTPRoute][], to authenticate matching requests before forwarding to a backend + service. +* Support HTTPRoutes as an Authentication API referent. HTTPRoute provides multiple [extension points][]. The + [HTTPRouteFilter][] is the extension point supported by the Authentication API. + +## Non-Goals + +* Allow infrastructure administrators to override or establish default authentication policies. +* Support referents other than HTTPRoute. +* Support Gateway API extension points other than HTTPRouteFilter. + +## Use-Cases + +These use-cases are presented as an aid for how users may attempt to utilize the outputs of the design. They are not an +exhaustive list of features for authentication support in Envoy Gateway. + +As a Service Producer, I need the ability to: +* Authenticate a request before forwarding it to a backend service. +* Have different authentication mechanisms per route rule. +* Choose from different authentication mechanisms supported by Envoy, e.g. OIDC. + +### Authentication API Type + +The Authentication API type defines authentication configuration for authenticating requests through managed Envoy +proxies. + +```go +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +) + +type AuthenticationFilter struct { + metav1.TypeMeta + metav1.ObjectMeta + + // Spec defines the desired state of the Authentication type. + Spec AuthenticationFilterSpec + + // Note: The status sub-resource has been excluded but may be added in the future. +} + +// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. +// +union +type AuthenticationFilterSpec struct { + // Type defines the type of authentication provider to use. Supported provider types are: + // + // * JWT: A provider that uses JSON Web Token (JWT) for authenticating requests. + // + // +unionDiscriminator + Type AuthenticationFilterType + + // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple + // jwtProviders are specified, the JWT is considered valid if any of the providers + // successfully validate the JWT. + JwtProviders []JwtAuthenticationFilterProvider +} + +... +``` + +Refer to [PR 773][] for the detailed AuthenticationFilter API spec. + +The status subresource is not included in the AuthenticationFilter API. Status will be surfaced by an HTTPRoute that +references an AuthenticationFilter. For example, an HTTPRoute will surface the `ResolvedRefs=False` status condition if it +references an AuthenticationFilter that does not exist. It may be beneficial to add AuthenticationFilter status fields in the future +based on defined use-cases. For example, a remote [JWKS][] can be validated based on the specified URI and have an +appropriate status condition surfaced. + +#### AuthenticationFilter Example + +The following is an AuthenticationFilter example with one JWT authentication provider: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example +spec: + type: JWT + jwtProviders: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJwks: + uri: https://foo.com/jwt/public-key/jwks.json + +``` + +__Note:__ `type` is a union type, allowing only one of any supported provider type such as `jwtProviders` to be +specified. + +The following is an example HTTPRoute configured to use the above JWT authentication provider: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example + backendRefs: + - name: backend + port: 3000 +``` + +Requests for `www.example.com/foo` will be authenticated using the referenced JWT provider before being forwarded to the +backend service named "backend". + +## Implementation Details + +The JWT authentication type is translated to an Envoy [JWT authentication filter][] and a cluster is created for each +remote [JWKS][]. The following examples provide additional details on how Gateway API and AuthenticationFilter resources are +translated into Envoy configuration. + +### Example 1: One Route with One JWT Provider + +The following cluster is created from the above HTTPRoute and AuthenticationFilter: + +```yaml +dynamic_clusters: + - name: foo.com|443 + load_assignment: + cluster_name: foo.com|443 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: foo.com + port_value: 443 + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: foo.com + common_tls_context: + validation_context: + match_subject_alt_names: + - exact: "*.foo.com" + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +``` + +A JWT authentication HTTP filter is added to the HTTP Connection Manager. For example: + +```yaml +dynamic_resources: + dynamic_listeners: + - name: example_listener + address: + socket_address: + address: 1.2.3.4 + port_value: 80 + filter_chains: + - filters: + - name: envoy.http_connection_manager + http_filters: + - name: envoy.filters.http.jwt_authn + typed_config: + "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication +``` + +This JWT authentication HTTP filter contains two fields: +* The `providers` field specifies how a JWT should be verified, such as where to extract the token, where to fetch the + public key ([JWKS][]) and where to output its payload. This field is built from the source resource `namespace-name`, and + the JWT provider name of an AuthenticationFilter. +* The `rules` field specifies matching rules and their requirements. If a request matches a rule, its requirement + applies. The requirement specifies which JWT providers should be used. This field is built from a HTTPRoute + `matches` rule that references the AuthenticationFilter. When a referenced Authentication specifies multiple + `jwtProviders`, the JWT is considered valid if __any__ of the providers successfully validate the JWT. + +The following JWT authentication HTTP filter `providers` configuration is created from the above AuthenticationFilter. + +```yaml +providers: + example: + issuer: https://www.example.com + audiences: + - foo.com + remote_jwks: + http_uri: + uri: https://foo.com/jwt/public-key/jwks.json + cluster: example_jwks_cluster + timeout: 1s +``` + +The following JWT authentication HTTP filter `rules` configuration is created from the above HTTPRoute. + +```yaml +rules: + - match: + prefix: /foo + requires: + provider_name: default-example-example +``` + +### Example 2: Two HTTPRoutes with Different AuthenticationFilters + +The following example contains: +* Two HTTPRoutes with different hostnames. +* Each HTTPRoute references a different AuthenticationFilter. +* Each AuthenticationFilter contains a different JWT provider. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example1 +spec: + type: JWT + jwtProviders: + - name: example1 + issuer: https://www.example1.com + audiences: + - foo.com + remoteJwks: + uri: https://foo.com/jwt/public-key/jwks.json +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example2 +spec: + type: JWT + jwtProviders: + - name: example2 + issuer: https://www.example2.com + audiences: + - bar.com + remoteJwks: + uri: https://bar.com/jwt/public-key/jwks.json +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example1 +spec: + hostnames: + - www.example1.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example1 + backendRefs: + - name: backend + port: 3000 +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example2 +spec: + hostnames: + - www.example2.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - matches: + - path: + type: PathPrefix + value: /bar + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example2 + backendRefs: + - name: backend2 + port: 3000 +``` + +The following xDS configuration is created from the above example resources: + +```yaml +configs: +... +dynamic_listeners: + - name: default-eg-http + ... + default_filter_chain: + filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + '@type': >- + type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: http + rds: + config_source: + ... + route_config_name: default-eg-http + http_filters: + - name: envoy.filters.http.jwt_authn + typed_config: + '@type': >- + type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication + providers: + default-example1-example1: + issuer: https://www.example1.com + audiences: + - foo.com + remote_jwks: + http_uri: + uri: https://foo.com/jwt/public-key/jwks.json + cluster: default-example1-example1-jwt + default-example2-example2: + issuer: https://www.example2.com + audiences: + - bar.com + remote_jwks: + http_uri: + uri: https://bar.com/jwt/public-key/jwks.json + cluster: default-example2-example2-jwt + rules: + - match: + exact: /foo + requires: + provider_name: default-example1-example1 + - match: + exact: /bar + requires: + provider_name: default-example2-example2 + - name: envoy.filters.http.router + typed_config: + '@type': >- + type.googleapis.com/envoy.extensions.filters.http.router.v3.Router +dynamic_route_configs: + - route_config: + '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration + name: default-eg-http + virtual_hosts: + - name: default-eg-http + domains: + - '*' + routes: + - match: + prefix: /foo + headers: + - name: ':authority' + string_match: + exact: www.example1.com + route: + cluster: default-backend-rule-0-match-0-www.example1.com + - match: + prefix: /bar + headers: + - name: ':authority' + string_match: + exact: www.example2.com + route: + cluster: default-backend2-rule-0-match-0-www.example2.com +dynamic_active_clusters: + - cluster: + name: default-backend-rule-0-match-0-www.example.com + ... + endpoints: + - locality: {} + lb_endpoints: + - endpoint: + address: + socket_address: + address: $BACKEND_SERVICE1_IP + port_value: 3000 + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + name: default-backend-rule-1-match-0-www.example.com + ... + endpoints: + - locality: {} + lb_endpoints: + - endpoint: + address: + socket_address: + address: $BACKEND_SERVICE2_IP + port_value: 3000 +... +``` + +__Note:__ The JWT provider cluster and route is omitted from the above example for brevity. + +### Implementation Outline + +* Update the Kubernetes provider to get/watch AuthenticationFilter resources that are referenced by managed HTTPRoutes. + Add the referenced AuthenticationFilter object to the resource map and publish it. +* Update the resource translator to include the AuthenticationFilter API in HTTPRoute processing. +* Update the xDS translator to translate an AuthenticationFilter into xDS resources. The translator should perform the + following: + * Convert a list of JWT rules from the xds IR into an Envoy JWT filter config. + * Create a JWT authentication HTTP filter. + * Build the HTTP Connection Manager (HCM) HTTP filters. + * Build the HCM. + * When building the Listener, create an HCM for each filter-chain. + +## Adding Authentication Types + +Additional authentication types can be added in the future through the `AuthenticationFilterType` API. For +example, to add the `Foo` authentication type: + +Define the `Foo` authentication provider: + +```go +package v1alpha1 + +// FooAuthenticationFilterProvider defines the "Foo" authentication filter provider type. +type FooAuthenticationFilterProvider struct { + // TODO: Define fields of the Foo authentication filter provider type. +} +``` + +Add the `FooAuthenticationFilterProvider` type to `AuthenticationFilterSpec`: + +```go +package v1alpha1 + +type AuthenticationFilterSpec struct { + ... + + // Foo defines the Foo authentication type. For additional + // details, see: + // + // + // + // +optional + Foo *FooAuthenticationFilterProvider +} +``` + +Lastly, add the type to the `AuthenticationType` enum: + +```go +// AuthenticationType is a type of authentication provider. +// +kubebuilder:validation:Enum=JWT,FOO +type AuthenticationFilterType string + +const ( + // JwtAuthenticationProviderType is the JWT authentication provider type. + FooAuthenticationFilterProviderType AuthenticationFilterType = "FOO" +) +``` + +The AuthenticationFilter API should support additional authentication types in the future, for example: +- OAuth2 +- OIDC + +## Outstanding Questions + +- If Envoy Gateway owns the AuthenticationFilter API, is an xDS IR equivalent needed? +- Should local [JWKS][] be implemented before remote [JWKS][]? +- How should Envoy obtain the trusted CA for a remote [JWKS][]? +- Should HTTPS be the only supported scheme for remote [JWKS][]? +- Should OR'ing JWT providers be supported? +- Should Authentication provide status? +- Are the API field validation rules acceptable? + +[Issue 336]: https://github.com/envoyproxy/gateway/issues/336 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels +[JWT]: https://jwt.io/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[extension points]: https://gateway-api.sigs.k8s.io/concepts/api-overview/?h=extension#extension-points +[HTTPRouteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter +[JWKS]: https://www.rfc-editor.org/rfc/rfc7517 +[JWT authentication filter]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn +[PR 773]: https://github.com/envoyproxy/gateway/pull/733 diff --git a/site/content/en/v0.3.0/design/system-design.md b/site/content/en/v0.3.0/design/system-design.md new file mode 100644 index 00000000000..72c0a98ecda --- /dev/null +++ b/site/content/en/v0.3.0/design/system-design.md @@ -0,0 +1,174 @@ +--- +title: "System Design" +weight: 2 +--- + +## Goals + +* Define the system components needed to satisfy the requirements of Envoy Gateway. + +## Non-Goals + +* Create a detailed design and interface specification for each system component. + +## Terminology + +* Control Plane- A collection of inter-related software components for providing application gateway and routing + functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. + These services are detailed in the [components](#components) section. +* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. + +## Architecture + +![Architecture](/img/architecture.png) + +## Configuration + +Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][gw_api] objects. + +### Static Configuration + +Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, +configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the +configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. + +### Dynamic Configuration + +Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using +reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is +defined as Kubernetes resources that provide the following services: + +* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is + expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be + referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, + e.g. expose Envoy network endpoints using a NodePort service instead of a LoadBalancer service. +* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP + requests for "www.example.com" to a backend service running a web server. This configuration is expressed through + [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. + Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] + reference. + +## Components + +Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described +in the [Watching Components Design][wcd]. + +### Provider + +A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve +services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap +via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A +provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). + +#### Kubernetes Provider + +* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the + [dynamic configuration](#dynamic-configuration). +* Manages the data plane through Kubernetes API CRUD operations. +* Uses Kubernetes for Service discovery. +* Uses etcd (via Kubernetes API) to persist data. + +#### File Provider + +* Uses a file watcher to watch files in a directory that define the data plane configuration. +* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. +* Uses the host's DNS for Service discovery. +* If needed, the local filesystem is used to persist data. + +### Resource Watcher + +The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The +mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes +provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator +as output. + +### Resource Translator + +The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate +Representation (IR). It is responsible for: + +* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. +* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. + +__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. + +### Intermediate Representation (IR) + +The Intermediate Representation defines internal data models that external resources are translated into. This allows +Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR +used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. + +* Infra IR- Used as the internal definition of the managed data plane infrastructure. +* xDS IR- Used as the internal definition of the managed data plane xDS configuration. + +### xDS Translator + +The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. + +### xDS Server + +The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server +Protocol and is responsible for using xDS to configure the data plane. + +### Infra Manager + +The Infra Manager is a provider-specific component responsible for managing the following infrastructure: + +* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, + Service, etc. resources to run Envoy in a Kubernetes cluster. +* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require + external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning + and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to + users through the [Custom Route Filters][crf] extension. + +The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. + +## Design Decisions + +* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with + `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy + Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. + `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy + infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. +* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. + * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. + * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners + in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are + met: + * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies + either the “HTTPS” or “TLS” Protocol. + * Each Listener within the group specifies a unique "Hostname". + * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no + other Listener matches. + * Envoy Gateway does __not__ merge listeners across multiple Gateways. +* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. + * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. +* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. + * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. +* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. + +The draft for this document is [here][draft_design]. + +[gw_api]: https://gateway-api.sigs.k8s.io +[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass +[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway +[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute +[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute +[go_cp]: https://github.com/envoyproxy/go-control-plane +[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[rls]: https://github.com/envoyproxy/ratelimit +[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional +[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts +[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners +[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route +[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional +[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster +[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference +[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ +[ wcd ]: ./watching.md +[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 +[roadmap]: roadmap.md diff --git a/site/content/en/v0.3.0/design/tcp-udp-design.md b/site/content/en/v0.3.0/design/tcp-udp-design.md new file mode 100644 index 00000000000..f517e24feda --- /dev/null +++ b/site/content/en/v0.3.0/design/tcp-udp-design.md @@ -0,0 +1,49 @@ +--- +title: "TCP and UDP Proxy Design " +--- + +Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP +and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the +design decision. + +Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) + and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) +, so ideally, Envoy Gateway should also be able to work in these two modes: + +## Non-transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the +TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when +proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see Envoy's IP address and port. + +## Transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies +the TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address +when proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see the original downstream IP address and Envoy's mac address. + +Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward +the port number. + +## The Implications of Transparent Proxy Mode + +### Escalated Privilege +Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated +CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. + +### Routing +The upstream can see the original source IP, but the original port number won't be passed, so the return +traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back +to the right port number of the downstream, which requires routing at the upstream side to be set up. +In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. + +## The Design Decision (For Now) + +The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and +port of the deployed Envoy instance instead of the client. diff --git a/site/content/en/v0.3.0/design/watching.md b/site/content/en/v0.3.0/design/watching.md new file mode 100644 index 00000000000..72a955043e0 --- /dev/null +++ b/site/content/en/v0.3.0/design/watching.md @@ -0,0 +1,120 @@ +--- +title: "Watching Components Design" +weight: 3 +--- + +Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch +external resources, and "publish" what they see for other components to consume; others watch what another publishes and +act on it (such as the resource translator watches what the providers publish, and then publishes its own results that +are watched by another component). Some of these internally published results are consumed by multiple components. + +To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the +standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub +pattern. + +## Pub + +Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" +tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if +we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by +the IR translator: + + ```go + type ResourceTable struct { + // gateway classes are cluster-scoped; no namespace + GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] + + // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. + Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] + + HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] + } + ``` + +The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; +updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` +method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher +doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just +`.Store` its data and `watchable` will do the right thing. + +## Sub + +Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or +`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: + + ```go + func(ctx context.Context) error { + for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { + fullState := irInput{ + GatewayClasses: k8sTable.GatewayClasses.LoadAll(), + Gateways: k8sTable.Gateways.LoadAll(), + HTTPRoutes: snapshot.State, + } + translate(irInput) + } + } + ``` + +Or, to watch multiple maps in the same loop: + + ```go + func worker(ctx context.Context) error { + classCh := k8sTable.GatewayClasses.Subscribe(ctx) + gwCh := k8sTable.Gateways.Subscribe(ctx) + routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) + for ctx.Err() == nil { + var arg irInput + select { + case snapshot := <-classCh: + arg.GatewayClasses = snapshot.State + case snapshot := <-gwCh: + arg.Gateways = snapshot.State + case snapshot := <-routeCh: + arg.Routes = snapshot.State + } + if arg.GateWayClasses == nil { + arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() + } + if arg.GateWays == nil { + arg.Gateways = k8sTable.GateWays.LoadAll() + } + if arg.HTTPRoutes == nil { + arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() + } + translate(irInput) + } + } + ``` + +From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; +but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a +handy way to know when to run, `.Load` and friends can be used without subscribing. + +There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but +it's probably wise to just have one publisher for each map. + +The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when +`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple +mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in +to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of +each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need +to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. + +If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` +entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you +must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to +help with this. + +## Other Notes + +The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the +map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb +`chan`. + +A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types +be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and +so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can +generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate +methods for. + +[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/site/content/en/v0.3.0/user/_index.md b/site/content/en/v0.3.0/user/_index.md new file mode 100644 index 00000000000..2f23014d867 --- /dev/null +++ b/site/content/en/v0.3.0/user/_index.md @@ -0,0 +1,6 @@ +--- +title: "User Guides" +weight: 2 +description: This section includes User Guides of Envoy Gateway. +--- + diff --git a/site/content/en/v0.3.0/user/authn.md b/site/content/en/v0.3.0/user/authn.md new file mode 100644 index 00000000000..312e4103b9b --- /dev/null +++ b/site/content/en/v0.3.0/user/authn.md @@ -0,0 +1,96 @@ +--- +title: "Request Authentication" +--- + +This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks +if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only +supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. + +## Installation + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Configuration + +Allow requests with a valid JWT by creating an [AuthenticationFilter][] and referencing it from the example HTTPRoute. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.3.0/examples/kubernetes/authn/jwt.yaml +``` + +The HTTPRoute is now updated to authenticate requests for `/foo` and allow unauthenticated requests to `/bar`. The +`/foo` route rule references an AuthenticationFilter that provides the JWT authentication configuration. + +Verify the HTTPRoute configuration and status: + +```shell +kubectl get httproute/backend -o yaml +``` + +The AuthenticationFilter is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the AuthenticationFilter configuration: + +```shell +kubectl get authenticationfilter/jwt-example -o yaml +``` + +## Testing + +Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](../quickstart) guide is set. If not, follow the +Quickstart instructions to set the variable. + +```shell +echo $GATEWAY_HOST +``` + +Verify that requests to `/foo` are denied without a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `401` HTTP response code should be returned. + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `/foo` with a valid JWT is allowed: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `200` HTTP response code should be returned. + +Verify that requests to `/bar` are allowed __without__ a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar +``` + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the AuthenticationFilter: + +```shell +kubectl delete authenticationfilter/jwt-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[jwt]: https://tools.ietf.org/html/rfc7519 +[AuthenticationFilter]: https://gateway.envoyproxy.io/v0.3.0/api/extension_types.html#authenticationfilter +[jwks]: https://tools.ietf.org/html/rfc7517 diff --git a/site/content/en/v0.3.0/user/grpc-routing.md b/site/content/en/v0.3.0/user/grpc-routing.md new file mode 100644 index 00000000000..71c7179243f --- /dev/null +++ b/site/content/en/v0.3.0/user/grpc-routing.md @@ -0,0 +1,102 @@ +--- +title: "GRPC Routing" +--- + +The [GRPCRoute][] resource allows users to configure gRPC routing by matching HTTP/2 traffic and forwarding it to backend gRPC servers. +To learn more about gRPC routing, refer to the [Gateway API documentation][]. + +## Prerequisites + +Install Envoy Gateway: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## Installation + +Install the gRPC routing example resources: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.3.0/examples/kubernetes/grpc-routing.yaml +``` + +The manifest installs a [GatewayClass][], [Gateway][], a Deployment, a Service, and a GRPCRoute resource. +The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. + +__Note:__ Envoy Gateway is configured by default to manage a GatewayClass with +`controllerName: gateway.envoyproxy.io/gatewayclass-controller`. + +## Verification + +Check the status of the GatewayClass: + +```shell +kubectl get gc --selector=example=grpc-routing +``` + +The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass. + +A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is +provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this +Gateway. Check the status of the Gateway: + +```shell +kubectl get gateways --selector=example=grpc-routing +``` + +The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also +provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend +services. + +Check the status of the GRPCRoute: + +```shell +kubectl get grpcroutes --selector=example=grpc-routing -o yaml +``` + +The status for the GRPCRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway. +The `example-route` matches any traffic for "grpc-example.com" and forwards it to the "yages" Service. + +## Testing the Configuration + +Before testing GRPC routing to the `yages` backend, get the Gateway's address. + +```shell +export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}') +``` + +Test GRPC routing to the `yages` backend using the [grpcurl][] command. + +```shell +grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +{ + "text": "pong" +} +``` + +Envoy Gateway also supports [gRPC-Web][] requests for this configuration. The below `curl` command can be used to send a grpc-Web request with over HTTP/2. You should receive the same response seen in the previous command. + +```shell +curl --http2-prior-knowledge -s ${GATEWAY_HOST}:80/yages.Echo/Ping -H 'Host: grpc-example.com' -H 'Content-Type: application/grpc-web-text' -H 'Accept: application/grpc-web-text' -XPOST -d'AAAAAAA=' | base64 -d +``` + + +[GRPCRoute]: https://gateway-api.sigs.k8s.io/api-types/grpcroute/ +[Gateway API documentation]: https://gateway-api.sigs.k8s.io/ +[GatewayClass]: https://gateway-api.sigs.k8s.io/api-types/gatewayclass/ +[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway/ +[Envoy proxy]: https://www.envoyproxy.io/ +[grpcurl]: https://github.com/fullstorydev/grpcurl +[gRPC-Web]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2 diff --git a/site/content/en/v0.3.0/user/http-redirect.md b/site/content/en/v0.3.0/user/http-redirect.md new file mode 100644 index 00000000000..da61bdaf32f --- /dev/null +++ b/site/content/en/v0.3.0/user/http-redirect.md @@ -0,0 +1,129 @@ +--- +title: "HTTP Redirects" +--- + +The [HTTPRoute][] resource can issue redirects to clients or rewrite paths sent upstream using filters. Note that +HTTPRoute rules cannot use both filter types at once. Currently, Envoy Gateway only supports __core__ +[HTTPRoute filters][] which consist of `RequestRedirect` and `RequestHeaderModifier` at the time of this writing. To +learn more about HTTP routing, refer to the [Gateway API documentation][]. + +## Prerequisites + +Follow the steps from the [Secure Gateways](secure-gateways.md) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTPS. + +## Redirects + +Redirects return HTTP 3XX responses to a client, instructing it to retrieve a different resource. A +[`RequestRedirect` filter][req_filter] instructs Gateways to emit a redirect response to requests that match the rule. +For example, to issue a permanent redirect (301) from HTTP to HTTPS, configure `requestRedirect.statusCode=301` and +`requestRedirect.scheme="https"`: + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something", + "foo" + ], +... +``` + +## Setting Request Headers + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + "headers": { + "Accept": [ + "*/*" + ], + "Set-Header": [ + "foo" + ], +... +``` + +## Removing Request Headers + +Headers can be removed from a request by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something" + ], +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: X-Foo: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< x-foo: value1 +< add-header: foo +< +... + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "X-Foo: value1" + ] +... +``` + +## Setting Response Headers + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: set-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< set-header: foo +< + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "set-header": value1" + ] +... +``` + +## Removing Response Headers + +Headers can be removed from a response by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: remove-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "remove-header": value1" + ] +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +## Multiple backendRefs + +If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is +configured. + +First, create a second instance of the example app from the quickstart: + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-75bcd4c969-lsxpz" +... +``` + +## Weighted backendRefs + +If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` +field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is +assumed to be `1`. + +The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of +requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the +HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. + +The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the +backend-2 service. + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< server: envoy +< content-length: 0 +< +``` + +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/site/content/en/v0.3.0/user/http-urlrewrite.md b/site/content/en/v0.3.0/user/http-urlrewrite.md new file mode 100644 index 00000000000..60c0bebeeef --- /dev/null +++ b/site/content/en/v0.3.0/user/http-urlrewrite.md @@ -0,0 +1,297 @@ +--- +title: "HTTP URL Rewrite" +--- + +[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be +used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Rewrite URL Prefix Path + +You can configure to rewrite the prefix in the url like below. In this example, any curls to +`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. + +```shell +cat < GET /get/origin/path HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> + +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:03:28 GMT +< content-length: 503 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/replace/origin/path", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "fd84b842-9937-4fb5-83c7-61470d854b90" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. + +## Rewrite URL Full Path + +You can configure to rewrite the fullpath in the url like below. In this example, any request sent to +`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to +`http://${GATEWAY_HOST}/force/replace/fullpath`. + +```shell +cat < GET /get/origin/path/extra HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:09:31 GMT +< content-length: 512 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/force/replace/fullpath", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path/extra" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "8ab774d6-9ffa-4faa-abbb-f45b0db00895" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is +`/force/replace/fullpath`. + +## Rewrite Host Name + +You can configure to rewrite the hostname like below. In this example, any requests sent to +`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. + +```shell +cat < GET /get HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:15:15 GMT +< content-length: 481 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "envoygateway.io", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Forwarded-Host": [ + "path.rewrite.example" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39aa447c-97b9-45a3-a675-9fb266ab1af0" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. + +[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPURLRewriteFilter diff --git a/site/content/en/v0.3.0/user/quickstart.md b/site/content/en/v0.3.0/user/quickstart.md new file mode 100644 index 00000000000..4875a1ff987 --- /dev/null +++ b/site/content/en/v0.3.0/user/quickstart.md @@ -0,0 +1,100 @@ +--- +title: "Quickstart" +weight: 1 +--- + +This guide will help you get started with Envoy Gateway in a few simple steps. + +## Prerequisites + +A Kubernetes cluster. + +__Note:__ Refer to the [Compatibility Matrix](/blog/2022/10/01/versions/) for supported Kubernetes versions. + +## Installation + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` + +### External LoadBalancer Support + +You can also test the same functionality by sending traffic to the External IP. To get the external IP of the +Envoy service, run: + +```shell +export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace +`ip` in the above command with `hostname`. + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get +``` + +## Clean-Up + +Use the steps in this section to uninstall everything from the quickstart guide. + +Delete the GatewayClass, Gateway, HTTPRoute and Example App: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml --ignore-not-found=true +``` + +Delete the Gateway API CRDs and Envoy Gateway: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml --ignore-not-found=true +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. diff --git a/site/content/en/v0.3.0/user/rate-limit.md b/site/content/en/v0.3.0/user/rate-limit.md new file mode 100644 index 00000000000..08eae102547 --- /dev/null +++ b/site/content/en/v0.3.0/user/rate-limit.md @@ -0,0 +1,491 @@ +--- +title: "Rate limit" +--- + +Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + +Here are some reasons why you may want to implements Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied +i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit +if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. + +Envoy Gateway introduces a new CRD called [RateLimitFilter][] that allows the user to describe their rate limit intent. This instantiated resource +can be linked to a [HTTPRoute][] resource using an [ExtensionRef][] filter. + +## Prerequisites + +### Install Envoy Gateway + +* Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +### Install Redis + +* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. +Lets install a Redis deployment in the `redis-system` namespce. + +```shell +cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com +; (1 server found) +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 +;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 +;; WARNING: recursion requested but not available + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +; COOKIE: 24fb86eba96ebf62 (echoed) +;; QUESTION SECTION: +;foo.bar.com. IN A + +;; ADDITIONAL SECTION: +foo.bar.com. 0 IN A 10.244.0.19 +_udp.foo.bar.com. 0 IN SRV 0 0 42376 . + +;; Query time: 1 msec +;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) +;; WHEN: Fri Jan 13 10:20:34 UTC 2023 +;; MSG SIZE rcvd: 114 +``` + +## Clean-Up + +Follow the steps from the [Quickstart Guide](../quickstart) to uninstall Envoy Gateway. + +Delete the CoreDNS example manifest and the UDPRoute: + +```shell +kubectl delete deploy/coredns +kubectl delete service/coredns +kubectl delete cm/coredns +kubectl delete udproute/coredns +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/v0.3.0/configuration/listeners/udp_filters/udp_proxy diff --git a/site/content/en/v0.4.0/_index.md b/site/content/en/v0.4.0/_index.md new file mode 100644 index 00000000000..53d07c8aae3 --- /dev/null +++ b/site/content/en/v0.4.0/_index.md @@ -0,0 +1,21 @@ ++++ +title = "Welcome to Envoy Gateway" +description = "Envoy Gateway Documents" +linktitle = "Documentation" + +[[cascade]] +type = "docs" ++++ + +{{% alert title="Note" color="primary" %}} + +This project is under **active** development. Many features are not complete. We would love for you to [Get Involved](contributions/)! + +{{% /alert %}} + +Envoy Gateway is an open source project for managing [Envoy Proxy](https://www.envoyproxy.io/) as a standalone or Kubernetes-based application +gateway. [Gateway API](https://gateway-api.sigs.k8s.io/) resources are used to dynamically provision and configure the managed Envoy Proxies. + +![architecture](/img/traffic.png) + +## Ready to get started? diff --git a/site/content/en/v0.4.0/api/_index.md b/site/content/en/v0.4.0/api/_index.md new file mode 100644 index 00000000000..396d9ffcefc --- /dev/null +++ b/site/content/en/v0.4.0/api/_index.md @@ -0,0 +1,5 @@ +--- +title: "API" +description: This section includes APIs of Envoy Gateway. +weight: 80 +--- diff --git a/site/content/en/v0.4.0/api/config_types.md b/site/content/en/v0.4.0/api/config_types.md new file mode 100644 index 00000000000..91f6b5fd532 --- /dev/null +++ b/site/content/en/v0.4.0/api/config_types.md @@ -0,0 +1,450 @@ +--- +title: "Config APIs" +--- + +## Packages +- [config.gateway.envoyproxy.io/v1alpha1](#configgatewayenvoyproxyiov1alpha1) + + +## config.gateway.envoyproxy.io/v1alpha1 + +Package v1alpha1 contains API schema definitions for the config.gateway.envoyproxy.io +API group. + + +### Resource Types +- [EnvoyGateway](#envoygateway) +- [EnvoyProxy](#envoyproxy) + + + +## EnvoyGateway + + + +EnvoyGateway is the schema for the envoygateways API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyGateway` +| `EnvoyGatewaySpec` _[EnvoyGatewaySpec](#envoygatewayspec)_ | EnvoyGatewaySpec defines the desired state of EnvoyGateway. | + + +## EnvoyGatewayFileProvider + + + +EnvoyGatewayFileProvider defines configuration for the File provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + + + +## EnvoyGatewayKubernetesProvider + + + +EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `rateLimitDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. If unspecified, default settings for the managed Envoy ratelimit deployment resource are applied. | + + +## EnvoyGatewayProvider + + + +EnvoyGatewayProvider defines the desired configuration of a provider. + +_Appears in:_ +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of provider to use. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider)_ | Kubernetes defines the configuration of the Kubernetes provider. Kubernetes provides runtime configuration via the Kubernetes API. | +| `file` _[EnvoyGatewayFileProvider](#envoygatewayfileprovider)_ | File defines the configuration of the File provider. File provides runtime configuration defined by one or more files. This type is not implemented until https://github.com/envoyproxy/gateway/issues/1001 is fixed. | + + +## EnvoyGatewaySpec + + + +EnvoyGatewaySpec defines the desired state of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) + +| Field | Description | +| --- | --- | +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extension` _[Extension](#extension)_ | Extension defines an extension to register for the Envoy Gateway Control Plane. | + + +## EnvoyProxy + + + +EnvoyProxy is the schema for the envoyproxies API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyProxy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyProxySpec](#envoyproxyspec)_ | EnvoyProxySpec defines the desired state of EnvoyProxy. | + + +## EnvoyProxyKubernetesProvider + + + +EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource provider. + +_Appears in:_ +- [EnvoyProxyProvider](#envoyproxyprovider) + +| Field | Description | +| --- | --- | +| `envoyDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | EnvoyDeployment defines the desired state of the Envoy deployment resource. If unspecified, default settings for the managed Envoy deployment resource are applied. | +| `envoyService` _[KubernetesServiceSpec](#kubernetesservicespec)_ | EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings for the managed Envoy service resource are applied. | + + +## EnvoyProxyProvider + + + +EnvoyProxyProvider defines the desired state of a resource provider. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of resource provider to use. A resource provider provides infrastructure resources for running the data plane, e.g. Envoy proxy, and optional auxiliary control planes. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider)_ | Kubernetes defines the desired state of the Kubernetes resource provider. Kubernetes provides infrastructure resources for running the data plane, e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings for managed Kubernetes resources are applied. | + + +## EnvoyProxySpec + + + +EnvoyProxySpec defines the desired state of EnvoyProxy. + +_Appears in:_ +- [EnvoyProxy](#envoyproxy) + +| Field | Description | +| --- | --- | +| `provider` _[EnvoyProxyProvider](#envoyproxyprovider)_ | Provider defines the desired resource provider and provider-specific configuration. If unspecified, the "Kubernetes" resource provider is used with default configuration parameters. | +| `logging` _[ProxyLogging](#proxylogging)_ | Logging defines logging parameters for managed proxies. If unspecified, default settings apply. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. | +| `bootstrap` _string_ | Bootstrap defines the Envoy Bootstrap as a YAML string. Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap to learn more about the syntax. If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration set by Envoy Gateway. Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources from it are not configurable and will result in the `EnvoyProxy` resource being rejected. Backward compatibility across minor versions is not guaranteed. We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. | + + + + +## Extension + + + +Extension defines the configuration for registering an extension to the Envoy Gateway control plane. + +_Appears in:_ +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `resources` _[GroupVersionKind](#groupversionkind) array_ | Resources defines the set of K8s resources the extension will handle. | +| `hooks` _[ExtensionHooks](#extensionhooks)_ | Hooks defines the set of hooks the extension supports | +| `service` _[ExtensionService](#extensionservice)_ | Service defines the configuration of the extension service that the Envoy Gateway Control Plane will call through extension hooks. | + + +## ExtensionHooks + + + +ExtensionHooks defines extension hooks across all supported runners + +_Appears in:_ +- [Extension](#extension) + +| Field | Description | +| --- | --- | +| `xdsTranslator` _[XDSTranslatorHooks](#xdstranslatorhooks)_ | XDSTranslator defines all the supported extension hooks for the xds-translator runner | + + +## ExtensionService + + + +ExtensionService defines the configuration for connecting to a registered extension service. + +_Appears in:_ +- [Extension](#extension) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `tls` _[ExtensionTLS](#extensiontls)_ | TLS defines TLS configuration for communication between Envoy Gateway and the extension service. | + + +## ExtensionTLS + + + +ExtensionTLS defines the TLS configuration when connecting to an extension service + +_Appears in:_ +- [ExtensionService](#extensionservice) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](#secretobjectreference)_ | CertificateRef contains a references to objects (Kubernetes objects or otherwise) that contains a TLS certificate and private keys. These certificates are used to establish a TLS handshake to the extension server. + CertificateRef can only reference a Kubernetes Secret at this time. | + + +## Gateway + + + +Gateway defines the desired Gateway API configuration of Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `controllerName` _string_ | ControllerName defines the name of the Gateway API controller. If unspecified, defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following for additional details: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass | + + +## GroupVersionKind + + + +GroupVersionKind unambiguously identifies a Kind. It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind + +_Appears in:_ +- [Extension](#extension) + +| Field | Description | +| --- | --- | +| `group` _string_ | | +| `version` _string_ | | +| `kind` _string_ | | + + +## KubernetesContainerSpec + + + +KubernetesContainerSpec defines the desired state of the Kubernetes container resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources required by this container. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | +| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#securitycontext-v1-core)_ | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| `image` _string_ | Image specifies the EnvoyProxy container image to be used, instead of the default image. | + + +## KubernetesDeploymentSpec + + + +KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `replicas` _integer_ | Replicas is the number of desired pods. Defaults to 1. | +| `pod` _[KubernetesPodSpec](#kubernetespodspec)_ | Pod defines the desired annotations and securityContext of container. | +| `container` _[KubernetesContainerSpec](#kubernetescontainerspec)_ | Container defines the resources and securityContext of container. | + + +## KubernetesPodSpec + + + +KubernetesPodSpec defines the desired state of the Kubernetes pod resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations are the annotations that should be appended to the pods. By default, no pod annotations are appended. | +| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)_ | SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. | + + +## KubernetesServiceSpec + + + +KubernetesServiceSpec defines the desired state of the Kubernetes service resource. + +_Appears in:_ +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations that should be appended to the service. By default, no annotations are appended. | +| `type` _[ServiceType](#servicetype)_ | Type determines how the Service is exposed. Defaults to LoadBalancer. Valid options are ClusterIP and LoadBalancer. "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. | + + +## LogComponent + +_Underlying type:_ `string` + +LogComponent defines a component that supports a configured logging level. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. + +_Appears in:_ +- [ProxyLogging](#proxylogging) + + + +## LogLevel + +_Underlying type:_ `string` + +LogLevel defines a log level for system logs. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. + +_Appears in:_ +- [ProxyLogging](#proxylogging) + + + +## ProviderType + +_Underlying type:_ `string` + +ProviderType defines the types of providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) +- [EnvoyProxyProvider](#envoyproxyprovider) + + + +## ProxyLogging + + + +ProxyLogging defines logging parameters for managed proxies. This type is not implemented until https://github.com/envoyproxy/gateway/issues/280 is fixed. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[LogComponent](#logcomponent), values:[LogLevel](#loglevel))_ | Level is a map of logging level per component, where the component is the key and the log level is the value. If unspecified, defaults to "System: Info". | + + +## RateLimit + + + +RateLimit defines the configuration associated with the Rate Limit Service used for Global Rate Limiting. + +_Appears in:_ +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `backend` _[RateLimitDatabaseBackend](#ratelimitdatabasebackend)_ | Backend holds the configuration associated with the database backend used by the rate limit service to store state associated with global ratelimiting. | + + +## RateLimitDatabaseBackend + + + +RateLimitDatabaseBackend defines the configuration associated with the database backend used by the rate limit service. + +_Appears in:_ +- [RateLimit](#ratelimit) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitDatabaseBackendType](#ratelimitdatabasebackendtype)_ | Type is the type of database backend to use. Supported types are: * Redis: Connects to a Redis database. | +| `redis` _[RateLimitRedisSettings](#ratelimitredissettings)_ | Redis defines the settings needed to connect to a Redis database. | + + +## RateLimitDatabaseBackendType + +_Underlying type:_ `string` + +RateLimitDatabaseBackendType specifies the types of database backend to be used by the rate limit service. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + + + +## RateLimitRedisSettings + + + +RateLimitRedisSettings defines the configuration for connecting to a Redis database. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + +| Field | Description | +| --- | --- | +| `url` _string_ | URL of the Redis Database. | + + +## ServiceType + +_Underlying type:_ `string` + +ServiceType string describes ingress methods for a service + +_Appears in:_ +- [KubernetesServiceSpec](#kubernetesservicespec) + + + +## XDSTranslatorHook + +_Underlying type:_ `string` + +XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support for the xds-translator + +_Appears in:_ +- [XDSTranslatorHooks](#xdstranslatorhooks) + + + +## XDSTranslatorHooks + + + +XDSTranslatorHooks contains all the pre and post hooks for the xds-translator runner. + +_Appears in:_ +- [ExtensionHooks](#extensionhooks) + +| Field | Description | +| --- | --- | +| `pre` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | +| `post` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | + + diff --git a/site/content/en/v0.4.0/api/extension_types.md b/site/content/en/v0.4.0/api/extension_types.md new file mode 100644 index 00000000000..9bc5172b250 --- /dev/null +++ b/site/content/en/v0.4.0/api/extension_types.md @@ -0,0 +1,231 @@ +--- +title: "Extension APIs" +--- + +## Packages +- [gateway.envoyproxy.io/v1alpha1](#gatewayenvoyproxyiov1alpha1) + + +## gateway.envoyproxy.io/v1alpha1 + +Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io API group. + + +### Resource Types +- [AuthenticationFilter](#authenticationfilter) +- [RateLimitFilter](#ratelimitfilter) + + + +## AuthenticationFilter + + + + + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `AuthenticationFilter` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[AuthenticationFilterSpec](#authenticationfilterspec)_ | Spec defines the desired state of the AuthenticationFilter type. | + + +## AuthenticationFilterSpec + + + +AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. + +_Appears in:_ +- [AuthenticationFilter](#authenticationfilter) + +| Field | Description | +| --- | --- | +| `type` _[AuthenticationFilterType](#authenticationfiltertype)_ | Type defines the type of authentication provider to use. Supported provider types are "JWT". | +| `jwtProviders` _[JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) array_ | JWT defines the JSON Web Token (JWT) authentication provider type. When multiple jwtProviders are specified, the JWT is considered valid if any of the providers successfully validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | + + +## AuthenticationFilterType + +_Underlying type:_ `string` + +AuthenticationFilterType is a type of authentication provider. + +_Appears in:_ +- [AuthenticationFilterSpec](#authenticationfilterspec) + + + +## GlobalRateLimit + + + +GlobalRateLimit defines global rate limit configuration. + +_Appears in:_ +- [RateLimitFilterSpec](#ratelimitfilterspec) + +| Field | Description | +| --- | --- | +| `rules` _[RateLimitRule](#ratelimitrule) array_ | Rules are a list of RateLimit selectors and limits. Each rule and its associated limit is applied in a mutually exclusive way i.e. if multiple rules get selected, each of their associated limits get applied, so a single traffic request might increase the rate limit counters for multiple rules if selected. | + + +## HeaderMatch + + + +HeaderMatch defines the match attributes within the HTTP Headers of the request. + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + +| Field | Description | +| --- | --- | +| `type` _[HeaderMatchType](#headermatchtype)_ | Type specifies how to match against the value of the header. | +| `name` _string_ | Name of the HTTP header. | +| `value` _string_ | Value within the HTTP header. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. Do not set this field when Type="Distinct", implying matching on any/all unique values within the header. | + + +## HeaderMatchType + +_Underlying type:_ `string` + +HeaderMatchType specifies the semantics of how HTTP header values should be compared. Valid HeaderMatchType values are "Exact", "RegularExpression", and "Distinct". + +_Appears in:_ +- [HeaderMatch](#headermatch) + + + +## JwtAuthenticationFilterProvider + + + +JwtAuthenticationFilterProvider defines the JSON Web Token (JWT) authentication provider type and how JWTs should be verified: + +_Appears in:_ +- [AuthenticationFilterSpec](#authenticationfilterspec) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines a unique name for the JWT provider. A name can have a variety of forms, including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. | +| `issuer` _string_ | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. | +| `audiences` _string array_ | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. | +| `remoteJWKS` _[RemoteJWKS](#remotejwks)_ | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. | + + +## RateLimitFilter + + + +RateLimitFilter allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `RateLimitFilter` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[RateLimitFilterSpec](#ratelimitfilterspec)_ | Spec defines the desired state of RateLimitFilter. | + + +## RateLimitFilterSpec + + + +RateLimitFilterSpec defines the desired state of RateLimitFilter. + +_Appears in:_ +- [RateLimitFilter](#ratelimitfilter) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitType](#ratelimittype)_ | Type decides the scope for the RateLimits. Valid RateLimitType values are "Global". | +| `global` _[GlobalRateLimit](#globalratelimit)_ | Global defines global rate limit configuration. | + + +## RateLimitRule + + + +RateLimitRule defines the semantics for matching attributes from the incoming requests, and setting limits for them. + +_Appears in:_ +- [GlobalRateLimit](#globalratelimit) + +| Field | Description | +| --- | --- | +| `clientSelectors` _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | ClientSelectors holds the list of select conditions to select specific clients using attributes from the traffic flow. All individual select conditions must hold True for this rule and its limit to be applied. If this field is empty, it is equivalent to True, and the limit is applied. | +| `limit` _[RateLimitValue](#ratelimitvalue)_ | Limit holds the rate limit values. This limit is applied for traffic flows when the selectors compute to True, causing the request to be counted towards the limit. The limit is enforced and the request is ratelimited, i.e. a response with 429 HTTP status code is sent back to the client when the selected requests have reached the limit. | + + +## RateLimitSelectCondition + + + +RateLimitSelectCondition specifies the attributes within the traffic flow that can be used to select a subset of clients to be ratelimited. All the individual conditions must hold True for the overall condition to hold True. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `headers` _[HeaderMatch](#headermatch) array_ | Headers is a list of request headers to match. Multiple header values are ANDed together, meaning, a request MUST match all the specified headers. | +| `sourceIP` _string_ | SourceIP is the IP CIDR that represents the range of Source IP Addresses of the client. These could also be the intermediate addresses through which the request has flown through and is part of the `X-Forwarded-For` header. For example, `192.168.0.1/32`, `192.168.0.0/24`, `001:db8::/64`. All IP Addresses within the specified SourceIP CIDR are treated as a single client selector and share the same rate limit bucket. | + + +## RateLimitType + +_Underlying type:_ `string` + +RateLimitType specifies the types of RateLimiting. + +_Appears in:_ +- [RateLimitFilterSpec](#ratelimitfilterspec) + + + +## RateLimitUnit + +_Underlying type:_ `string` + +RateLimitUnit specifies the intervals for setting rate limits. Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". + +_Appears in:_ +- [RateLimitValue](#ratelimitvalue) + + + +## RateLimitValue + + + +RateLimitValue defines the limits for rate limiting. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `requests` _integer_ | | +| `unit` _[RateLimitUnit](#ratelimitunit)_ | | + + +## RemoteJWKS + + + +RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. + +_Appears in:_ +- [JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) + +| Field | Description | +| --- | --- | +| `uri` _string_ | URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to validate the server certificate. | + + diff --git a/site/content/en/v0.4.0/contributions/CODEOWNERS.md b/site/content/en/v0.4.0/contributions/CODEOWNERS.md new file mode 100644 index 00000000000..63b751abde5 --- /dev/null +++ b/site/content/en/v0.4.0/contributions/CODEOWNERS.md @@ -0,0 +1,19 @@ +--- +title: "Maintainers" +description: "This section includes Maintainers of Envoy Gateway." +--- + +## The following maintainers, listed in alphabetical order, own everything + +- @AliceProxy +- @arkodg +- @Xunzhuo +- @zirain +- @qicz + +## Emeritus Maintainers + +- @danehans +- @alexgervais +- @skriss +- @youngnick diff --git a/site/content/en/v0.4.0/contributions/CODE_OF_CONDUCT.md b/site/content/en/v0.4.0/contributions/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..e19da050dff --- /dev/null +++ b/site/content/en/v0.4.0/contributions/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ +--- +title: "Code of Conduct" +description: "This section includes Code of Conduct of Envoy Gateway." +--- + +Envoy Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/site/content/en/v0.4.0/contributions/CONTRIBUTING.md b/site/content/en/v0.4.0/contributions/CONTRIBUTING.md new file mode 100644 index 00000000000..f94b2c940e9 --- /dev/null +++ b/site/content/en/v0.4.0/contributions/CONTRIBUTING.md @@ -0,0 +1,190 @@ +--- +title: "Contributing" +description: "This section tells how to contribute to Envoy Gateway." +weight: 3 +--- + +We welcome contributions from the community. Please carefully review the [project goals](/about) +and following guidelines to streamline your contributions. + +## Communication + +* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no + one else is working on it and ask you to open a GitHub issue. +* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or + changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to + agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process + for major features is also important so that [affiliations with commit access](../codeowners) can + come to agreement on the design. If it's appropriate to write a design document, the document must + be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable + location. +* Small patches and bug fixes don't need prior communication. + +## Inclusivity + +The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere +to the following guidelines for all code, APIs, and documentation: + +* The following words and phrases are not allowed: + * *Whitelist*: use allowlist instead. + * *Blacklist*: use denylist or blocklist instead. + * *Master*: use primary instead. + * *Slave*: use secondary or replica instead. +* Documentation should be written in an inclusive style. The [Google developer + documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent + reference on this topic. +* The above policy is not considered definitive and may be amended in the future as industry best + practices evolve. Additional comments on this topic may be provided by maintainers during code + review. + +## Submitting a PR + +* Fork the repo. +* Hack +* DCO sign-off each commit. This can be done with `git commit -s`. +* Submit your PR. +* Tests will automatically run for you. +* We will **not** merge any PR that is not passing tests. +* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage + build. If your PR cannot have 100% coverage for some reason please clearly explain why when you + open it. +* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/site) folder of the repo as + well as the [changelog](/blog/releases). +* All code comments and documentation are expected to have proper English grammar and punctuation. + If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try + to find some help but there are no guarantees. +* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary + and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. + Examples: + * "docs: fix grammar error" + * "feat(translator): add new feature" + * "fix: fix xx bug" + * "chore: change ci & build tools etc" +* Your PR commit message will be used as the commit message when your PR is merged. You should + update this field if your PR diverges during review. +* Your PR description should have details on what the PR does. If it fixes an existing issue it + should end with "Fixes #XXX". +* If your PR is co-authored or based on an earlier PR from another contributor, + please attribute them with `Co-authored-by: name `. See + GitHub's [multiple author + guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) + for further details. +* When all tests are passing and all other conditions described herein are satisfied, a maintainer + will be assigned to review and merge the PR. +* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits + are new commits and/or merges. We squash and merge so the number of commits you have in the PR + doesn't matter. +* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. + We reserve the right to close PRs that are not making progress. This is generally defined as no + changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. + Closing stale PRs helps us to keep on top of all the work currently in flight. + +## Maintainer PR Review Policy + +* See [CODEOWNERS.md](../codeowners) for the current list of maintainers. +* A maintainer representing a different affiliation from the PR owner is required to review and + approve the PR. +* When the project matures, it is expected that a "domain expert" for the code the PR touches should + review the PR. This person does not require commit access, just domain knowledge. +* The above rules may be waived for PRs which only update docs or comments, or trivial changes to + tests and tools (where trivial is decided by the maintainer in question). +* If there is a question on who should review a PR please discuss in Slack. +* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. +* Please make sure that the PR title, commit message, and description are updated if the PR changes + significantly during review. +* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge + title with the original title, and the commit body with every individual commit from the PR. + The maintainer doing the merge should make sure the title follows the guidelines above and should + overwrite the body with the original commit message from the PR (cleaning it up if necessary) + while preserving the PR author's final DCO sign-off. + +## Decision making + +This is a new and complex project, and we need to make a lot of decisions very quickly. +To this end, we've settled on this process for making (possibly contentious) decisions: + +* For decisions that need a record, we create an issue. +* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. +* Maintainers can cast binding votes on that comment by reacting or replying in another comment. +* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. +* Voting will be resolved by simple majority. +* In the event of deadlocks, the question will be put to steering instead. + +## DCO: Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign-off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +```bash +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` + +## Fixing DCO + +If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best +practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +the commit history to a single commit, append the DCO sign-off as described above, and [force +push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in +your history: + +```bash +git rebase -i HEAD^^ +(interactive squash + DCO append) +git push origin -f +``` + +Note, that in general rewriting history in this way is a hindrance to the review process and this +should only be done to correct a DCO mistake. diff --git a/site/content/en/v0.4.0/contributions/DEVELOP.md b/site/content/en/v0.4.0/contributions/DEVELOP.md new file mode 100644 index 00000000000..6f82c4a411f --- /dev/null +++ b/site/content/en/v0.4.0/contributions/DEVELOP.md @@ -0,0 +1,163 @@ +--- +title: "Developer Guide" +description: "This section tells how to develop Envoy Gateway." +weight: 2 +--- + +Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. + +## Prerequisites + +### go + +* Version: 1.20 +* Installation Guide: https://go.dev/doc/install + +### make + +* Recommended Version: 4.0 or later +* Installation Guide: https://www.gnu.org/software/make + +### docker + +* Optional when you want to build a Docker image or run `make` inside Docker. +* Recommended Version: 20.10.16 +* Installation Guide: https://docs.docker.com/engine/install + +### python3 + +* Need a `python3` program +* Must have a functioning `venv` module; this is part of the standard + library, but some distributions (such as Debian and Ubuntu) replace + it with a stub and require you to install a `python3-venv` package + separately. + +## Quickstart + +* Run `make help` to see all the available targets to build, test and run Envoy Gateway. + +### Building + +* Run `make build` to build all the binaries. +* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. +* Run `make build BINS="egctl"` to build the egctl binary. + +__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. + +### Testing + +* Run `make test` to run the golang tests. + +* Run `make testdata` to generate the golden YAML testdata files. + +### Running Linters + +* Run `make lint` to make sure your code passes all the linter checks. +__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). + +### Building and Pushing the Image + +* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. +* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. + +__Note:__ Replace `IMAGE` with your registry's image name. + +### Deploying Envoy Gateway for Test/Dev + +* Run `make create-cluster` to create a [Kind][] cluster. + +#### Option 1: Use the Latest [gateway-dev][] Image + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` + to use a different image tag. + +#### Option 2: Use a Custom Image + +* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. +* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. + +### Deploying Envoy Gateway in Kubernetes + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to + the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or + tag. +* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. + +__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. + +### Demo Setup + +* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource +(similar to steps outlined in the [Quickstart][] docs) and test the configuration. +* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. + +### Run Gateway API Conformance Tests + +The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the +Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run +`TAG=latest make run-conformance` to run the conformance tests. + +#### On a Linux Host + +* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] + image, and run Gateway API conformance tests. + +#### On a Mac Host + +Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following +workarounds to run conformance tests: + +* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run + `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image + to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to + uninstall Envoy Gateway. +* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. + +__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` +is unspecified, the short SHA of your current branch is used. + +### Debugging the Envoy Config + +An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port +(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. + +Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: + +```shell +export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward the admin interface port: + +```shell +kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 +``` + +Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. + +There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. + +### JWT Testing + +An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] +user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs +verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching +settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure +and is hosted in the repo. + +[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md +[make]: https://www.gnu.org/software/make/ +[Github Actions]: https://docs.github.com/en/actions +[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows +[Kind]: https://kind.sigs.k8s.io/ +[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ +[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ +[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ +[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags +[mac_connect]: https://github.com/chipmk/docker-mac-net-connect +[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[JWT Debugger]: https://jwt.io/ +[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/site/content/en/v0.4.0/contributions/DOCS.md b/site/content/en/v0.4.0/contributions/DOCS.md new file mode 100644 index 00000000000..8266c9e9a2b --- /dev/null +++ b/site/content/en/v0.4.0/contributions/DOCS.md @@ -0,0 +1,67 @@ +--- +title: "Working on Envoy Gateway Docs" +description: "This section tells the development of + Envoy Gateway Documents." +--- + +The documentation for the Envoy Gateway lives in the `docs/` directory. Any +individual document can be written using either [reStructuredText] or [Markdown], +you can choose the format that you're most comfortable with when working on the +documentation. + +## Documentation Structure + +We supported the versioned Docs now, the directory name under docs represents +the version of docs. The root of the latest site is in `docs/latest/index.rst`. +This is probably where to start if you're trying to understand how things fit together. + +Note that the new contents should be added to `docs/latest` and will be cut off at +the next release. The contents under `docs/v0.2.0` are auto-generated, +and usually do not need to make changes to them, unless if you find the current release pages have +some incorrect contents. If so, you should send a PR to update contents both of `docs/latest` +and `docs/v0.2.0`. + +It's important to note that a given document _must_ have a reference in some +`.. toctree::` section for the document to be reachable. Not everything needs +to be in `docs/index.rst`'s `toctree` though. + +You can access the website which represents the current release in default, +and you can access the website which contains the latest version changes in +[Here][latest-website] or at the footer of the pages. + +## Documentation Workflow + +To work with the docs, just edit reStructuredText or Markdown files in `docs`, +then run + +```bash +make docs +``` + +This will create `docs/html` with the built HTML pages. You can view the docs +either simply by pointing a web browser at the `file://` path to your +`docs/html`, or by firing up a static webserver from that directory, e.g. + +``` shell +make docs-serve +``` + +If you want to generate a new release version of the docs, like `v0.3.0`, then run + +```bash +make docs-release TAG=v0.3.0 +``` + +This will update the VERSION file at the project root, which records current release version, +and it will be used in the pages version context and binary version output. Also, this will generate +new dir `docs/v0.3.0`, which contains docs at v0.3.0 and updates artifact links to `v0.3.0` +in all files under `docs/v0.3.0/user`, like `quickstart.md`, `http-routing.md` and etc. + +## Publishing Docs + +Whenever docs are pushed to `main`, CI will publish the built docs to GitHub +Pages. For more details, see `.github/workflows/docs.yaml`. + +[reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html +[Markdown]: https://daringfireball.net/projects/markdown/syntax +[latest-website]: https://gateway.envoyproxy.io/latest diff --git a/site/content/en/v0.4.0/contributions/RELEASING.md b/site/content/en/v0.4.0/contributions/RELEASING.md new file mode 100644 index 00000000000..013707d4569 --- /dev/null +++ b/site/content/en/v0.4.0/contributions/RELEASING.md @@ -0,0 +1,231 @@ +--- +title: "Release Process" +description: "This section tells the release process of Envoy Gateway." +--- + +This document guides maintainers through the process of creating an Envoy Gateway release. + +- [Release Candidate](#release-candidate) +- [Minor Release](#minor-release) +- [Announce the Release](#announce-the-release) + +## Release Candidate + +The following steps should be used for creating a release candidate. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export RELEASE_CANDIDATE_NUMBER=1 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and + the [Build and Test][] has successfully completed. +5. Create a new release branch from `main`. The release branch should be named + `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. Push the branch to the Envoy Gateway repo. + + ```shell + git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] + for additional details on updating the image tag. +8. Sign, commit, and push your changes to your fork. +9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not + proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. +10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' + ``` + +11. Push the tag to the Envoy Gateway repository. + + ```shell + git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} + ``` + +12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +13. Confirm that the [release workflow][] completed successfully. +14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +15. Confirm that the [release][] was created. +16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test + the quickstart steps using the release candidate by manually updating the links. +17. [Generate][] the GitHub changelog. +18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. +19. If you find any bugs in this process, please create an issue. + +### Setup cherry picker action + +After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. + +Configuration looks like following: + +```yaml + cherry_pick_release_v0_4: + runs-on: ubuntu-latest + name: Cherry pick into release-v0.4 + if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cherry pick into release/v0.4 + uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 + with: + branch: release/v0.4 + title: "[release/v0.4] {old_title}" + body: "Cherry picking #{old_pull_request_id} onto release/v0.4" + labels: | + cherrypick/release-v0.4 + # put release manager here + reviewers: | + AliceProxy +``` + +Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. + +## Minor Release + +The following steps should be used for creating a minor release. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. +- A release branch that has been cut from the corresponding release candidate. Refer to the + [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. + + 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release + notes should be an accumulation of the release candidate release notes and any changes since the release + candidate. + 2. Create a release announcement. Refer to [PR #635] as an example release announcement. + 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. + 4. Generate the versioned release docs: + + ``` shell + make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged + and the [Build and Test][] has completed for your final PR. + +5. Checkout the release branch. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. If the tip of the release branch does not match the tip of `main`, perform the following: + + 1. Create a topic branch from the release branch. + 2. Cherry-pick the commits from `main` that differ from the release branch. + 3. Run tests locally, e.g. `make lint`. + 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. + 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. + 6. Do not proceed until the PR has merged and CI passes for the merged PR. + 7. If you are still on your topic branch, change to the release branch: + + ```shell + git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + + 8. Ensure your local release branch is up-to-date: + + ```shell + git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Tag the head of your release branch with the release tag. For example: + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' + ``` + + __Note:__ The tag version differs from the release branch by including the `.0` patch version. + +8. Push the tag to the Envoy Gateway repository. + + ```shell + git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +10. Confirm that the [release workflow][] completed successfully. +11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +12. Confirm that the [release][] was created. +13. Confirm that the steps in the [Quickstart Guide][] work as expected. +14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: + + ```console + # Release Announcement + + Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] + (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. + ``` + +If you find any bugs in this process, please create an issue. + +## Announce the Release + +It's important that the world knows about the release. Use the following steps to announce the release. + +1. Set the release information in the Envoy Gateway Slack channel. For example: + + ```shell + Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +2. Send a message to the Envoy Gateway Slack channel. For example: + + ```shell + On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway + v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. + Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs + to start using Envoy Gateway. + ... + ``` + + Link to the GitHub release and release announcement page that highlights the release. + +[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes +[Pull Request]: https://github.com/envoyproxy/gateway/pulls +[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md +[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml +[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml +[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml +[image]: https://hub.docker.com/r/envoyproxy/gateway/tags +[release]: https://github.com/envoyproxy/gateway/releases +[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +[PR #635]: https://github.com/envoyproxy/gateway/pull/635 +[PR #958]: https://github.com/envoyproxy/gateway/pull/958 +[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 +[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/site/content/en/v0.4.0/contributions/_index.md b/site/content/en/v0.4.0/contributions/_index.md new file mode 100644 index 00000000000..3255d996472 --- /dev/null +++ b/site/content/en/v0.4.0/contributions/_index.md @@ -0,0 +1,5 @@ +--- +title: Get Involved +description: "This section includes contents related to **Contributions**" +weight: 100 +--- diff --git a/site/content/en/v0.4.0/contributions/roadmap.md b/site/content/en/v0.4.0/contributions/roadmap.md new file mode 100644 index 00000000000..356cec270ac --- /dev/null +++ b/site/content/en/v0.4.0/contributions/roadmap.md @@ -0,0 +1,92 @@ +--- +title: "Roadmap" +weight: -1 +description: "This section records the roadmap of Envoy Gateway." +--- + +This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of +the project. + +## Contributing to the Roadmap + +- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use + case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update + this document accordingly. +- To help with an existing roadmap item, comment on or assign yourself to the associated issue. +- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A + maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be + discussed in an issue or a community meeting before implementing it. + +If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. +Look for issues with the `help wanted` label to get started. + +## Details + +Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific +roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by +contributing to the project. + +`Last Updated: April 2023` + +### [v0.2.0][v0.2.0]: Establish a Solid Foundation + +- Complete the core Envoy Gateway implementation- [Issue #60][60]. +- Establish initial testing, e2e, integration, etc- [Issue #64][64]. +- Establish user and developer project documentation- [Issue #17][17]. +- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. +- Setup a CI/CD pipeline- [Issue #63][63]. + +### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms + +- Support extended Gateway API fields [Issue #707][707]. +- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. +- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. +- Rate Limiting [Issue #670][670]. +- Authentication [Issue #336][336]. + +### [v0.4.0][v0.4.0]: Customizing Envoy Gateway + +- Extending Envoy Gateway control plane [Issue #20][20] +- Helm based installation for Envoy Gateway [Issue #650][650] +- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] +- Configuring xDS Bootstrap [Issue #31][31] + +### [v0.5.0][v0.5.0]: Observability and Scale + +- Observability for control plane and data plane [Issue #701][701]. +- Compute and document Envoy Gateway performance [Issue #1365][1365]. +- Allow users to configure xDS Resources [Issue #24][24]. + +### [v0.6.0][v0.6.0]: Preparation for GA + +- Envoy Gateway meets readiness criteria [Issue #1160][1160]. + +[issue]: https://github.com/envoyproxy/gateway/issues +[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing +[pr]: https://github.com/envoyproxy/gateway/compare +[milestones]: https://github.com/envoyproxy/gateway/milestones +[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 +[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 +[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 +[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 +[v0.6.0]: https://github.com/envoyproxy/gateway/milestone/15 +[17]: https://github.com/envoyproxy/gateway/issues/17 +[20]: https://github.com/envoyproxy/gateway/issues/20 +[24]: https://github.com/envoyproxy/gateway/issues/24 +[31]: https://github.com/envoyproxy/gateway/issues/31 +[60]: https://github.com/envoyproxy/gateway/issues/60 +[63]: https://github.com/envoyproxy/gateway/issues/63 +[64]: https://github.com/envoyproxy/gateway/issues/64 +[65]: https://github.com/envoyproxy/gateway/issues/65 +[336]: https://github.com/envoyproxy/gateway/issues/336 +[641]: https://github.com/envoyproxy/gateway/issues/641 +[642]: https://github.com/envoyproxy/gateway/issues/642 +[648]: https://github.com/envoyproxy/gateway/issues/648 +[650]: https://github.com/envoyproxy/gateway/issues/650 +[643]: https://github.com/envoyproxy/gateway/issues/643 +[670]: https://github.com/envoyproxy/gateway/issues/670 +[675]: https://github.com/envoyproxy/gateway/issues/675 +[701]: https://github.com/envoyproxy/gateway/issues/701 +[707]: https://github.com/envoyproxy/gateway/issues/707 +[1160]: https://github.com/envoyproxy/gateway/issues/1160 +[1365]: https://github.com/envoyproxy/gateway/issues/1365 diff --git a/site/content/en/v0.4.0/design/_index.md b/site/content/en/v0.4.0/design/_index.md new file mode 100644 index 00000000000..21650809f7d --- /dev/null +++ b/site/content/en/v0.4.0/design/_index.md @@ -0,0 +1,6 @@ +--- +title: "Design" +weight: 1 +description: This section includes Designs of Envoy Gateway. +--- + diff --git a/site/content/en/v0.4.0/design/bootstrap.md b/site/content/en/v0.4.0/design/bootstrap.md new file mode 100644 index 00000000000..9a8f0c789ef --- /dev/null +++ b/site/content/en/v0.4.0/design/bootstrap.md @@ -0,0 +1,381 @@ +--- +title: "Bootstrap Design" +--- + +## Overview + +[Issue 31][] specifies the need for allowing advanced users to specify their custom +Envoy Bootstrap configuration rather than using the default Bootstrap configuration +defined in Envoy Gateway. This allows advanced users to extend Envoy Gateway and +support their custom use cases such setting up tracing and stats configuration +that is not supported by Envoy Gateway. + +## Goals + +* Define an API field to allow a user to specify a custom Bootstrap +* Provide tooling to allow the user to generate the default Bootstrap configuration + as well as validate their custom Bootstrap. + +## Non Goals + +* Allow user to configure only a section of the Bootstrap + +## API + +Leverage the existing [EnvoyProxy][] resource which can be attached to the [GatewayClass][] using +the [parametersRef][] field, and define a `Bootstrap` field within the resource. If this field is set, +the value is used as the Bootstrap configuration for all managed Envoy Proxies created by Envoy Gateway. + +```go +// EnvoyProxySpec defines the desired state of EnvoyProxy. +type EnvoyProxySpec struct { + ...... + // Bootstrap defines the Envoy Bootstrap as a YAML string. + // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + // to learn more about the syntax. + // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration + // set by Envoy Gateway. + // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources + // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. + // Backward compatibility across minor versions is not guaranteed. + // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default + // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. + // + // +optional + Bootstrap *string `json:"bootstrap,omitempty"` +} +``` + +## Tooling + +A CLI tool `egctl x translate` will be provided to the user to help generate a working Bootstrap configuration. +Here is an example where a user inputs a `GatewayClass` and the CLI generates the `EnvoyProxy` resource with the `Bootstrap` field populated. + +``` +cat < /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: {} +EOF +``` + +This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. + +The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: + foo: bar +EOF +``` + +__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for +illustration purposes only. + +The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the +File provider: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: File + file: + foo: bar +EOF +``` + +__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration +purposes only. + +Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use +default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to +manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: foo +``` + +With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: + +```shell +$ ./envoy-gateway +``` + +## Data Plane API + +The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. +Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through +`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure +configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: + +* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane + infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. +* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: + > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the + > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are + > not propagated down to existing Gateways. + +The initial `EnvoyProxy` API: + +```go +// gateway/api/config/v1alpha1/envoyproxy.go + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyProxy is the Schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvoyProxySpec `json:"spec,omitempty"` + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure +// configuration. +type EnvoyProxySpec struct { + // Undefined by this design spec. +} + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. +type EnvoyProxyStatus struct { + // Undefined by this design spec. +} +``` + +The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use +cases are better understood. + +### Data Plane Configuration + +GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is +running with the Kubernetes provider. + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration +parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening +on port 80. + +The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer +service: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + name: example-config + group: config.gateway.envoyproxy.io + kind: EnvoyProxy +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: example-config +spec: + networkPublishing: + type: ClusterIPService +``` + +__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. + +[issue_51]: https://github.com/envoyproxy/gateway/issues/51 +[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md +[gw_api]: https://gateway-api.sigs.k8s.io/ +[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/site/content/en/v0.4.0/design/egctl.md b/site/content/en/v0.4.0/design/egctl.md new file mode 100644 index 00000000000..0f67d99f100 --- /dev/null +++ b/site/content/en/v0.4.0/design/egctl.md @@ -0,0 +1,59 @@ +--- +title: "egctl Design" +--- + +## Motivation + +EG should provide a command line tool with following capabilities: + +- Collect configuration from envoy proxy and gateway +- Analyse system configuration to diagnose any issues in envoy gateway + +This tool is named `egctl`. + +## Syntax + +Use the following syntax to run `egctl` commands from your terminal window: + +```console +egctl [command] [entity] [name] [flags] +``` + +where `command`, `name`, and `flags` are: + +* `command`: Specifies the operation that you want to perform on one or more resources, + for example `config`, `version`. + +* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. + +* `name`: Specifies the name of the specified instance. + +* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. + +If you need help, run `egctl help` from the terminal window. + +## Operation + +The following table includes short descriptions and the general syntax for all the `egctl` operations: + +| Operation | Syntax | Description | +| --------------| -------------------------------- | -------------------------------------------------------------------------------------| +| `version` | `egctl version` | Prints out build version information. | +| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | +| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | +| `experimental`| `egctl experimental` | Subcommand for experimental features. These do not guarantee backwards compatibility | + +## Examples + +Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: + +```console +# Retrieve all information about proxy configuration from envoy +egctl config envoy-proxy all + +# Retrieve listener information about proxy configuration from envoy +egctl config envoy-proxy listener + +# Retrieve information about envoy gateway +egctl config envoy-gateway +``` diff --git a/site/content/en/v0.4.0/design/extending-envoy-gateway.md b/site/content/en/v0.4.0/design/extending-envoy-gateway.md new file mode 100644 index 00000000000..fd840848990 --- /dev/null +++ b/site/content/en/v0.4.0/design/extending-envoy-gateway.md @@ -0,0 +1,326 @@ +--- +title: "Envoy Gateway Extensions Design" +--- + +As outlined in the [official goals][] for the Envoy Gateway project, one of the main goals is to "provide a common foundation for vendors to build value-added products +without having to re-engineer fundamental interactions". Development of the Envoy Gateway project has been focused on developing the core features for the project and +Kubernetes Gateway API conformance. This system focuses on the “common foundation for vendors” component by introducing a way for vendors to extend Envoy Gateway. + +To meaningfully extend Envoy Gateway and provide additional features, Extensions need to be able to introduce their own custom resources and have a high level of control +over the configuration generated by Envoy Gateway. Simply applying some static xDS configuration patches or relying on the existing Gateway API resources are both insufficient on their own +as means to add larger features that require dynamic user-configuration. + +As an example, an extension developer may wish to provide their own out-of-the-box authentication filters that require configuration from the end-user. This is a scenario where the ability to introduce +custom resources and attach them to [HTTPRoute][]s as an [ExtensionRef][] is necessary. Providing the same feature through a series of xDS patch resources would be too cumbersome for many end-users that want to avoid +that level of complexity when managing their clusters. + +## Goals + +- Provide a foundation for extending the Envoy Gateway control plane +- Allow Extension Developers to introduce their own custom resources for extending the Gateway-API via [ExtensionRefs][], [policyAttachments][] (future) and [backendRefs][] (future). +- Extension developers should **NOT** have to maintain a custom fork of Envoy Gateway +- Provide a system for extending Envoy Gateway which allows extension projects to ship updates independent of Envoy Gateway's release schedule +- Modify the generated Envoy xDS config +- Setup a foundation for the initial iteration of Extending Envoy Gateway +- Allow an Extension to hook into the infra manager pipeline (future) + +## Non-Goals + +- The initial design does not capture every hook that Envoy Gateway will eventually support. +- Extend [Gateway API Policy Attachments][]. At some point, these will be addressed using this extension system, but the initial implementation omits these. +- Support multiple extensions at the same time. Due to the fact that extensions will be modifying xDS resources after they are generated, handling the order of extension execution for each individual hook point is a challenge. Additionally, there is no +real way to prevent one extension from overwriting or breaking modifications to xDS resources that were made by another extension that was executed first. + +## Overview + +Envoy Gateway can be extended by vendors by means of an extension server developed by the vendor and deployed alongside Envoy Gateway. +An extension server can make use of one or more pre/post hooks inside Envoy Gateway before and after its major components (translator, etc.) to allow the extension to modify the data going into or coming out of these components. +An extension can be created external to Envoy Gateway as its own Kubernetes deployment or loaded as a sidecar. gRPC is used for the calls between Envoy Gateway and an extension. In the hook call, Envoy Gateway sends data as well +as context information to the extension and expects a reply with a modified version of the data that was sent to the extension. Since extensions fundamentally alter the logic and data that Envoy Gateway provides, Extension projects assume responsibility for any bugs and issues +they create as a direct result of their modification of Envoy Gateway. + +## Diagram + +![Architecture](/img/extension-example.png) + +## Registering Extensions in Envoy Gateway + +Information about the extension that Envoy Gateway needs to load is configured in the Envoy Gateway config. + +An example configuration: + +```yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +extension: + resources: + - group: example.myextension.io + version: v2 + kind: OAuth2Filter + hooks: + post: + - Route + - VirtualHost + - HTTPListener + - Translation + service: + host: my-extension.example + port: 443 + tls: + certificateRef: + name: my-secret + namespace: default +``` + +An extension must supply connection information in the `extension.service` field so that Envoy Gateway can communicate with the extension. The `tls` configuration is optional. + +If the extension wants Envoy Gateway to watch resources for it then the extension must configure the optional `extension.resources` field and supply a list of: + +- `group`: the API group of the resource +- `version`: the API version of the resource +- `kind`: the Kind of resource + +The extension can configure the `extension.hooks` field to specify which hook points it would like to support. If a given hook is not listed here then it will not be executed even +if the extension is configured properly. This allows extension developers to only opt-in to the hook points they want to make use of. + +This configuration is required to be provided at bootstrap and modifying the registered extension during runtime is not currently supported. +Envoy Gateway will keep track of the registered extension and its API `groups` and `kinds` when processing Gateway API resources. + +## Extending Gateway API and the Data Plane + +Envoy Gateway manages [Envoy][] deployments, which act as the data plane that handles actual user traffic. Users configure the data plane using the K8s Gateway API resources which Envoy +Gateway converts into [Envoy specific configuration (xDS)][] to send over to Envoy. + +Gateway API offers [ExtensionRef filters][] and [Policy Attachments][] as extension points for implementers to use. Envoy Gateway extends the Gateway API using these extension points to provide support for [rate limiting][] +and [authentication][] native to the project. The initial design of Envoy Gateway extensions will primarily focus on ExtensionRef filters so that extension developers can reference their own resources as HTTP Filters in the same way +that Envoy Gateway has native support for rate limiting and authentication filters. + +When Envoy Gateway encounters an [HTTPRoute][] or [GRPCRoute][] that has an `ExtensionRef` `filter` with a `group` and `kind` that Envoy Gateway does not support, it will first +check the registered extension to determine if it supports the referenced object before considering it a configuration error. + +This allows users to be able to reference additional filters provided by their Envoy Gateway Extension, in their `HTTPRoute`s / `GRPCRoute`s: + +```yaml +apiVersion: example.myextension.io/v1alpha1 +kind: OAuth2Filter +metadata: + name: oauth2-filter +spec: + ... + +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - clientSelectors: + - path: + type: PathPrefix + value: / + filters: + - type: ExtensionRef + extensionRef: + group: example.myextension.io + kind: OAuth2Filter + name: oauth2-filter + backendRefs: + - name: backend + port: 3000 +``` + +In order to enable the usage of new resources introduced by an extension for translation and xDS modification, Envoy Gateway provides hook points within the translation pipeline, where it calls out to the extension service registered in the [EnvoyGateway config][] +if they specify an `group` that matches the `group` of an `ExtensionRef` filter. The extension will then be able to modify the xDS that Envoy Gateway generated and send back the +modified configuration. If an extension is not registered or if the registered extension does not specify support for the `group` of an `ExtensionRef` filter then Envoy Gateway will treat it as an unknown resource +and provide an error to the user. + +**Note:** Currently (as of [v1beta1][]) Gateway API does not provide a means to specify the namespace or version of an object referenced as an `ExtensionRef`. The extension mechanism will assume that +the namespace of any `ExtensionRef` is the same as the namespace of the `HTTPRoute` or `GRPCRoute` it is attached to rather than treating the `name` field of an `ExtensionRef` as a `name.namespace` string. +If Gateway API adds support for these fields then the design of the Envoy Gateway extensions will be updated to support them. + +## Watching New Resources + +Envoy Gateway will dynamically create new watches on resources introduced by the registered Extension. It does so by using the [controller-runtime][] to create new watches on [Unstructured][] resources that match the `version`s, `group`s, and `kind`s that the +registered extension configured. When communicating with an extension, Envoy Gateway sends these Unstructured resources over to the extension. This eliminates the need for the extension to create its own watches which would have a strong chance of creating race conditions and reconciliation loops when resources change. When an extension receives the Unstructured resources from Envoy Gateway it can perform its own type validation on them. Currently we make the simplifying assumption that the registered extension's `Kinds` are filters referenced by `extensionRef` in `HTTPRouteFilter`s . Support for Policy attachments will be introduced at a later time. + +## xDS Hooks API + +Envoy Gateway supports the following hooks as the initial foundation of the Extension system. Additional hooks can be developed using this extension system at a later point as new use-cases and needs are discovered. The primary iteration of the extension hooks +focuses solely on the modification of xDS resources. + +### Route Modification Hook + +The [Route][] level Hook provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized. +Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the +Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to ext_authz filters. +The Post Route Modify hook also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that created this xDS route +This hook is always executed when an extension is loaded that has added `Route` to the `EnvoyProxy.extensions.hooks.post`, and only on Routes which were generated from an HTTPRoute that uses extension resources as externalRef filters. + +```go +// PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified +message PostRouteModifyRequest { + envoy.config.route.v3.Route route = 1; + PostRouteExtensionContext post_route_context = 2; +} + +// RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway +// additional context information can be added to this message as more use-cases are discovered +message PostRouteExtensionContext { + // Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute + repeated ExtensionResource extension_resources = 1; + + // hostnames are the fully qualified domain names attached to the HTTPRoute + repeated string hostnames = 2; +} + +// ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter +// extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension +// can marshal the bytes from this resource back into an unstructured.Unstructured and then +// perform type checking to obtain the resource. +message ExtensionResource { + bytes unstructured_bytes = 1; +} + +// PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent +// If an extension returns a nil Route then it will not be modified +message PostRouteModifyResponse { + envoy.config.route.v3.Route route = 1; +} +``` + +### VirtualHost Modification Hook + +The [VirtualHost][] Hook provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized. +An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway. +This hook is always executed when an extension is loaded that has added `VirtualHost` to the `EnvoyProxy.extensions.hooks.post`. +An extension may return nil to not make any changes to the VirtualHost. + +```protobuf +// PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified +message PostVirtualHostModifyRequest { + envoy.config.route.v3.VirtualHost virtual_host = 1; + PostVirtualHostExtensionContext post_virtual_host_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostVirtualHostExtensionContext {} + +// PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent +// If an extension returns a nil Virtual Host then it will not be modified +message PostVirtualHostModifyResponse { + envoy.config.route.v3.VirtualHost virtual_host = 1; +} +``` + +### HTTP Listener Modification Hook + +The HTTP [Listener][] modification hook is the broadest xDS modification Hook available and allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized. +This hook is always executed when an extension is loaded that has added `HTTPListener` to the `EnvoyProxy.extensions.hooks.post`. An extension may return nil +in order to not make any changes to the Listener. + +```protobuf +// PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified +message PostHTTPListenerModifyRequest { + envoy.config.listener.v3.Listener listener = 1; + PostHTTPListenerExtensionContext post_listener_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostHTTPListenerExtensionContext {} + +// PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent +// If an extension returns a nil Listener then it will not be modified +message PostHTTPListenerModifyResponse { + envoy.config.listener.v3.Listener listener = 1; +} +``` + +### Post xDS Translation Modify Hook + +The Post Translate Modify hook allows an extension to modify the clusters and secrets in the xDS config. +This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than +using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed. +An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension. +The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets +This hook is always executed when an extension is loaded that has added `Translation` to the `EnvoyProxy.extensions.hooks.post`. + +```protobuf +// PostTranslateModifyRequest currently sends only clusters and secrets to an extension. +// The extension is free to add/modify/remove the resources it received. +message PostTranslateModifyRequest { + PostTranslateExtensionContext post_translate_context = 1; + repeated envoy.config.cluster.v3.Cluster clusters = 2; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3; +} + +// PostTranslateModifyResponse is the expected response from an extension and contains +// the full list of xDS clusters and secrets to be used for the xDS config. +message PostTranslateModifyResponse { + repeated envoy.config.cluster.v3.Cluster clusters = 1; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 2; +} +``` + +### Extension Service + +Currently, an extension must implement all of the following hooks although it may return the input(s) it received +if no modification of the resource is desired. A future expansion of the extension hooks will allow an Extension to specify +with config which Hooks it would like to "subscribe" to and which Hooks it does not wish to support. These specific Hooks were chosen +in order to provide extensions with the ability to have both broad and specific control over xDS resources and to minimize the amount of data being sent. + +```protobuf +service EnvoyGatewayExtension { + rpc PostRouteModify (PostRouteModifyRequest) returns (PostRouteModifyResponse) {}; + rpc PostVirtualHostModify(PostVirtualHostModifyRequest) returns (PostVirtualHostModifyResponse) {}; + rpc PostHTTPListenerModify(PostHTTPListenerModifyRequest) returns (PostHTTPListenerModifyResponse) {}; + rpc PostTranslateModify(PostTranslateModifyRequest) returns (PostTranslateModifyResponse) {}; +} +``` + +## Design Decisions + +- Envoy Gateway watches new custom resources introduced by a loaded extension and passes the resources back to the extension when they are used. + - This decision was made to solve the problem about how resources introduced by an extension get watched. If an extension server watches its own resources then it would need some way to trigger an Envoy Gateway reconfigure when a resource that Envoy Gateway is not watching gets updated. Having Envoy Gateway watch all resources removes any concern about creating race confitions or reconcile loops that would result from Envoy Gateway and the extension server both having so much separate state that needs to be synchronized. +- The Extension Server takes ownership of producing the correct xDS configuration in the hook responses +- The Extension Server will be responsible for ensuring the performance of the hook processing time +- The Post xDS level gRPC hooks all currently send a context field even though it contains nothing for several hooks. These fields exist so that they can be updadated in the future to pass +additional information to extensions as new use-cases and needs are discovered. +- The initial design supplies the scaffolding for both "pre xDS" and "post xDS" hooks. Only the post hooks are currently implemented which operate on xDS resources after they have been generated. +The pre hooks will be implemented at a later date along with one or more hooks in the infra manager. The infra manager level hook(s) will exist to power use-cases such as dynamically creating Deployments/Services for the extension the +whenever Envoy Gateway creates an instance of Envoy Proxy. An extension developer might want to take advantage of this functionality to inject a new authorization service as a sidecar on the Envoy Proxy deployment for reduced latency. +- Multiple extensions are not be supported at the same time. Preventing conflict between multiple extensions that are mangling xDS resources is too difficult to ensure compatibility with and is likely to only generate issues. + +## Known Challenges + +Extending Envoy Gateway by using an external extension server which makes use of hook points in Envoy Gateway does comes with a few trade-offs. One known trade-off is the impact of the time that it takes for the hook calls to be executed. Since an extension would make use of hook points in Envoy Gateway that use gRPC for communication, the time it takes to perform these requests could become a concern for some extension developers. One way to minimize the request time of the hook calls is to load the extension server as a sidecar to Envoy Gateway to minimize the impact of networking on the hook calls. + +[official goals]: https://github.com/envoyproxy/gateway/blob/main/GOALS.md#extensibility +[ExtensionRef filters]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference +[ExtensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference +[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendObjectReference +[Gateway API Policy attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Policy Attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[policyAttachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Envoy]: https://www.envoyproxy.io/ +[Envoy specific configuration (xDS)]: https://www.envoyproxy.io/docs/envoy/v1.25.1/configuration/configuration +[v1beta1]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1 +[rate limiting]: https://gateway.envoyproxy.io/v0.3.0/user/rate-limit.html +[authentication]: https://gateway.envoyproxy.io/v0.3.0/user/authn.html +[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[EnvoyGateway config]: https://gateway.envoyproxy.io/v0.3.0/api/config_types.html#envoygateway +[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime +[Unstructured]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured +[Listener]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener +[VirtualHost]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost +[Route]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-route diff --git a/site/content/en/v0.4.0/design/gatewayapi-translator.md b/site/content/en/v0.4.0/design/gatewayapi-translator.md new file mode 100644 index 00000000000..b83ed82588d --- /dev/null +++ b/site/content/en/v0.4.0/design/gatewayapi-translator.md @@ -0,0 +1,253 @@ +--- +title: "Gateway API Translator Design" +weight: 4 +--- + +The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate +Representation (IR). + +## Assumptions + +Initially target core conformance features only, to be followed by extended conformance features. + +## Inputs and Outputs + +The main inputs to the Gateway API translator are: + +- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. + +__Note:__ ReferenceGrant is not fully implemented as of v0.2. + +The outputs of the Gateway API translator are: + +- Xds and Infra Internal Representations (IRs). +- Status updates for GatewayClass, Gateways, HTTPRoutes + +## Listener Compatibility + +Envoy Gateway follows Gateway API listener compatibility spec: +> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. + +__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. + +### Listener Compatibility Examples + +#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +## Computing Status + +Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway +must compute the appropriate status fields and conditions for managed resources. + +Status is computed and set for: + +- The managed GatewayClass (`gatewayclass.status.conditions`). +- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the + Envoy Deployment and Service status are also included to calculate Gateway status. +- Listeners for each Gateway (`gateway.status.listeners`). +- The ParentRef for each Route (`route.status.parents`). + +The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to +the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and +updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to +update resource status on the Kubernetes API server. + +## Outline + +The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway +API resources. + +1. Process Gateway Listeners + - Validate unique hostnames, ports, and protocols. + - Validate and compute supported kinds. + - Validate allowed namespaces (validate selector if specified). + - Validate TLS fields if specified, including resolving referenced Secrets. + +2. Process HTTPRoutes + - foreach route rule: + - compute matches + - [core] path exact, path prefix + - [core] header exact + - [extended] query param exact + - [extended] HTTP method + - compute filters + - [core] request header modifier (set/add/remove) + - [core] request redirect (hostname, statuscode) + - [extended] request mirror + - compute backends + - [core] Kubernetes services + - foreach route parent ref: + - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) + - foreach matching listener: + - foreach hostname intersection with route: + - add each computed route rule to host + +## Context Structs + +To help store, access and manipulate information as it's processed during the translation process, a set of context +structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support +processing. + +`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. + +```go +type GatewayContext struct { + // The managed Gateway + *v1beta1.Gateway + + // A list of Gateway ListenerContexts. + listeners []*ListenerContext +} +``` + +`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on +the associated Gateway. + +```go +type ListenerContext struct { + // The Gateway listener. + *v1beta1.Listener + + // The Gateway this Listener belongs to. + gateway *v1beta1.Gateway + + // An index used for managing this listener in the list of Gateway listeners. + listenerStatusIdx int + + // Only Routes in namespaces selected by the selector may be attached + // to the Gateway this listener belongs to. + namespaceSelector labels.Selector + + // The TLS Secret for this Listener, if applicable. + tlsSecret *v1.Secret +} +``` + +`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. + +```go +type RouteContext interface { + client.Object + + // GetRouteType returns the Kind of the Route object, HTTPRoute, + // TLSRoute, TCPRoute, UDPRoute etc. + GetRouteType() string + + // GetHostnames returns the hosts targeted by the Route object. + GetHostnames() []string + + // GetParentReferences returns the ParentReference of the Route object. + GetParentReferences() []v1beta1.ParentReference + + // GetRouteParentContext returns RouteParentContext by using the Route + // objects' ParentReference. + GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext +} +``` + +[message bus]: watching.md diff --git a/site/content/en/v0.4.0/design/goals.md b/site/content/en/v0.4.0/design/goals.md new file mode 100644 index 00000000000..fd38b2004c6 --- /dev/null +++ b/site/content/en/v0.4.0/design/goals.md @@ -0,0 +1,91 @@ +--- +title: "Goals" +weight: 1 +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption +through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing +use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer +fundamental interactions. + +## Objectives + +### Expressive API + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +### Batteries included + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +### All environments + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API][]. +Additional goals include multi-cluster support and various runtime environments. + +### Extensibility + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +## Non-objectives + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +## Personas + +_In order of priority_ + +### 1. Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +### 2. Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/v0.4.0/design/rate-limit.md b/site/content/en/v0.4.0/design/rate-limit.md new file mode 100644 index 00000000000..e6e0656c819 --- /dev/null +++ b/site/content/en/v0.4.0/design/rate-limit.md @@ -0,0 +1,348 @@ +--- +title: "Rate Limit Design" +--- + +## Overview + +Rate limit is a feature that allows the user to limit the number of incoming requests +to a predefined value based on attributes within the traffic flow. + +Here are some reasons why a user may want to implements Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +## Scope Types + +The rate limit type here describes the scope of rate limits. + +* Global - In this case, the rate limit is common across all the instances of Envoy proxies +where its applied i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is +10 requests/second, this limit is common and will be hit if 5 requests pass through the first replica +and 5 requests pass through the second replica within the same second. + +* Local - In this case, the rate limits are specific to each instance/replica of Envoy running. +Note - This is not part of the initial design and will be added as a future enhancement. + +## Match Types + +### Rate limit a specific traffic flow + +* Here is an example of a ratelimit implemented by the application developer to limit a specific user +by matching on a custom `x-user-id` header with a value set to `one` + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-specific-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-specific-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit all traffic flows + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route to safeguard health of internal application components. In this case, no specific `headers` match +is specified, and the rate limit is applied to all traffic flows accepted by this `HTTPRoute`. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-all-requests +spec: + type: Global + global: + rules: + - limit: + requests: 1000 + unit: Second +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-requests + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per distinct value + +* Here is an example of a rate limit implemented by the application developer to limit any unique user +by matching on a custom `x-user-id` header. Here, user A (recognised from the traffic flow using the header +`x-user-id` and value `a`) will be rate limited at 10 requests/hour and so will user B +(recognised from the traffic flow using the header `x-user-id` and value `b`). + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-per-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per source IP + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on source IP. In this case, requests from `x.x.x.x` will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-per-ip +spec: + type: Global + global: + rules: + - clientSelectors: + - sourceIP: x.x.x.x/32 + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +## Multiple RateLimitFilters, rules and clientSelectors +* Users can create multiple `RateLimitFilter`s and apply it to the same `HTTPRoute`. In such a case each +`RateLimitFilter` will be applied to the route and matched (and limited) in a mutually exclusive way, independent of each other. +* Rate limits are applied for each `RateLimitFilter` `rule` when ALL the conditions under `clientSelectors` hold true. + +Here's an example highlighting this - + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-all-safeguard-app +spec: + type: Global + global: + rules: + - limit: + requests: 100 + unit: Second +--- + +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-per-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 1000 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-safeguard-app + backendRefs: + - name: backend + port: 3000 +``` + +* The user has created two `RateLimitFilter`s and has attached it to a `HTTPRoute` - one(`ratelimit-all-safeguard-app`) to +ensure that the backend does not get overwhelmed with requests, any excess requests are rate limited irrespective of +the attributes within the traffic flow, and another(`ratelimit-per-user`) to rate limit each distinct user client +who can be differentiated using the `x-user-id` header, to ensure that each client does not make exessive requests to the backend. +* If user `baz` (identified with the header and value of `x-user-id: baz`) sends 90 requests within the first second, and +user `bar` sends 11 more requests during that same interval of 1 second, and user `bar` sends the 101th request within that second, +the rule defined in `ratelimit-all-safeguard-app` gets activated and Envoy Gateway will ratelimit the request sent by `bar` (and any other +request sent within that 1 second). After 1 second, the rate limit counter associated with the `ratelimit-all-safeguard-app` rule +is reset and again evaluated. +* If user `bar` also ends up sending 90 more requests within the hour, summing up `bar`'s total request count to 101, the rate limit rule +defined within `ratelimit-per-user` will get activated, and `bar`'s requests will be rate limited again until the hour interval ends. +* Within the same above hour, if `baz` sends 991 more requests, summing up `baz`'s total request count to 1001, the rate limit rule defined +within `ratelimit-per-user` will get activated for `baz`, and `baz`'s requests will also be rate limited until the hour interval ends. + +## Design Decisions + +* The initial design uses an Extension filter to apply the Rate Limit functionality on a specific [HTTPRoute][]. +This was preferred over the [PolicyAttachment][] extension mechanism, because it is unclear whether Rate Limit +will be required to be enforced or overridden by the platform administrator or not. +* The RateLimitFilter can only be applied as a filter to a [HTTPRouteRule[], applying it across all backends within a [HTTPRoute][] +and cannot be applied a filter within a [HTTPBackendRef][] for a specific backend. +* The [HTTPRoute][] API has a [matches][] field within each [rule][] to select a specific traffic flow to be routed to +the destination backend. The RateLimitFilter API that can be attached to an HTTPRoute via an [extensionRef][] filter, +also has a `clientSelectors` field within each `rule` to select attributes within the traffic flow to rate limit specific clients. +The two levels of selectors/matches allow for flexibility and aim to hold match information specific to its use, allowing the author/owner +of each configuration to be different. It also allows the `clientSelectors` field within the RateLimitFilter to be enhanced with other matchable +attribute such as [IP subnet][] in the future that are not relevant in the [HTTPRoute][] API. + +## Implementation Details + +### Global Rate limiting + +* [Global rate limiting][] in Envoy Proxy can be achieved using the following - + * [Actions][] can be configured per [xDS Route][]. + * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] + defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined + using a [rate limit filter][]. + * Based on information received by the rate limit service and its programmed configuration, a decision is computed, whether to rate limit + the HTTP Request or not, and is sent back to Envoy, which enforces this decision on the data plane. +* Envoy Gateway will leverage this Envoy Proxy feature by - + * Translating the user facing RateLimitFilter API into Rate limit [Actions][] as well as Rate limit service configuration to implement + the desired API intent. + * Envoy Gateway will use the existing [reference implementation][] of the rate limit service. + * The Infrastructure administrator will need to enable the rate limit service using new settings that will be defined in the [EnvoyGateway][] config API. + * The xDS IR will be enhanced to hold the user facing rate limit intent. + * The xDS Translator will be enhanced to translate the rate limit field within the xDS IR into Rate limit [Actions][] as well as instantiate the [rate limit filter][]. + * A new runner called `rate-limit` will be added that subscribes to the xDS IR messages and translates it into a new Rate Limit Infra IR which contains + the [rate limit service configuration][] as well as other information needed to deploy the rate limit service. + * The infrastructure service will be enhanced to subscribe to the Rate Limit Infra IR and deploy a provider specific rate limit service runnable entity. + * A Status field within the RateLimitFilter API will be added to reflect whether the specific configuration was programmed correctly in these multiple locations or not. + +[PolicyAttachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPBackendRef +[matches]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch +[rule]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch +[extensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType +[IP subnet]: https://en.wikipedia.org/wiki/Subnetwork +[Actions]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit-action +[descriptors]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter.html?highlight=descriptor#example-1 +[Global rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[xDS Route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-routeaction +[rate limit filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[rate limit service]: https://www.envoyproxy.io/docs/envoy/latest/configuration/other_features/rate_limit#config-rate-limit-service +[reference implementation]: https://github.com/envoyproxy/ratelimit +[EnvoyGateway]: https://github.com/envoyproxy/gateway/blob/main/api/config/v1alpha1/envoygateway_types.go +[rate limit service configuration]: https://github.com/envoyproxy/ratelimit#configuration diff --git a/site/content/en/v0.4.0/design/request-authentication.md b/site/content/en/v0.4.0/design/request-authentication.md new file mode 100644 index 00000000000..82682bf2a0b --- /dev/null +++ b/site/content/en/v0.4.0/design/request-authentication.md @@ -0,0 +1,515 @@ +--- +title: "Request Authentication Design" +--- + +## Overview + +[Issue 336][] specifies the need for exposing a user-facing API to configure request authentication. Request +authentication is defined as an authentication mechanism to be enforced by Envoy on a per-request basis. A connection +will be rejected if it contains invalid authentication information, based on the `AuthenticationFilter` API type +proposed in this design document. + +Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and +implementation-specific API [support levels][] for implementers such as Envoy Gateway to expose features. Since +implementing request authentication is not covered by `Core` or `Extended` APIs, an `Implementation-specific` API will +be created for this purpose. + +## Goals + +* Define an API for configuring request authentication. +* Implement [JWT] as the first supported authentication type. +* Allow users that manage routes, e.g. [HTTPRoute][], to authenticate matching requests before forwarding to a backend + service. +* Support HTTPRoutes as an Authentication API referent. HTTPRoute provides multiple [extension points][]. The + [HTTPRouteFilter][] is the extension point supported by the Authentication API. + +## Non-Goals + +* Allow infrastructure administrators to override or establish default authentication policies. +* Support referents other than HTTPRoute. +* Support Gateway API extension points other than HTTPRouteFilter. + +## Use-Cases + +These use-cases are presented as an aid for how users may attempt to utilize the outputs of the design. They are not an +exhaustive list of features for authentication support in Envoy Gateway. + +As a Service Producer, I need the ability to: +* Authenticate a request before forwarding it to a backend service. +* Have different authentication mechanisms per route rule. +* Choose from different authentication mechanisms supported by Envoy, e.g. OIDC. + +### Authentication API Type + +The Authentication API type defines authentication configuration for authenticating requests through managed Envoy +proxies. + +```go +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +) + +type AuthenticationFilter struct { + metav1.TypeMeta + metav1.ObjectMeta + + // Spec defines the desired state of the Authentication type. + Spec AuthenticationFilterSpec + + // Note: The status sub-resource has been excluded but may be added in the future. +} + +// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. +// +union +type AuthenticationFilterSpec struct { + // Type defines the type of authentication provider to use. Supported provider types are: + // + // * JWT: A provider that uses JSON Web Token (JWT) for authenticating requests. + // + // +unionDiscriminator + Type AuthenticationFilterType + + // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple + // jwtProviders are specified, the JWT is considered valid if any of the providers + // successfully validate the JWT. + JwtProviders []JwtAuthenticationFilterProvider +} + +... +``` + +Refer to [PR 773][] for the detailed AuthenticationFilter API spec. + +The status subresource is not included in the AuthenticationFilter API. Status will be surfaced by an HTTPRoute that +references an AuthenticationFilter. For example, an HTTPRoute will surface the `ResolvedRefs=False` status condition if it +references an AuthenticationFilter that does not exist. It may be beneficial to add AuthenticationFilter status fields in the future +based on defined use-cases. For example, a remote [JWKS][] can be validated based on the specified URI and have an +appropriate status condition surfaced. + +#### AuthenticationFilter Example + +The following is an AuthenticationFilter example with one JWT authentication provider: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example +spec: + type: JWT + jwtProviders: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJwks: + uri: https://foo.com/jwt/public-key/jwks.json + +``` + +__Note:__ `type` is a union type, allowing only one of any supported provider type such as `jwtProviders` to be +specified. + +The following is an example HTTPRoute configured to use the above JWT authentication provider: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example + backendRefs: + - name: backend + port: 3000 +``` + +Requests for `www.example.com/foo` will be authenticated using the referenced JWT provider before being forwarded to the +backend service named "backend". + +## Implementation Details + +The JWT authentication type is translated to an Envoy [JWT authentication filter][] and a cluster is created for each +remote [JWKS][]. The following examples provide additional details on how Gateway API and AuthenticationFilter resources are +translated into Envoy configuration. + +### Example 1: One Route with One JWT Provider + +The following cluster is created from the above HTTPRoute and AuthenticationFilter: + +```yaml +dynamic_clusters: + - name: foo.com|443 + load_assignment: + cluster_name: foo.com|443 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: foo.com + port_value: 443 + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: foo.com + common_tls_context: + validation_context: + match_subject_alt_names: + - exact: "*.foo.com" + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +``` + +A JWT authentication HTTP filter is added to the HTTP Connection Manager. For example: + +```yaml +dynamic_resources: + dynamic_listeners: + - name: example_listener + address: + socket_address: + address: 1.2.3.4 + port_value: 80 + filter_chains: + - filters: + - name: envoy.http_connection_manager + http_filters: + - name: envoy.filters.http.jwt_authn + typed_config: + "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication +``` + +This JWT authentication HTTP filter contains two fields: +* The `providers` field specifies how a JWT should be verified, such as where to extract the token, where to fetch the + public key ([JWKS][]) and where to output its payload. This field is built from the source resource `namespace-name`, and + the JWT provider name of an AuthenticationFilter. +* The `rules` field specifies matching rules and their requirements. If a request matches a rule, its requirement + applies. The requirement specifies which JWT providers should be used. This field is built from a HTTPRoute + `matches` rule that references the AuthenticationFilter. When a referenced Authentication specifies multiple + `jwtProviders`, the JWT is considered valid if __any__ of the providers successfully validate the JWT. + +The following JWT authentication HTTP filter `providers` configuration is created from the above AuthenticationFilter. + +```yaml +providers: + example: + issuer: https://www.example.com + audiences: + - foo.com + remote_jwks: + http_uri: + uri: https://foo.com/jwt/public-key/jwks.json + cluster: example_jwks_cluster + timeout: 1s +``` + +The following JWT authentication HTTP filter `rules` configuration is created from the above HTTPRoute. + +```yaml +rules: + - match: + prefix: /foo + requires: + provider_name: default-example-example +``` + +### Example 2: Two HTTPRoutes with Different AuthenticationFilters + +The following example contains: +* Two HTTPRoutes with different hostnames. +* Each HTTPRoute references a different AuthenticationFilter. +* Each AuthenticationFilter contains a different JWT provider. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example1 +spec: + type: JWT + jwtProviders: + - name: example1 + issuer: https://www.example1.com + audiences: + - foo.com + remoteJwks: + uri: https://foo.com/jwt/public-key/jwks.json +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example2 +spec: + type: JWT + jwtProviders: + - name: example2 + issuer: https://www.example2.com + audiences: + - bar.com + remoteJwks: + uri: https://bar.com/jwt/public-key/jwks.json +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example1 +spec: + hostnames: + - www.example1.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example1 + backendRefs: + - name: backend + port: 3000 +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example2 +spec: + hostnames: + - www.example2.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - matches: + - path: + type: PathPrefix + value: /bar + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example2 + backendRefs: + - name: backend2 + port: 3000 +``` + +The following xDS configuration is created from the above example resources: + +```yaml +configs: +... +dynamic_listeners: + - name: default-eg-http + ... + default_filter_chain: + filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + '@type': >- + type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: http + rds: + config_source: + ... + route_config_name: default-eg-http + http_filters: + - name: envoy.filters.http.jwt_authn + typed_config: + '@type': >- + type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication + providers: + default-example1-example1: + issuer: https://www.example1.com + audiences: + - foo.com + remote_jwks: + http_uri: + uri: https://foo.com/jwt/public-key/jwks.json + cluster: default-example1-example1-jwt + default-example2-example2: + issuer: https://www.example2.com + audiences: + - bar.com + remote_jwks: + http_uri: + uri: https://bar.com/jwt/public-key/jwks.json + cluster: default-example2-example2-jwt + rules: + - match: + exact: /foo + requires: + provider_name: default-example1-example1 + - match: + exact: /bar + requires: + provider_name: default-example2-example2 + - name: envoy.filters.http.router + typed_config: + '@type': >- + type.googleapis.com/envoy.extensions.filters.http.router.v3.Router +dynamic_route_configs: + - route_config: + '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration + name: default-eg-http + virtual_hosts: + - name: default-eg-http + domains: + - '*' + routes: + - match: + prefix: /foo + headers: + - name: ':authority' + string_match: + exact: www.example1.com + route: + cluster: default-backend-rule-0-match-0-www.example1.com + - match: + prefix: /bar + headers: + - name: ':authority' + string_match: + exact: www.example2.com + route: + cluster: default-backend2-rule-0-match-0-www.example2.com +dynamic_active_clusters: + - cluster: + name: default-backend-rule-0-match-0-www.example.com + ... + endpoints: + - locality: {} + lb_endpoints: + - endpoint: + address: + socket_address: + address: $BACKEND_SERVICE1_IP + port_value: 3000 + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + name: default-backend-rule-1-match-0-www.example.com + ... + endpoints: + - locality: {} + lb_endpoints: + - endpoint: + address: + socket_address: + address: $BACKEND_SERVICE2_IP + port_value: 3000 +... +``` + +__Note:__ The JWT provider cluster and route is omitted from the above example for brevity. + +### Implementation Outline + +* Update the Kubernetes provider to get/watch AuthenticationFilter resources that are referenced by managed HTTPRoutes. + Add the referenced AuthenticationFilter object to the resource map and publish it. +* Update the resource translator to include the AuthenticationFilter API in HTTPRoute processing. +* Update the xDS translator to translate an AuthenticationFilter into xDS resources. The translator should perform the + following: + * Convert a list of JWT rules from the xds IR into an Envoy JWT filter config. + * Create a JWT authentication HTTP filter. + * Build the HTTP Connection Manager (HCM) HTTP filters. + * Build the HCM. + * When building the Listener, create an HCM for each filter-chain. + +## Adding Authentication Types + +Additional authentication types can be added in the future through the `AuthenticationFilterType` API. For +example, to add the `Foo` authentication type: + +Define the `Foo` authentication provider: + +```go +package v1alpha1 + +// FooAuthenticationFilterProvider defines the "Foo" authentication filter provider type. +type FooAuthenticationFilterProvider struct { + // TODO: Define fields of the Foo authentication filter provider type. +} +``` + +Add the `FooAuthenticationFilterProvider` type to `AuthenticationFilterSpec`: + +```go +package v1alpha1 + +type AuthenticationFilterSpec struct { + ... + + // Foo defines the Foo authentication type. For additional + // details, see: + // + // + // + // +optional + Foo *FooAuthenticationFilterProvider +} +``` + +Lastly, add the type to the `AuthenticationType` enum: + +```go +// AuthenticationType is a type of authentication provider. +// +kubebuilder:validation:Enum=JWT,FOO +type AuthenticationFilterType string + +const ( + // JwtAuthenticationProviderType is the JWT authentication provider type. + FooAuthenticationFilterProviderType AuthenticationFilterType = "FOO" +) +``` + +The AuthenticationFilter API should support additional authentication types in the future, for example: +- OAuth2 +- OIDC + +## Outstanding Questions + +- If Envoy Gateway owns the AuthenticationFilter API, is an xDS IR equivalent needed? +- Should local [JWKS][] be implemented before remote [JWKS][]? +- How should Envoy obtain the trusted CA for a remote [JWKS][]? +- Should HTTPS be the only supported scheme for remote [JWKS][]? +- Should OR'ing JWT providers be supported? +- Should Authentication provide status? +- Are the API field validation rules acceptable? + +[Issue 336]: https://github.com/envoyproxy/gateway/issues/336 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels +[JWT]: https://jwt.io/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[extension points]: https://gateway-api.sigs.k8s.io/concepts/api-overview/?h=extension#extension-points +[HTTPRouteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter +[JWKS]: https://www.rfc-editor.org/rfc/rfc7517 +[JWT authentication filter]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn +[PR 773]: https://github.com/envoyproxy/gateway/pull/733 diff --git a/site/content/en/v0.4.0/design/system-design.md b/site/content/en/v0.4.0/design/system-design.md new file mode 100644 index 00000000000..16123948ee7 --- /dev/null +++ b/site/content/en/v0.4.0/design/system-design.md @@ -0,0 +1,174 @@ +--- +title: "System Design" +weight: 2 +--- + +## Goals + +* Define the system components needed to satisfy the requirements of Envoy Gateway. + +## Non-Goals + +* Create a detailed design and interface specification for each system component. + +## Terminology + +* Control Plane- A collection of inter-related software components for providing application gateway and routing + functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. + These services are detailed in the [components](#components) section. +* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. + +## Architecture + +![Architecture](/img/architecture.png) + +## Configuration + +Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][gw_api] objects. + +### Static Configuration + +Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, +configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the +configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. + +### Dynamic Configuration + +Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using +reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is +defined as Kubernetes resources that provide the following services: + +* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is + expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be + referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, + e.g. expose Envoy network endpoints using a `ClusterIP` service instead of a `LoadBalancer` service. +* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP + requests for "www.example.com" to a backend service running a web server. This configuration is expressed through + [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. + Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] + reference. + +## Components + +Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described +in the [Watching Components Design][wcd]. + +### Provider + +A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve +services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap +via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A +provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). + +#### Kubernetes Provider + +* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the + [dynamic configuration](#dynamic-configuration). +* Manages the data plane through Kubernetes API CRUD operations. +* Uses Kubernetes for Service discovery. +* Uses etcd (via Kubernetes API) to persist data. + +#### File Provider + +* Uses a file watcher to watch files in a directory that define the data plane configuration. +* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. +* Uses the host's DNS for Service discovery. +* If needed, the local filesystem is used to persist data. + +### Resource Watcher + +The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The +mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes +provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator +as output. + +### Resource Translator + +The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate +Representation (IR). It is responsible for: + +* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. +* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. + +__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. + +### Intermediate Representation (IR) + +The Intermediate Representation defines internal data models that external resources are translated into. This allows +Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR +used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. + +* Infra IR- Used as the internal definition of the managed data plane infrastructure. +* xDS IR- Used as the internal definition of the managed data plane xDS configuration. + +### xDS Translator + +The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. + +### xDS Server + +The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server +Protocol and is responsible for using xDS to configure the data plane. + +### Infra Manager + +The Infra Manager is a provider-specific component responsible for managing the following infrastructure: + +* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, + Service, etc. resources to run Envoy in a Kubernetes cluster. +* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require + external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning + and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to + users through the [Custom Route Filters][crf] extension. + +The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. + +## Design Decisions + +* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with + `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy + Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. + `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy + infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. +* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. + * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. + * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners + in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are + met: + * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies + either the “HTTPS” or “TLS” Protocol. + * Each Listener within the group specifies a unique "Hostname". + * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no + other Listener matches. + * Envoy Gateway does __not__ merge listeners across multiple Gateways. +* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. + * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. +* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. + * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. +* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. + +The draft for this document is [here][draft_design]. + +[gw_api]: https://gateway-api.sigs.k8s.io +[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass +[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway +[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute +[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute +[go_cp]: https://github.com/envoyproxy/go-control-plane +[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[rls]: https://github.com/envoyproxy/ratelimit +[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional +[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts +[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners +[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route +[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional +[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster +[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference +[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ +[ wcd ]: ./watching.md +[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 +[roadmap]: roadmap.md diff --git a/site/content/en/v0.4.0/design/tcp-udp-design.md b/site/content/en/v0.4.0/design/tcp-udp-design.md new file mode 100644 index 00000000000..f517e24feda --- /dev/null +++ b/site/content/en/v0.4.0/design/tcp-udp-design.md @@ -0,0 +1,49 @@ +--- +title: "TCP and UDP Proxy Design " +--- + +Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP +and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the +design decision. + +Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) + and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) +, so ideally, Envoy Gateway should also be able to work in these two modes: + +## Non-transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the +TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when +proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see Envoy's IP address and port. + +## Transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies +the TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address +when proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see the original downstream IP address and Envoy's mac address. + +Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward +the port number. + +## The Implications of Transparent Proxy Mode + +### Escalated Privilege +Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated +CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. + +### Routing +The upstream can see the original source IP, but the original port number won't be passed, so the return +traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back +to the right port number of the downstream, which requires routing at the upstream side to be set up. +In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. + +## The Design Decision (For Now) + +The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and +port of the deployed Envoy instance instead of the client. diff --git a/site/content/en/v0.4.0/design/watching.md b/site/content/en/v0.4.0/design/watching.md new file mode 100644 index 00000000000..72a955043e0 --- /dev/null +++ b/site/content/en/v0.4.0/design/watching.md @@ -0,0 +1,120 @@ +--- +title: "Watching Components Design" +weight: 3 +--- + +Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch +external resources, and "publish" what they see for other components to consume; others watch what another publishes and +act on it (such as the resource translator watches what the providers publish, and then publishes its own results that +are watched by another component). Some of these internally published results are consumed by multiple components. + +To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the +standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub +pattern. + +## Pub + +Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" +tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if +we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by +the IR translator: + + ```go + type ResourceTable struct { + // gateway classes are cluster-scoped; no namespace + GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] + + // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. + Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] + + HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] + } + ``` + +The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; +updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` +method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher +doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just +`.Store` its data and `watchable` will do the right thing. + +## Sub + +Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or +`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: + + ```go + func(ctx context.Context) error { + for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { + fullState := irInput{ + GatewayClasses: k8sTable.GatewayClasses.LoadAll(), + Gateways: k8sTable.Gateways.LoadAll(), + HTTPRoutes: snapshot.State, + } + translate(irInput) + } + } + ``` + +Or, to watch multiple maps in the same loop: + + ```go + func worker(ctx context.Context) error { + classCh := k8sTable.GatewayClasses.Subscribe(ctx) + gwCh := k8sTable.Gateways.Subscribe(ctx) + routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) + for ctx.Err() == nil { + var arg irInput + select { + case snapshot := <-classCh: + arg.GatewayClasses = snapshot.State + case snapshot := <-gwCh: + arg.Gateways = snapshot.State + case snapshot := <-routeCh: + arg.Routes = snapshot.State + } + if arg.GateWayClasses == nil { + arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() + } + if arg.GateWays == nil { + arg.Gateways = k8sTable.GateWays.LoadAll() + } + if arg.HTTPRoutes == nil { + arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() + } + translate(irInput) + } + } + ``` + +From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; +but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a +handy way to know when to run, `.Load` and friends can be used without subscribing. + +There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but +it's probably wise to just have one publisher for each map. + +The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when +`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple +mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in +to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of +each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need +to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. + +If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` +entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you +must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to +help with this. + +## Other Notes + +The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the +map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb +`chan`. + +A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types +be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and +so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can +generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate +methods for. + +[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/site/content/en/v0.4.0/user/_index.md b/site/content/en/v0.4.0/user/_index.md new file mode 100644 index 00000000000..2f23014d867 --- /dev/null +++ b/site/content/en/v0.4.0/user/_index.md @@ -0,0 +1,6 @@ +--- +title: "User Guides" +weight: 2 +description: This section includes User Guides of Envoy Gateway. +--- + diff --git a/site/content/en/v0.4.0/user/authn.md b/site/content/en/v0.4.0/user/authn.md new file mode 100644 index 00000000000..9f25623bdf7 --- /dev/null +++ b/site/content/en/v0.4.0/user/authn.md @@ -0,0 +1,96 @@ +--- +title: "Request Authentication" +--- + +This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks +if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only +supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. + +## Installation + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Configuration + +Allow requests with a valid JWT by creating an [AuthenticationFilter][] and referencing it from the example HTTPRoute. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.4.0/examples/kubernetes/authn/jwt.yaml +``` + +The HTTPRoute is now updated to authenticate requests for `/foo` and allow unauthenticated requests to `/bar`. The +`/foo` route rule references an AuthenticationFilter that provides the JWT authentication configuration. + +Verify the HTTPRoute configuration and status: + +```shell +kubectl get httproute/backend -o yaml +``` + +The AuthenticationFilter is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the AuthenticationFilter configuration: + +```shell +kubectl get authenticationfilter/jwt-example -o yaml +``` + +## Testing + +Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](../quickstart) guide is set. If not, follow the +Quickstart instructions to set the variable. + +```shell +echo $GATEWAY_HOST +``` + +Verify that requests to `/foo` are denied without a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `401` HTTP response code should be returned. + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `/foo` with a valid JWT is allowed: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `200` HTTP response code should be returned. + +Verify that requests to `/bar` are allowed __without__ a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar +``` + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the AuthenticationFilter: + +```shell +kubectl delete authenticationfilter/jwt-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[jwt]: https://tools.ietf.org/html/rfc7519 +[AuthenticationFilter]: https://gateway.envoyproxy.io/v0.4.0/api/extension_types.html#authenticationfilter +[jwks]: https://tools.ietf.org/html/rfc7517 diff --git a/site/content/en/v0.4.0/user/customize-envoyproxy.md b/site/content/en/v0.4.0/user/customize-envoyproxy.md new file mode 100644 index 00000000000..0f2b92f2dab --- /dev/null +++ b/site/content/en/v0.4.0/user/customize-envoyproxy.md @@ -0,0 +1,253 @@ +--- +title: "Customize EnvoyProxy" +--- + +Envoy Gateway provides a [EnvoyProxy][] CRD that can be linked to the ParametersRef +in GatewayClass y cluster admins to customize the managed EnvoyProxy Deployment and +Service. To learn more about GatewayClass and ParametersRef, please refer to [Gateway API documentation][]. + +## Installation + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Add GatewayClass ParametersRef + +First, you need to add ParametersRef in GatewayClass, and refer to EnvoyProxy Config: + +```shell +cat < GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8888 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:19:42 GMT +< content-length: 521 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.marketing.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.157" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "c637977c-458a-48ae-92b3-f8c429849322" + ] + }, + "namespace": "marketing", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-bcs8f" +* Connection #0 to host localhost left intact +``` + +* Lets deploy Envoy Gateway in the `product` namespace + +``` +helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.4.0 -n product --create-namespace +``` + +Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. + +```shell +cat < GET /get HTTP/1.1 +> Host: www.product.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:20:17 GMT +< content-length: 517 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.product.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.156" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39196453-2250-4331-b756-54003b2853c2" + ] + }, + "namespace": "product", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-64fjs" +* Connection #0 to host localhost left intact +``` + +With the below command you can ensure that you are no able to access the marketing team's backend exposed using the `www.marketing.example.com` hostname +and the product team's data plane. + +```shell +curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get +``` + +``` +* Trying 127.0.0.1:8889... +* Connected to localhost (127.0.0.1) port 8889 (#0) +> GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 404 Not Found +< date: Thu, 20 Apr 2023 19:22:13 GMT +< server: envoy +< content-length: 0 +< +* Connection #0 to host localhost left intact +``` diff --git a/site/content/en/v0.4.0/user/egctl.md b/site/content/en/v0.4.0/user/egctl.md new file mode 100644 index 00000000000..29f0200f896 --- /dev/null +++ b/site/content/en/v0.4.0/user/egctl.md @@ -0,0 +1,806 @@ +--- +title: "Use egctl" +--- + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + +## Installing egctl + +This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. + +### From The Envoy Gateway Project + +The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. + +### From the Binary Releases + +Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. + +1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) +2. Unpack it (tar -zxvf egctl_v0.4.0_linux_amd64.tar.gz) +3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) + +From there, you should be able to run: `egctl help`. + +### From Script + +`egctl` now has an installer script that will automatically grab the v0.4.0 release version of egctl and install it locally. + +You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. + +```shell +curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh + +chmod +x get-egctl.sh + +# get help info of the +bash get-egctl.sh --help + +# install the v0.4.0 development version of egctl +bash VERSION=v0.4.0 get-egctl.sh +``` + +Yes, you can just use the below command if you want to live on the edge. + +```shell +curl https://gateway.envoyproxy.io/get-egctl.sh | VERSION=v0.4.0 bash +``` + +## egctl experimental translate + +This subcommand allows users to translate from an input configuration type to an output configuration type. + +In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS +resources. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something", + "foo" + ], +... +``` + +## Setting Request Headers + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + "headers": { + "Accept": [ + "*/*" + ], + "Set-Header": [ + "foo" + ], +... +``` + +## Removing Request Headers + +Headers can be removed from a request by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something" + ], +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: X-Foo: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< x-foo: value1 +< add-header: foo +< +... + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "X-Foo: value1" + ] +... +``` + +## Setting Response Headers + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: set-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< set-header: foo +< + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "set-header": value1" + ] +... +``` + +## Removing Response Headers + +Headers can be removed from a response by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: remove-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "remove-header": value1" + ] +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +## Multiple backendRefs + +If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is +configured. + +First, create a second instance of the example app from the quickstart: + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-75bcd4c969-lsxpz" +... +``` + +## Weighted backendRefs + +If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` +field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is +assumed to be `1`. + +The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of +requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the +HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. + +The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the +backend-2 service. + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< server: envoy +< content-length: 0 +< +``` + +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/site/content/en/v0.4.0/user/http-urlrewrite.md b/site/content/en/v0.4.0/user/http-urlrewrite.md new file mode 100644 index 00000000000..60c0bebeeef --- /dev/null +++ b/site/content/en/v0.4.0/user/http-urlrewrite.md @@ -0,0 +1,297 @@ +--- +title: "HTTP URL Rewrite" +--- + +[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be +used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Rewrite URL Prefix Path + +You can configure to rewrite the prefix in the url like below. In this example, any curls to +`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. + +```shell +cat < GET /get/origin/path HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> + +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:03:28 GMT +< content-length: 503 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/replace/origin/path", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "fd84b842-9937-4fb5-83c7-61470d854b90" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. + +## Rewrite URL Full Path + +You can configure to rewrite the fullpath in the url like below. In this example, any request sent to +`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to +`http://${GATEWAY_HOST}/force/replace/fullpath`. + +```shell +cat < GET /get/origin/path/extra HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:09:31 GMT +< content-length: 512 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/force/replace/fullpath", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path/extra" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "8ab774d6-9ffa-4faa-abbb-f45b0db00895" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is +`/force/replace/fullpath`. + +## Rewrite Host Name + +You can configure to rewrite the hostname like below. In this example, any requests sent to +`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. + +```shell +cat < GET /get HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:15:15 GMT +< content-length: 481 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "envoygateway.io", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Forwarded-Host": [ + "path.rewrite.example" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39aa447c-97b9-45a3-a675-9fb266ab1af0" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. + +[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPURLRewriteFilter diff --git a/site/content/en/v0.4.0/user/quickstart.md b/site/content/en/v0.4.0/user/quickstart.md new file mode 100644 index 00000000000..ef4df466c1a --- /dev/null +++ b/site/content/en/v0.4.0/user/quickstart.md @@ -0,0 +1,100 @@ +--- +title: "Quickstart" +weight: 1 +--- + +This guide will help you get started with Envoy Gateway in a few simple steps. + +## Prerequisites + +A Kubernetes cluster. + +__Note:__ Refer to the [Compatibility Matrix](/blog/2022/10/01/versions/) for supported Kubernetes versions. + +## Installation + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.4.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.4.0/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/v0.4.0/quickstart.yaml + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` + +### External LoadBalancer Support + +You can also test the same functionality by sending traffic to the External IP. To get the external IP of the +Envoy service, run: + +```shell +export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace +`ip` in the above command with `hostname`. + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get +``` + +## Clean-Up + +Use the steps in this section to uninstall everything from the quickstart guide. + +Delete the GatewayClass, Gateway, HTTPRoute and Example App: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.4.0/quickstart.yaml --ignore-not-found=true +``` + +Delete the Gateway API CRDs and Envoy Gateway: + +```shell +helm uninstall eg -n envoy-gateway-system +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. diff --git a/site/content/en/v0.4.0/user/rate-limit.md b/site/content/en/v0.4.0/user/rate-limit.md new file mode 100644 index 00000000000..e932db92627 --- /dev/null +++ b/site/content/en/v0.4.0/user/rate-limit.md @@ -0,0 +1,633 @@ +--- +title: "Rate limit" +--- + +Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + +Here are some reasons why you may want to implements Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied +i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit +if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. + +Envoy Gateway introduces a new CRD called [RateLimitFilter][] that allows the user to describe their rate limit intent. This instantiated resource +can be linked to a [HTTPRoute][] resource using an [ExtensionRef][] filter. + +## Prerequisites + +### Install Envoy Gateway + +* Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +### Install Redis + +* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. +Lets install a Redis deployment in the `redis-system` namespce. + +```shell +cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com +; (1 server found) +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 +;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 +;; WARNING: recursion requested but not available + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +; COOKIE: 24fb86eba96ebf62 (echoed) +;; QUESTION SECTION: +;foo.bar.com. IN A + +;; ADDITIONAL SECTION: +foo.bar.com. 0 IN A 10.244.0.19 +_udp.foo.bar.com. 0 IN SRV 0 0 42376 . + +;; Query time: 1 msec +;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) +;; WHEN: Fri Jan 13 10:20:34 UTC 2023 +;; MSG SIZE rcvd: 114 +``` + +## Clean-Up + +Follow the steps from the [Quickstart Guide](../quickstart) to uninstall Envoy Gateway. + +Delete the CoreDNS example manifest and the UDPRoute: + +```shell +kubectl delete deploy/coredns +kubectl delete service/coredns +kubectl delete cm/coredns +kubectl delete udproute/coredns +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/v0.4.0/configuration/listeners/udp_filters/udp_proxy diff --git a/site/content/en/v0.5.0/_index.md b/site/content/en/v0.5.0/_index.md new file mode 100644 index 00000000000..53d07c8aae3 --- /dev/null +++ b/site/content/en/v0.5.0/_index.md @@ -0,0 +1,21 @@ ++++ +title = "Welcome to Envoy Gateway" +description = "Envoy Gateway Documents" +linktitle = "Documentation" + +[[cascade]] +type = "docs" ++++ + +{{% alert title="Note" color="primary" %}} + +This project is under **active** development. Many features are not complete. We would love for you to [Get Involved](contributions/)! + +{{% /alert %}} + +Envoy Gateway is an open source project for managing [Envoy Proxy](https://www.envoyproxy.io/) as a standalone or Kubernetes-based application +gateway. [Gateway API](https://gateway-api.sigs.k8s.io/) resources are used to dynamically provision and configure the managed Envoy Proxies. + +![architecture](/img/traffic.png) + +## Ready to get started? diff --git a/site/content/en/v0.5.0/api/_index.md b/site/content/en/v0.5.0/api/_index.md new file mode 100644 index 00000000000..396d9ffcefc --- /dev/null +++ b/site/content/en/v0.5.0/api/_index.md @@ -0,0 +1,5 @@ +--- +title: "API" +description: This section includes APIs of Envoy Gateway. +weight: 80 +--- diff --git a/site/content/en/v0.5.0/api/config_types.md b/site/content/en/v0.5.0/api/config_types.md new file mode 100644 index 00000000000..93764201f34 --- /dev/null +++ b/site/content/en/v0.5.0/api/config_types.md @@ -0,0 +1,980 @@ +--- +title: "Config APIs" +--- + +## Packages +- [config.gateway.envoyproxy.io/v1alpha1](#configgatewayenvoyproxyiov1alpha1) + + +## config.gateway.envoyproxy.io/v1alpha1 + +Package v1alpha1 contains API schema definitions for the config.gateway.envoyproxy.io +API group. + + +### Resource Types +- [EnvoyGateway](#envoygateway) +- [EnvoyProxy](#envoyproxy) + + + +## CustomTag + + + + + +_Appears in:_ +- [ProxyTracing](#proxytracing) + +| Field | Description | +| --- | --- | +| `type` _[CustomTagType](#customtagtype)_ | Type defines the type of custom tag. | +| `literal` _[LiteralCustomTag](#literalcustomtag)_ | Literal adds hard-coded value to each span. It's required when the type is "Literal". | +| `environment` _[EnvironmentCustomTag](#environmentcustomtag)_ | Environment adds value from environment variable to each span. It's required when the type is "Environment". | +| `requestHeader` _[RequestHeaderCustomTag](#requestheadercustomtag)_ | RequestHeader adds value from request header to each span. It's required when the type is "RequestHeader". | + + +## CustomTagType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [CustomTag](#customtag) + + + +## EnvironmentCustomTag + + + +EnvironmentCustomTag adds value from environment variable to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines the name of the environment variable which to extract the value from. | +| `defaultValue` _string_ | DefaultValue defines the default value to use if the environment variable is not set. | + + +## EnvoyGateway + + + +EnvoyGateway is the schema for the envoygateways API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyGateway` +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | +| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | +| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | + + +## EnvoyGatewayAdmin + + + +EnvoyGatewayAdmin defines the Envoy Gateway Admin configuration. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `address` _[EnvoyGatewayAdminAddress](#envoygatewayadminaddress)_ | Address defines the address of Envoy Gateway Admin Server. | +| `debug` _boolean_ | Debug defines if enable the /debug endpoint of Envoy Gateway. | + + +## EnvoyGatewayAdminAddress + + + +EnvoyGatewayAdminAddress defines the Envoy Gateway Admin Address configuration. + +_Appears in:_ +- [EnvoyGatewayAdmin](#envoygatewayadmin) + +| Field | Description | +| --- | --- | +| `port` _integer_ | Port defines the port the admin server is exposed on. | +| `host` _string_ | Host defines the admin server hostname. | + + +## EnvoyGatewayCustomProvider + + + +EnvoyGatewayCustomProvider defines configuration for the Custom provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `resource` _[EnvoyGatewayResourceProvider](#envoygatewayresourceprovider)_ | Resource defines the desired resource provider. This provider is used to specify the provider to be used to retrieve the resource configurations such as Gateway API resources | +| `infrastructure` _[EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider)_ | Infrastructure defines the desired infrastructure provider. This provider is used to specify the provider to be used to provide an environment to deploy the out resources like the Envoy Proxy data plane. | + + +## EnvoyGatewayFileResourceProvider + + + +EnvoyGatewayFileResourceProvider defines configuration for the File Resource provider. + +_Appears in:_ +- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) + +| Field | Description | +| --- | --- | +| `paths` _string array_ | Paths are the paths to a directory or file containing the resource configuration. Recursive sub directories are not currently supported. | + + +## EnvoyGatewayHostInfrastructureProvider + + + +EnvoyGatewayHostInfrastructureProvider defines configuration for the Host Infrastructure provider. + +_Appears in:_ +- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) + + + +## EnvoyGatewayInfrastructureProvider + + + +EnvoyGatewayInfrastructureProvider defines configuration for the Custom Infrastructure provider. + +_Appears in:_ +- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) + +| Field | Description | +| --- | --- | +| `type` _[InfrastructureProviderType](#infrastructureprovidertype)_ | Type is the type of infrastructure providers to use. Supported types are "Host". | +| `host` _[EnvoyGatewayHostInfrastructureProvider](#envoygatewayhostinfrastructureprovider)_ | Host defines the configuration of the Host provider. Host provides runtime deployment of the data plane as a child process on the host environment. | + + +## EnvoyGatewayKubernetesProvider + + + +EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `rateLimitDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. If unspecified, default settings for the managed Envoy ratelimit deployment resource are applied. | +| `watch` _[KubernetesWatchMode](#kuberneteswatchmode)_ | Watch holds configuration of which input resources should be watched and reconciled. | +| `deploy` _[KubernetesDeployMode](#kubernetesdeploymode)_ | Deploy holds configuration of how output managed resources such as the Envoy Proxy data plane should be deployed | +| `overwrite_control_plane_certs` _boolean_ | OverwriteControlPlaneCerts updates the secrets containing the control plane certs, when set. | + + +## EnvoyGatewayLogComponent + +_Underlying type:_ `string` + +EnvoyGatewayLogComponent defines a component that supports a configured logging level. + +_Appears in:_ +- [EnvoyGatewayLogging](#envoygatewaylogging) + + + +## EnvoyGatewayLogging + + + +EnvoyGatewayLogging defines logging for Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[EnvoyGatewayLogComponent](#envoygatewaylogcomponent), values:[LogLevel](#loglevel))_ | Level is the logging level. If unspecified, defaults to "info". EnvoyGatewayLogComponent options: default/provider/gateway-api/xds-translator/xds-server/infrastructure/global-ratelimit. LogLevel options: debug/info/error/warn. | + + +## EnvoyGatewayProvider + + + +EnvoyGatewayProvider defines the desired configuration of a provider. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of provider to use. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider)_ | Kubernetes defines the configuration of the Kubernetes provider. Kubernetes provides runtime configuration via the Kubernetes API. | +| `custom` _[EnvoyGatewayCustomProvider](#envoygatewaycustomprovider)_ | Custom defines the configuration for the Custom provider. This provider allows you to define a specific resource provider and a infrastructure provider. | + + +## EnvoyGatewayResourceProvider + + + +EnvoyGatewayResourceProvider defines configuration for the Custom Resource provider. + +_Appears in:_ +- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) + +| Field | Description | +| --- | --- | +| `type` _[ResourceProviderType](#resourceprovidertype)_ | Type is the type of resource provider to use. Supported types are "File". | +| `file` _[EnvoyGatewayFileResourceProvider](#envoygatewayfileresourceprovider)_ | File defines the configuration of the File provider. File provides runtime configuration defined by one or more files. | + + +## EnvoyGatewaySpec + + + +EnvoyGatewaySpec defines the desired state of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) + +| Field | Description | +| --- | --- | +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | +| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | +| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | + + +## EnvoyProxy + + + +EnvoyProxy is the schema for the envoyproxies API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `config.gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyProxy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyProxySpec](#envoyproxyspec)_ | EnvoyProxySpec defines the desired state of EnvoyProxy. | + + +## EnvoyProxyKubernetesProvider + + + +EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource provider. + +_Appears in:_ +- [EnvoyProxyProvider](#envoyproxyprovider) + +| Field | Description | +| --- | --- | +| `envoyDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | EnvoyDeployment defines the desired state of the Envoy deployment resource. If unspecified, default settings for the managed Envoy deployment resource are applied. | +| `envoyService` _[KubernetesServiceSpec](#kubernetesservicespec)_ | EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings for the managed Envoy service resource are applied. | + + +## EnvoyProxyProvider + + + +EnvoyProxyProvider defines the desired state of a resource provider. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of resource provider to use. A resource provider provides infrastructure resources for running the data plane, e.g. Envoy proxy, and optional auxiliary control planes. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider)_ | Kubernetes defines the desired state of the Kubernetes resource provider. Kubernetes provides infrastructure resources for running the data plane, e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings for managed Kubernetes resources are applied. | + + +## EnvoyProxySpec + + + +EnvoyProxySpec defines the desired state of EnvoyProxy. + +_Appears in:_ +- [EnvoyProxy](#envoyproxy) + +| Field | Description | +| --- | --- | +| `provider` _[EnvoyProxyProvider](#envoyproxyprovider)_ | Provider defines the desired resource provider and provider-specific configuration. If unspecified, the "Kubernetes" resource provider is used with default configuration parameters. | +| `logging` _[ProxyLogging](#proxylogging)_ | Logging defines logging parameters for managed proxies. | +| `telemetry` _[ProxyTelemetry](#proxytelemetry)_ | Telemetry defines telemetry parameters for managed proxies. | +| `bootstrap` _string_ | Bootstrap defines the Envoy Bootstrap as a YAML string. Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap to learn more about the syntax. If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration set by Envoy Gateway. Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources from it are not configurable and will result in the `EnvoyProxy` resource being rejected. Backward compatibility across minor versions is not guaranteed. We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. | + + + + +## ExtensionAPISettings + + + +ExtensionAPISettings defines the settings specific to Gateway API Extensions. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `enableEnvoyPatchPolicy` _boolean_ | EnableEnvoyPatchPolicy enables Envoy Gateway to reconcile and implement the EnvoyPatchPolicy resources. | + + +## ExtensionHooks + + + +ExtensionHooks defines extension hooks across all supported runners + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `xdsTranslator` _[XDSTranslatorHooks](#xdstranslatorhooks)_ | XDSTranslator defines all the supported extension hooks for the xds-translator runner | + + +## ExtensionManager + + + +ExtensionManager defines the configuration for registering an extension manager to the Envoy Gateway control plane. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `resources` _[GroupVersionKind](#groupversionkind) array_ | Resources defines the set of K8s resources the extension will handle. | +| `hooks` _[ExtensionHooks](#extensionhooks)_ | Hooks defines the set of hooks the extension supports | +| `service` _[ExtensionService](#extensionservice)_ | Service defines the configuration of the extension service that the Envoy Gateway Control Plane will call through extension hooks. | + + +## ExtensionService + + + +ExtensionService defines the configuration for connecting to a registered extension service. + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `tls` _[ExtensionTLS](#extensiontls)_ | TLS defines TLS configuration for communication between Envoy Gateway and the extension service. | + + +## ExtensionTLS + + + +ExtensionTLS defines the TLS configuration when connecting to an extension service + +_Appears in:_ +- [ExtensionService](#extensionservice) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef contains a references to objects (Kubernetes objects or otherwise) that contains a TLS certificate and private keys. These certificates are used to establish a TLS handshake to the extension server. + CertificateRef can only reference a Kubernetes Secret at this time. | + + +## FileEnvoyProxyAccessLog + + + + + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + +| Field | Description | +| --- | --- | +| `path` _string_ | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). Empty value disables accesslog. | + + +## Gateway + + + +Gateway defines the desired Gateway API configuration of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `controllerName` _string_ | ControllerName defines the name of the Gateway API controller. If unspecified, defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following for additional details: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass | + + +## GroupVersionKind + + + +GroupVersionKind unambiguously identifies a Kind. It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `group` _string_ | | +| `version` _string_ | | +| `kind` _string_ | | + + +## InfrastructureProviderType + +_Underlying type:_ `string` + +InfrastructureProviderType defines the types of custom infrastructure providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) + + + +## KubernetesContainerSpec + + + +KubernetesContainerSpec defines the desired state of the Kubernetes container resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#envvar-v1-core) array_ | List of environment variables to set in the container. | +| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources required by this container. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | +| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#securitycontext-v1-core)_ | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| `image` _string_ | Image specifies the EnvoyProxy container image to be used, instead of the default image. | +| `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volumemount-v1-core) array_ | VolumeMounts are volumes to mount into the container's filesystem. Cannot be updated. | + + +## KubernetesDeployMode + + + +KubernetesDeployMode holds configuration for how to deploy managed resources such as the Envoy Proxy data plane fleet. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) + + + +## KubernetesDeploymentSpec + + + +KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `replicas` _integer_ | Replicas is the number of desired pods. Defaults to 1. | +| `strategy` _[DeploymentStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#deploymentstrategy-v1-apps)_ | The deployment strategy to use to replace existing pods with new ones. | +| `pod` _[KubernetesPodSpec](#kubernetespodspec)_ | Pod defines the desired annotations and securityContext of container. | +| `container` _[KubernetesContainerSpec](#kubernetescontainerspec)_ | Container defines the resources and securityContext of container. | + + +## KubernetesPodSpec + + + +KubernetesPodSpec defines the desired state of the Kubernetes pod resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations are the annotations that should be appended to the pods. By default, no pod annotations are appended. | +| `labels` _object (keys:string, values:string)_ | Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. | +| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)_ | SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. | +| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#affinity-v1-core)_ | If specified, the pod's scheduling constraints. | +| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#toleration-v1-core) array_ | If specified, the pod's tolerations. | +| `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volume-v1-core) array_ | Volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes | + + +## KubernetesServiceSpec + + + +KubernetesServiceSpec defines the desired state of the Kubernetes service resource. + +_Appears in:_ +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations that should be appended to the service. By default, no annotations are appended. | +| `type` _[ServiceType](#servicetype)_ | Type determines how the Service is exposed. Defaults to LoadBalancer. Valid options are ClusterIP, LoadBalancer and NodePort. "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | + + +## KubernetesWatchMode + + + +KubernetesWatchMode holds the configuration for which input resources to watch and reconcile. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) + +| Field | Description | +| --- | --- | +| `Namespaces` _string array_ | Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped resources such as Gateway, HTTPRoute and Service. Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as GatewayClass that it is linked to. By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources from all namespaces. | + + +## LiteralCustomTag + + + +LiteralCustomTag adds hard-coded value to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `value` _string_ | Value defines the hard-coded value to add to each span. | + + +## LogComponent + +_Underlying type:_ `string` + +LogComponent defines a component that supports a configured logging level. + +_Appears in:_ +- [ProxyLogging](#proxylogging) + + + +## LogLevel + +_Underlying type:_ `string` + +LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. This type is not implemented for EnvoyProxy until https://github.com/envoyproxy/gateway/issues/280 is fixed. + +_Appears in:_ +- [EnvoyGatewayLogging](#envoygatewaylogging) +- [ProxyLogging](#proxylogging) + + + +## MetricSink + + + + + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `type` _[MetricSinkType](#metricsinktype)_ | Type defines the metric sink type. EG currently only supports OpenTelemetry. | +| `openTelemetry` _[OpenTelemetrySink](#opentelemetrysink)_ | OpenTelemetry defines the configuration for OpenTelemetry sink. It's required if the sink type is OpenTelemetry. | + + +## MetricSinkType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [MetricSink](#metricsink) + + + +## OpenTelemetryEnvoyProxyAccessLog + + + +TODO: consider reuse ExtensionService? + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `resources` _object (keys:string, values:string)_ | Resources is a set of labels that describe the source of a log entry, including envoy node info. It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). | + + +## OpenTelemetrySink + + + + + +_Appears in:_ +- [MetricSink](#metricsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the service hostname. | +| `port` _integer_ | Port defines the port the service is exposed on. | + + +## PrometheusProvider + + + + + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + + + +## ProviderType + +_Underlying type:_ `string` + +ProviderType defines the types of providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) +- [EnvoyProxyProvider](#envoyproxyprovider) + + + +## ProxyAccessLog + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable disables access logging for managed proxies if set to true. | +| `settings` _[ProxyAccessLogSetting](#proxyaccesslogsetting) array_ | Settings defines accesslog settings for managed proxies. If unspecified, will send default format to stdout. | + + +## ProxyAccessLogFormat + + + +ProxyAccessLogFormat defines the format of accesslog. + +_Appears in:_ +- [ProxyAccessLogSetting](#proxyaccesslogsetting) + +| Field | Description | +| --- | --- | +| `type` _[ProxyAccessLogFormatType](#proxyaccesslogformattype)_ | Type defines the type of accesslog format. | +| `text` _string_ | Text defines the text accesslog format, following Envoy accesslog formatting, empty value results in proxy's default access log format. It's required when the format type is "Text". Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. | +| `json` _object (keys:string, values:string)_ | JSON is additional attributes that describe the specific event occurrence. Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) can be used as values for fields within the Struct. It's required when the format type is "JSON". | + + +## ProxyAccessLogFormatType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [ProxyAccessLogFormat](#proxyaccesslogformat) + + + +## ProxyAccessLogSetting + + + + + +_Appears in:_ +- [ProxyAccessLog](#proxyaccesslog) + +| Field | Description | +| --- | --- | +| `format` _[ProxyAccessLogFormat](#proxyaccesslogformat)_ | Format defines the format of accesslog. | +| `sinks` _[ProxyAccessLogSink](#proxyaccesslogsink) array_ | Sinks defines the sinks of accesslog. | + + +## ProxyAccessLogSink + + + + + +_Appears in:_ +- [ProxyAccessLogSetting](#proxyaccesslogsetting) + +| Field | Description | +| --- | --- | +| `type` _[ProxyAccessLogSinkType](#proxyaccesslogsinktype)_ | Type defines the type of accesslog sink. | +| `file` _[FileEnvoyProxyAccessLog](#fileenvoyproxyaccesslog)_ | File defines the file accesslog sink. | +| `openTelemetry` _[OpenTelemetryEnvoyProxyAccessLog](#opentelemetryenvoyproxyaccesslog)_ | OpenTelemetry defines the OpenTelemetry accesslog sink. | + + +## ProxyAccessLogSinkType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + + + +## ProxyLogging + + + +ProxyLogging defines logging parameters for managed proxies. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[LogComponent](#logcomponent), values:[LogLevel](#loglevel))_ | Level is a map of logging level per component, where the component is the key and the log level is the value. If unspecified, defaults to "default: warn". | + + +## ProxyMetrics + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `prometheus` _[PrometheusProvider](#prometheusprovider)_ | Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. | +| `sinks` _[MetricSink](#metricsink) array_ | Sinks defines the metric sinks where metrics are sent to. | + + +## ProxyTelemetry + + + + + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `accessLog` _[ProxyAccessLog](#proxyaccesslog)_ | AccessLogs defines accesslog parameters for managed proxies. If unspecified, will send default format to stdout. | +| `tracing` _[ProxyTracing](#proxytracing)_ | Tracing defines tracing configuration for managed proxies. If unspecified, will not send tracing data. | +| `metrics` _[ProxyMetrics](#proxymetrics)_ | Metrics defines metrics configuration for managed proxies. | + + +## ProxyTracing + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `samplingRate` _integer_ | SamplingRate controls the rate at which traffic will be selected for tracing if no prior sampling decision has been made. Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. | +| `customTags` _object (keys:string, values:[CustomTag](#customtag))_ | CustomTags defines the custom tags to add to each span. If provider is kubernetes, pod name and namespace are added by default. | +| `provider` _[TracingProvider](#tracingprovider)_ | Provider defines the tracing provider. Only OpenTelemetry is supported currently. | + + +## RateLimit + + + +RateLimit defines the configuration associated with the Rate Limit Service used for Global Rate Limiting. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `backend` _[RateLimitDatabaseBackend](#ratelimitdatabasebackend)_ | Backend holds the configuration associated with the database backend used by the rate limit service to store state associated with global ratelimiting. | + + +## RateLimitDatabaseBackend + + + +RateLimitDatabaseBackend defines the configuration associated with the database backend used by the rate limit service. + +_Appears in:_ +- [RateLimit](#ratelimit) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitDatabaseBackendType](#ratelimitdatabasebackendtype)_ | Type is the type of database backend to use. Supported types are: * Redis: Connects to a Redis database. | +| `redis` _[RateLimitRedisSettings](#ratelimitredissettings)_ | Redis defines the settings needed to connect to a Redis database. | + + +## RateLimitDatabaseBackendType + +_Underlying type:_ `string` + +RateLimitDatabaseBackendType specifies the types of database backend to be used by the rate limit service. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + + + +## RateLimitRedisSettings + + + +RateLimitRedisSettings defines the configuration for connecting to redis database. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + +| Field | Description | +| --- | --- | +| `url` _string_ | URL of the Redis Database. | +| `tls` _[RedisTLSSettings](#redistlssettings)_ | TLS defines TLS configuration for connecting to redis database. | + + +## RedisTLSSettings + + + +RedisTLSSettings defines the TLS configuration for connecting to redis database. + +_Appears in:_ +- [RateLimitRedisSettings](#ratelimitredissettings) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef defines the client certificate reference for TLS connections. Currently only a Kubernetes Secret of type TLS is supported. | + + +## RequestHeaderCustomTag + + + +RequestHeaderCustomTag adds value from request header to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines the name of the request header which to extract the value from. | +| `defaultValue` _string_ | DefaultValue defines the default value to use if the request header is not set. | + + +## ResourceProviderType + +_Underlying type:_ `string` + +ResourceProviderType defines the types of custom resource providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) + + + +## ServiceType + +_Underlying type:_ `string` + +ServiceType string describes ingress methods for a service + +_Appears in:_ +- [KubernetesServiceSpec](#kubernetesservicespec) + + + +## TracingProvider + + + + + +_Appears in:_ +- [ProxyTracing](#proxytracing) + +| Field | Description | +| --- | --- | +| `type` _[TracingProviderType](#tracingprovidertype)_ | Type defines the tracing provider type. EG currently only supports OpenTelemetry. | +| `host` _string_ | Host define the provider service hostname. | +| `port` _integer_ | Port defines the port the provider service is exposed on. | + + +## TracingProviderType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [TracingProvider](#tracingprovider) + + + +## XDSTranslatorHook + +_Underlying type:_ `string` + +XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support for the xds-translator + +_Appears in:_ +- [XDSTranslatorHooks](#xdstranslatorhooks) + + + +## XDSTranslatorHooks + + + +XDSTranslatorHooks contains all the pre and post hooks for the xds-translator runner. + +_Appears in:_ +- [ExtensionHooks](#extensionhooks) + +| Field | Description | +| --- | --- | +| `pre` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | +| `post` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | + + diff --git a/site/content/en/v0.5.0/api/extension_types.md b/site/content/en/v0.5.0/api/extension_types.md new file mode 100644 index 00000000000..5eb811ac451 --- /dev/null +++ b/site/content/en/v0.5.0/api/extension_types.md @@ -0,0 +1,393 @@ +--- +title: "Extension APIs" +--- + +## Packages +- [gateway.envoyproxy.io/v1alpha1](#gatewayenvoyproxyiov1alpha1) + + +## gateway.envoyproxy.io/v1alpha1 + +Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io API group. + + +### Resource Types +- [AuthenticationFilter](#authenticationfilter) +- [EnvoyPatchPolicy](#envoypatchpolicy) +- [EnvoyPatchPolicyList](#envoypatchpolicylist) +- [RateLimitFilter](#ratelimitfilter) + + + +## AuthenticationFilter + + + + + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `AuthenticationFilter` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[AuthenticationFilterSpec](#authenticationfilterspec)_ | Spec defines the desired state of the AuthenticationFilter type. | + + +## AuthenticationFilterSpec + + + +AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. + +_Appears in:_ +- [AuthenticationFilter](#authenticationfilter) + +| Field | Description | +| --- | --- | +| `type` _[AuthenticationFilterType](#authenticationfiltertype)_ | Type defines the type of authentication provider to use. Supported provider types are "JWT". | +| `jwtProviders` _[JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) array_ | JWT defines the JSON Web Token (JWT) authentication provider type. When multiple jwtProviders are specified, the JWT is considered valid if any of the providers successfully validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | + + +## AuthenticationFilterType + +_Underlying type:_ `string` + +AuthenticationFilterType is a type of authentication provider. + +_Appears in:_ +- [AuthenticationFilterSpec](#authenticationfilterspec) + + + +## ClaimToHeader + + + +ClaimToHeader defines a configuration to convert JWT claims into HTTP headers + +_Appears in:_ +- [JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) + +| Field | Description | +| --- | --- | +| `header` _string_ | Header defines the name of the HTTP request header that the JWT Claim will be saved into. | +| `claim` _string_ | Claim is the JWT Claim that should be saved into the header : it can be a nested claim of type (eg. "claim.nested.key", "sub"). The nested claim name must use dot "." to separate the JSON name path. | + + +## EnvoyJSONPatchConfig + + + +EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource using JSONPatch semantic + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyResourceType](#envoyresourcetype)_ | Type is the typed URL of the Envoy xDS Resource | +| `name` _string_ | Name is the name of the resource | +| `operation` _[JSONPatchOperation](#jsonpatchoperation)_ | Patch defines the JSON Patch Operation | + + +## EnvoyPatchPolicy + + + +EnvoyPatchPolicy allows the user to modify the generated Envoy xDS resources by Envoy Gateway using this patch API + +_Appears in:_ +- [EnvoyPatchPolicyList](#envoypatchpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyPatchPolicySpec](#envoypatchpolicyspec)_ | Spec defines the desired state of EnvoyPatchPolicy. | + + +## EnvoyPatchPolicyList + + + +EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[EnvoyPatchPolicy](#envoypatchpolicy) array_ | | + + +## EnvoyPatchPolicySpec + + + +EnvoyPatchPolicySpec defines the desired state of EnvoyPatchPolicy. + +_Appears in:_ +- [EnvoyPatchPolicy](#envoypatchpolicy) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyPatchType](#envoypatchtype)_ | Type decides the type of patch. Valid EnvoyPatchType values are "JSONPatch". | +| `jsonPatches` _[EnvoyJSONPatchConfig](#envoyjsonpatchconfig) array_ | JSONPatch defines the JSONPatch configuration. | +| `targetRef` _[PolicyTargetReference](#policytargetreference)_ | TargetRef is the name of the Gateway API resource this policy is being attached to. Currently only attaching to Gateway is supported This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway TargetRef | +| `priority` _integer_ | Priority of the EnvoyPatchPolicy. If multiple EnvoyPatchPolicies are applied to the same TargetRef, they will be applied in the ascending order of the priority i.e. int32.min has the highest priority and int32.max has the lowest priority. Defaults to 0. | + + + + +## EnvoyPatchType + +_Underlying type:_ `string` + +EnvoyPatchType specifies the types of Envoy patching mechanisms. + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + + + +## EnvoyResourceType + +_Underlying type:_ `string` + +EnvoyResourceType specifies the type URL of the Envoy resource. + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + + + +## GlobalRateLimit + + + +GlobalRateLimit defines global rate limit configuration. + +_Appears in:_ +- [RateLimitFilterSpec](#ratelimitfilterspec) + +| Field | Description | +| --- | --- | +| `rules` _[RateLimitRule](#ratelimitrule) array_ | Rules are a list of RateLimit selectors and limits. Each rule and its associated limit is applied in a mutually exclusive way i.e. if multiple rules get selected, each of their associated limits get applied, so a single traffic request might increase the rate limit counters for multiple rules if selected. | + + +## HeaderMatch + + + +HeaderMatch defines the match attributes within the HTTP Headers of the request. + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + +| Field | Description | +| --- | --- | +| `type` _[HeaderMatchType](#headermatchtype)_ | Type specifies how to match against the value of the header. | +| `name` _string_ | Name of the HTTP header. | +| `value` _string_ | Value within the HTTP header. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. Do not set this field when Type="Distinct", implying matching on any/all unique values within the header. | + + +## HeaderMatchType + +_Underlying type:_ `string` + +HeaderMatchType specifies the semantics of how HTTP header values should be compared. Valid HeaderMatchType values are "Exact", "RegularExpression", and "Distinct". + +_Appears in:_ +- [HeaderMatch](#headermatch) + + + +## JSONPatchOperation + + + +JSONPatchOperation defines the JSON Patch Operation as defined in https://datatracker.ietf.org/doc/html/rfc6902 + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + +| Field | Description | +| --- | --- | +| `op` _[JSONPatchOperationType](#jsonpatchoperationtype)_ | Op is the type of operation to perform | +| `path` _string_ | Path is the location of the target document/field where the operation will be performed Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. | +| `value` _[JSON](#json)_ | Value is the new value of the path location. | + + +## JSONPatchOperationType + +_Underlying type:_ `string` + +JSONPatchOperationType specifies the JSON Patch operations that can be performed. + +_Appears in:_ +- [JSONPatchOperation](#jsonpatchoperation) + + + +## JwtAuthenticationFilterProvider + + + +JwtAuthenticationFilterProvider defines the JSON Web Token (JWT) authentication provider type and how JWTs should be verified: + +_Appears in:_ +- [AuthenticationFilterSpec](#authenticationfilterspec) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines a unique name for the JWT provider. A name can have a variety of forms, including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. | +| `issuer` _string_ | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. | +| `audiences` _string array_ | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. | +| `remoteJWKS` _[RemoteJWKS](#remotejwks)_ | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. | +| `claimToHeaders` _[ClaimToHeader](#claimtoheader) array_ | ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers For examples, following config: The claim must be of type; string, int, double, bool. Array type claims are not supported | + + +## RateLimitFilter + + + +RateLimitFilter allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `RateLimitFilter` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[RateLimitFilterSpec](#ratelimitfilterspec)_ | Spec defines the desired state of RateLimitFilter. | + + +## RateLimitFilterSpec + + + +RateLimitFilterSpec defines the desired state of RateLimitFilter. + +_Appears in:_ +- [RateLimitFilter](#ratelimitfilter) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitType](#ratelimittype)_ | Type decides the scope for the RateLimits. Valid RateLimitType values are "Global". | +| `global` _[GlobalRateLimit](#globalratelimit)_ | Global defines global rate limit configuration. | + + +## RateLimitRule + + + +RateLimitRule defines the semantics for matching attributes from the incoming requests, and setting limits for them. + +_Appears in:_ +- [GlobalRateLimit](#globalratelimit) + +| Field | Description | +| --- | --- | +| `clientSelectors` _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | ClientSelectors holds the list of select conditions to select specific clients using attributes from the traffic flow. All individual select conditions must hold True for this rule and its limit to be applied. If this field is empty, it is equivalent to True, and the limit is applied. | +| `limit` _[RateLimitValue](#ratelimitvalue)_ | Limit holds the rate limit values. This limit is applied for traffic flows when the selectors compute to True, causing the request to be counted towards the limit. The limit is enforced and the request is ratelimited, i.e. a response with 429 HTTP status code is sent back to the client when the selected requests have reached the limit. | + + +## RateLimitSelectCondition + + + +RateLimitSelectCondition specifies the attributes within the traffic flow that can be used to select a subset of clients to be ratelimited. All the individual conditions must hold True for the overall condition to hold True. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `headers` _[HeaderMatch](#headermatch) array_ | Headers is a list of request headers to match. Multiple header values are ANDed together, meaning, a request MUST match all the specified headers. | +| `sourceIP` _string_ | Deprecated: Use SourceCIDR instead. | +| `sourceCIDR` _[SourceMatch](#sourcematch)_ | SourceCIDR is the client IP Address range to match on. | + + +## RateLimitType + +_Underlying type:_ `string` + +RateLimitType specifies the types of RateLimiting. + +_Appears in:_ +- [RateLimitFilterSpec](#ratelimitfilterspec) + + + +## RateLimitUnit + +_Underlying type:_ `string` + +RateLimitUnit specifies the intervals for setting rate limits. Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". + +_Appears in:_ +- [RateLimitValue](#ratelimitvalue) + + + +## RateLimitValue + + + +RateLimitValue defines the limits for rate limiting. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `requests` _integer_ | | +| `unit` _[RateLimitUnit](#ratelimitunit)_ | | + + +## RemoteJWKS + + + +RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. + +_Appears in:_ +- [JwtAuthenticationFilterProvider](#jwtauthenticationfilterprovider) + +| Field | Description | +| --- | --- | +| `uri` _string_ | URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to validate the server certificate. | + + +## SourceMatch + + + + + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + +| Field | Description | +| --- | --- | +| `type` _[SourceMatchType](#sourcematchtype)_ | | +| `value` _string_ | Value is the IP CIDR that represents the range of Source IP Addresses of the client. These could also be the intermediate addresses through which the request has flown through and is part of the `X-Forwarded-For` header. For example, `192.168.0.1/32`, `192.168.0.0/24`, `001:db8::/64`. | + + +## SourceMatchType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [SourceMatch](#sourcematch) + + + diff --git a/site/content/en/v0.5.0/contributions/CODEOWNERS.md b/site/content/en/v0.5.0/contributions/CODEOWNERS.md new file mode 100644 index 00000000000..63b751abde5 --- /dev/null +++ b/site/content/en/v0.5.0/contributions/CODEOWNERS.md @@ -0,0 +1,19 @@ +--- +title: "Maintainers" +description: "This section includes Maintainers of Envoy Gateway." +--- + +## The following maintainers, listed in alphabetical order, own everything + +- @AliceProxy +- @arkodg +- @Xunzhuo +- @zirain +- @qicz + +## Emeritus Maintainers + +- @danehans +- @alexgervais +- @skriss +- @youngnick diff --git a/site/content/en/v0.5.0/contributions/CODE_OF_CONDUCT.md b/site/content/en/v0.5.0/contributions/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..e19da050dff --- /dev/null +++ b/site/content/en/v0.5.0/contributions/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ +--- +title: "Code of Conduct" +description: "This section includes Code of Conduct of Envoy Gateway." +--- + +Envoy Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/site/content/en/v0.5.0/contributions/CONTRIBUTING.md b/site/content/en/v0.5.0/contributions/CONTRIBUTING.md new file mode 100644 index 00000000000..f94b2c940e9 --- /dev/null +++ b/site/content/en/v0.5.0/contributions/CONTRIBUTING.md @@ -0,0 +1,190 @@ +--- +title: "Contributing" +description: "This section tells how to contribute to Envoy Gateway." +weight: 3 +--- + +We welcome contributions from the community. Please carefully review the [project goals](/about) +and following guidelines to streamline your contributions. + +## Communication + +* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no + one else is working on it and ask you to open a GitHub issue. +* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or + changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to + agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process + for major features is also important so that [affiliations with commit access](../codeowners) can + come to agreement on the design. If it's appropriate to write a design document, the document must + be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable + location. +* Small patches and bug fixes don't need prior communication. + +## Inclusivity + +The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere +to the following guidelines for all code, APIs, and documentation: + +* The following words and phrases are not allowed: + * *Whitelist*: use allowlist instead. + * *Blacklist*: use denylist or blocklist instead. + * *Master*: use primary instead. + * *Slave*: use secondary or replica instead. +* Documentation should be written in an inclusive style. The [Google developer + documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent + reference on this topic. +* The above policy is not considered definitive and may be amended in the future as industry best + practices evolve. Additional comments on this topic may be provided by maintainers during code + review. + +## Submitting a PR + +* Fork the repo. +* Hack +* DCO sign-off each commit. This can be done with `git commit -s`. +* Submit your PR. +* Tests will automatically run for you. +* We will **not** merge any PR that is not passing tests. +* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage + build. If your PR cannot have 100% coverage for some reason please clearly explain why when you + open it. +* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/site) folder of the repo as + well as the [changelog](/blog/releases). +* All code comments and documentation are expected to have proper English grammar and punctuation. + If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try + to find some help but there are no guarantees. +* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary + and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. + Examples: + * "docs: fix grammar error" + * "feat(translator): add new feature" + * "fix: fix xx bug" + * "chore: change ci & build tools etc" +* Your PR commit message will be used as the commit message when your PR is merged. You should + update this field if your PR diverges during review. +* Your PR description should have details on what the PR does. If it fixes an existing issue it + should end with "Fixes #XXX". +* If your PR is co-authored or based on an earlier PR from another contributor, + please attribute them with `Co-authored-by: name `. See + GitHub's [multiple author + guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) + for further details. +* When all tests are passing and all other conditions described herein are satisfied, a maintainer + will be assigned to review and merge the PR. +* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits + are new commits and/or merges. We squash and merge so the number of commits you have in the PR + doesn't matter. +* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. + We reserve the right to close PRs that are not making progress. This is generally defined as no + changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. + Closing stale PRs helps us to keep on top of all the work currently in flight. + +## Maintainer PR Review Policy + +* See [CODEOWNERS.md](../codeowners) for the current list of maintainers. +* A maintainer representing a different affiliation from the PR owner is required to review and + approve the PR. +* When the project matures, it is expected that a "domain expert" for the code the PR touches should + review the PR. This person does not require commit access, just domain knowledge. +* The above rules may be waived for PRs which only update docs or comments, or trivial changes to + tests and tools (where trivial is decided by the maintainer in question). +* If there is a question on who should review a PR please discuss in Slack. +* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. +* Please make sure that the PR title, commit message, and description are updated if the PR changes + significantly during review. +* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge + title with the original title, and the commit body with every individual commit from the PR. + The maintainer doing the merge should make sure the title follows the guidelines above and should + overwrite the body with the original commit message from the PR (cleaning it up if necessary) + while preserving the PR author's final DCO sign-off. + +## Decision making + +This is a new and complex project, and we need to make a lot of decisions very quickly. +To this end, we've settled on this process for making (possibly contentious) decisions: + +* For decisions that need a record, we create an issue. +* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. +* Maintainers can cast binding votes on that comment by reacting or replying in another comment. +* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. +* Voting will be resolved by simple majority. +* In the event of deadlocks, the question will be put to steering instead. + +## DCO: Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign-off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +```bash +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` + +## Fixing DCO + +If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best +practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +the commit history to a single commit, append the DCO sign-off as described above, and [force +push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in +your history: + +```bash +git rebase -i HEAD^^ +(interactive squash + DCO append) +git push origin -f +``` + +Note, that in general rewriting history in this way is a hindrance to the review process and this +should only be done to correct a DCO mistake. diff --git a/site/content/en/v0.5.0/contributions/DEVELOP.md b/site/content/en/v0.5.0/contributions/DEVELOP.md new file mode 100644 index 00000000000..6f82c4a411f --- /dev/null +++ b/site/content/en/v0.5.0/contributions/DEVELOP.md @@ -0,0 +1,163 @@ +--- +title: "Developer Guide" +description: "This section tells how to develop Envoy Gateway." +weight: 2 +--- + +Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. + +## Prerequisites + +### go + +* Version: 1.20 +* Installation Guide: https://go.dev/doc/install + +### make + +* Recommended Version: 4.0 or later +* Installation Guide: https://www.gnu.org/software/make + +### docker + +* Optional when you want to build a Docker image or run `make` inside Docker. +* Recommended Version: 20.10.16 +* Installation Guide: https://docs.docker.com/engine/install + +### python3 + +* Need a `python3` program +* Must have a functioning `venv` module; this is part of the standard + library, but some distributions (such as Debian and Ubuntu) replace + it with a stub and require you to install a `python3-venv` package + separately. + +## Quickstart + +* Run `make help` to see all the available targets to build, test and run Envoy Gateway. + +### Building + +* Run `make build` to build all the binaries. +* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. +* Run `make build BINS="egctl"` to build the egctl binary. + +__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. + +### Testing + +* Run `make test` to run the golang tests. + +* Run `make testdata` to generate the golden YAML testdata files. + +### Running Linters + +* Run `make lint` to make sure your code passes all the linter checks. +__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). + +### Building and Pushing the Image + +* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. +* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. + +__Note:__ Replace `IMAGE` with your registry's image name. + +### Deploying Envoy Gateway for Test/Dev + +* Run `make create-cluster` to create a [Kind][] cluster. + +#### Option 1: Use the Latest [gateway-dev][] Image + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` + to use a different image tag. + +#### Option 2: Use a Custom Image + +* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. +* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. + +### Deploying Envoy Gateway in Kubernetes + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to + the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or + tag. +* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. + +__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. + +### Demo Setup + +* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource +(similar to steps outlined in the [Quickstart][] docs) and test the configuration. +* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. + +### Run Gateway API Conformance Tests + +The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the +Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run +`TAG=latest make run-conformance` to run the conformance tests. + +#### On a Linux Host + +* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] + image, and run Gateway API conformance tests. + +#### On a Mac Host + +Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following +workarounds to run conformance tests: + +* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run + `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image + to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to + uninstall Envoy Gateway. +* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. + +__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` +is unspecified, the short SHA of your current branch is used. + +### Debugging the Envoy Config + +An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port +(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. + +Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: + +```shell +export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward the admin interface port: + +```shell +kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 +``` + +Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. + +There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. + +### JWT Testing + +An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] +user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs +verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching +settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure +and is hosted in the repo. + +[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md +[make]: https://www.gnu.org/software/make/ +[Github Actions]: https://docs.github.com/en/actions +[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows +[Kind]: https://kind.sigs.k8s.io/ +[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ +[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ +[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ +[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags +[mac_connect]: https://github.com/chipmk/docker-mac-net-connect +[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[JWT Debugger]: https://jwt.io/ +[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/site/content/en/v0.5.0/contributions/DOCS.md b/site/content/en/v0.5.0/contributions/DOCS.md new file mode 100644 index 00000000000..ae19953a8b5 --- /dev/null +++ b/site/content/en/v0.5.0/contributions/DOCS.md @@ -0,0 +1,69 @@ +--- +title: "Working on Envoy Gateway Docs" +description: "This section tells the development of + Envoy Gateway Documents." +--- + +{{% alert title="Note" color="warning" %}} +We migrated from ***Sphinx*** to ***Hugo*** for Envoy Gateway Documents. + +Read blog: [Welcome to new website!](/blog/2023/10/08/welcome-to-new-website/) +{{% /alert %}} + +The documentation for the Envoy Gateway lives in the `site/content/en` directory. Any +individual document can be written using [Markdown]. + +## Documentation Structure + +We supported the versioned Docs now, the directory name under docs represents +the version of docs. The root of the latest site is in `site/content/en/latest`. +This is probably where to start if you're trying to understand how things fit together. + +Note that the new contents should be added to `site/content/en/latest` and will be cut off at +the next release. The contents under `site/content/en/v0.5.0` are auto-generated, +and usually do not need to make changes to them, unless if you find the current release pages have +some incorrect contents. If so, you should send a PR to update contents both of `site/content/en/latest` +and `site/content/en/v0.5.0`. + +You can access the website which represents the current release in default, +and you can access the website which contains the latest version changes in +[Here][latest-website] or at the footer of the pages. + +## Documentation Workflow + +To work with the docs, just edit Markdown files in `site/content/en/latest`, +then run + +```bash +make docs +``` + +This will create `site/public` with the built HTML pages. You can preview it +by running: + +``` shell +make docs-serve +``` + +If you want to generate a new release version of the docs, like `v0.6.0`, then run + +```bash +make docs-release TAG=v0.6.0 +``` + +This will update the VERSION file at the project root, which records current release version, +and it will be used in the pages version context and binary version output. Also, this will generate +new dir `site/content/en/v0.6.0`, which contains docs at v0.6.0 and updates artifact links to `v0.6.0` +in all files under `site/content/en/v0.6.0/user`, like `quickstart.md`, `http-routing.md` and etc. + +## Publishing Docs + +Whenever docs are pushed to `main`, CI will publish the built docs to GitHub +Pages. For more details, see `.github/workflows/docs.yaml`. + +## Reference + +Go to [Hugo](https://gohugo.io) and [Docsy](https://www.docsy.dev/docs) to learn more. + +[Markdown]: https://daringfireball.net/projects/markdown/syntax +[latest-website]: /latest diff --git a/site/content/en/v0.5.0/contributions/RELEASING.md b/site/content/en/v0.5.0/contributions/RELEASING.md new file mode 100644 index 00000000000..f5ec7954f27 --- /dev/null +++ b/site/content/en/v0.5.0/contributions/RELEASING.md @@ -0,0 +1,252 @@ +--- +title: "Release Process" +description: "This section tells the release process of Envoy Gateway." +--- + +This document guides maintainers through the process of creating an Envoy Gateway release. + +- [Release Candidate](#release-candidate) +- [Minor Release](#minor-release) +- [Announce the Release](#announce-the-release) + +## Release Candidate + +The following steps should be used for creating a release candidate. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export RELEASE_CANDIDATE_NUMBER=1 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and + the [Build and Test][] has successfully completed. +5. Create a new release branch from `main`. The release branch should be named + `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. Push the branch to the Envoy Gateway repo. + + ```shell + git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Create a topic branch for updating the Envoy proxy image to the tag supported by the release. Reference [PR #958][] + for additional details on updating the image tag. +8. Sign, commit, and push your changes to your fork. +9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not + proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. +10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' + ``` + +11. Push the tag to the Envoy Gateway repository. + + ```shell + git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} + ``` + +12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +13. Confirm that the [release workflow][] completed successfully. +14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +15. Confirm that the [release][] was created. +16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test + the quickstart steps using the release candidate by manually updating the links. +17. [Generate][] the GitHub changelog. +18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. +19. If you find any bugs in this process, please create an issue. + +### Setup cherry picker action + +After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. + +Configuration looks like following: + +```yaml + cherry_pick_release_v0_4: + runs-on: ubuntu-latest + name: Cherry pick into release-v0.4 + if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cherry pick into release/v0.4 + uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 + with: + branch: release/v0.4 + title: "[release/v0.4] {old_title}" + body: "Cherry picking #{old_pull_request_id} onto release/v0.4" + labels: | + cherrypick/release-v0.4 + # put release manager here + reviewers: | + AliceProxy +``` + +Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. + +## Minor Release + +The following steps should be used for creating a minor release. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. +- A release branch that has been cut from the corresponding release candidate. Refer to the + [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. + + 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release + notes should be an accumulation of the release candidate release notes and any changes since the release + candidate. + 2. Create a release announcement. Refer to [PR #635] as an example release announcement. + 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. + 4. Generate the versioned release docs: + + ``` shell + make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + + 5. Update the `Get Started` and `Contributing` button referred link in `site/content/en/_index.md`: + + ```shell + + Get Started + + + Contributing + + ``` + + 6. Uodate the `Documentation` referred link on the menu in `site/hugo.toml`: + + ```shell + [[menu.main]] + name = "Documentation" + weight = -101 + pre = "" + url = "/v0.5.0" + ``` + +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged + and the [Build and Test][] has completed for your final PR. + +5. Checkout the release branch. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. If the tip of the release branch does not match the tip of `main`, perform the following: + + 1. Create a topic branch from the release branch. + 2. Cherry-pick the commits from `main` that differ from the release branch. + 3. Run tests locally, e.g. `make lint`. + 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. + 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. + 6. Do not proceed until the PR has merged and CI passes for the merged PR. + 7. If you are still on your topic branch, change to the release branch: + + ```shell + git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + + 8. Ensure your local release branch is up-to-date: + + ```shell + git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Tag the head of your release branch with the release tag. For example: + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' + ``` + + __Note:__ The tag version differs from the release branch by including the `.0` patch version. + +8. Push the tag to the Envoy Gateway repository. + + ```shell + git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +10. Confirm that the [release workflow][] completed successfully. +11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +12. Confirm that the [release][] was created. +13. Confirm that the steps in the [Quickstart Guide][] work as expected. +14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: + + ```console + # Release Announcement + + Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] + (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. + ``` + +If you find any bugs in this process, please create an issue. + +## Announce the Release + +It's important that the world knows about the release. Use the following steps to announce the release. + +1. Set the release information in the Envoy Gateway Slack channel. For example: + + ```shell + Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +2. Send a message to the Envoy Gateway Slack channel. For example: + + ```shell + On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway + v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. + Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs + to start using Envoy Gateway. + ... + ``` + + Link to the GitHub release and release announcement page that highlights the release. + +[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes +[Pull Request]: https://github.com/envoyproxy/gateway/pulls +[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md +[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml +[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml +[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml +[image]: https://hub.docker.com/r/envoyproxy/gateway/tags +[release]: https://github.com/envoyproxy/gateway/releases +[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +[PR #635]: https://github.com/envoyproxy/gateway/pull/635 +[PR #958]: https://github.com/envoyproxy/gateway/pull/958 +[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 +[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/site/content/en/v0.5.0/contributions/_index.md b/site/content/en/v0.5.0/contributions/_index.md new file mode 100644 index 00000000000..3255d996472 --- /dev/null +++ b/site/content/en/v0.5.0/contributions/_index.md @@ -0,0 +1,5 @@ +--- +title: Get Involved +description: "This section includes contents related to **Contributions**" +weight: 100 +--- diff --git a/site/content/en/v0.5.0/contributions/roadmap.md b/site/content/en/v0.5.0/contributions/roadmap.md new file mode 100644 index 00000000000..955af2a9623 --- /dev/null +++ b/site/content/en/v0.5.0/contributions/roadmap.md @@ -0,0 +1,96 @@ +--- +title: "Roadmap" +weight: -1 +description: "This section records the roadmap of Envoy Gateway." +--- + +This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of +the project. + +## Contributing to the Roadmap + +- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use + case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update + this document accordingly. +- To help with an existing roadmap item, comment on or assign yourself to the associated issue. +- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A + maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be + discussed in an issue or a community meeting before implementing it. + +If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. +Look for issues with the `help wanted` label to get started. + +## Details + +Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific +roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by +contributing to the project. + +`Last Updated: April 2023` + +### [v0.2.0][v0.2.0]: Establish a Solid Foundation + +- Complete the core Envoy Gateway implementation- [Issue #60][60]. +- Establish initial testing, e2e, integration, etc- [Issue #64][64]. +- Establish user and developer project documentation- [Issue #17][17]. +- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. +- Setup a CI/CD pipeline- [Issue #63][63]. + +### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms + +- Support extended Gateway API fields [Issue #707][707]. +- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. +- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. +- Rate Limiting [Issue #670][670]. +- Authentication [Issue #336][336]. + +### [v0.4.0][v0.4.0]: Customizing Envoy Gateway + +- Extending Envoy Gateway control plane [Issue #20][20] +- Helm based installation for Envoy Gateway [Issue #650][650] +- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] +- Configuring xDS Bootstrap [Issue #31][31] + +### [v0.5.0][v0.5.0]: Observability and Scale + +- Observability for data plane [Issue #699][699]. +- Allow users to configure xDS Resources [Issue #24][24]. + +### [v0.6.0][v0.6.0]: Preparation for GA + +- Observability for control plane [Issue #700][700]. +- Compute and document Envoy Gateway performance [Issue #1365][1365]. +- Add TrafficPolicy APIs for advanced features [Issue #1492][1492]. +- Envoy Gateway meets readiness criteria [Issue #1160][1160]. + +[issue]: https://github.com/envoyproxy/gateway/issues +[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing +[pr]: https://github.com/envoyproxy/gateway/compare +[milestones]: https://github.com/envoyproxy/gateway/milestones +[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 +[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 +[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 +[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 +[v0.6.0]: https://github.com/envoyproxy/gateway/milestone/15 +[17]: https://github.com/envoyproxy/gateway/issues/17 +[20]: https://github.com/envoyproxy/gateway/issues/20 +[24]: https://github.com/envoyproxy/gateway/issues/24 +[31]: https://github.com/envoyproxy/gateway/issues/31 +[60]: https://github.com/envoyproxy/gateway/issues/60 +[63]: https://github.com/envoyproxy/gateway/issues/63 +[64]: https://github.com/envoyproxy/gateway/issues/64 +[65]: https://github.com/envoyproxy/gateway/issues/65 +[336]: https://github.com/envoyproxy/gateway/issues/336 +[641]: https://github.com/envoyproxy/gateway/issues/641 +[642]: https://github.com/envoyproxy/gateway/issues/642 +[648]: https://github.com/envoyproxy/gateway/issues/648 +[650]: https://github.com/envoyproxy/gateway/issues/650 +[643]: https://github.com/envoyproxy/gateway/issues/643 +[670]: https://github.com/envoyproxy/gateway/issues/670 +[675]: https://github.com/envoyproxy/gateway/issues/675 +[699]: https://github.com/envoyproxy/gateway/issues/699 +[700]: https://github.com/envoyproxy/gateway/issues/700 +[707]: https://github.com/envoyproxy/gateway/issues/707 +[1160]: https://github.com/envoyproxy/gateway/issues/1160 +[1365]: https://github.com/envoyproxy/gateway/issues/1365 +[1492]: https://github.com/envoyproxy/gateway/issues/1492 diff --git a/site/content/en/v0.5.0/design/_index.md b/site/content/en/v0.5.0/design/_index.md new file mode 100644 index 00000000000..21650809f7d --- /dev/null +++ b/site/content/en/v0.5.0/design/_index.md @@ -0,0 +1,6 @@ +--- +title: "Design" +weight: 1 +description: This section includes Designs of Envoy Gateway. +--- + diff --git a/site/content/en/v0.5.0/design/accesslog.md b/site/content/en/v0.5.0/design/accesslog.md new file mode 100644 index 00000000000..46f39afa8ef --- /dev/null +++ b/site/content/en/v0.5.0/design/accesslog.md @@ -0,0 +1,237 @@ +--- +title: "Observability: Accesslog" +--- + +## Overview + +Envoy supports extensible accesslog to different sinks, File, gRPC etc. Envoy supports customizable access log formats using predefined fields as well as arbitrary HTTP request and response headers. Envoy supports several built-in access log filters and extension filters that are registered at runtime. + +Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementers such as Envoy Gateway to expose features. Since accesslog is not covered by `Core` or `Extended` APIs, EG should provide an easy to config access log formats and sinks per `EnvoyProxy`. + +## Goals + +- Support send accesslog to `File` or `OpenTelemetry` backend +- TODO: Support access log filters base on [CEL](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/filters/cel/v3/cel.proto#extension-envoy-access-loggers-extension-filters-cel) expression + +## Non-Goals + +- Support non-CEL filters, e.g. `status_code_filter`, `response_flag_filter` +- Support [HttpGrpcAccessLogConfig](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-httpgrpcaccesslogconfig) or [TcpGrpcAccessLogConfig](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-tcpgrpcaccesslogconfig) + +## Use-Cases + +- Configure accesslog for a `EnvoyProxy` to `File` +- Configure accesslog for a `EnvoyProxy` to `OpenTelemetry` backend +- Configure multi accesslog providers for a `EnvoyProxy` + +### ProxyAccessLog API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/accesslogging_types.go" +type ProxyAccessLog struct { + // Disable disables access logging for managed proxies if set to true. + Disable bool `json:"disable,omitempty"` + // Settings defines accesslog settings for managed proxies. + // If unspecified, will send default format to stdout. + // +optional + Settings []ProxyAccessLogSetting `json:"settings,omitempty"` +} + +type ProxyAccessLogSetting struct { + // Format defines the format of accesslog. + Format ProxyAccessLogFormat `json:"format"` + // Sinks defines the sinks of accesslog. + // +kubebuilder:validation:MinItems=1 + Sinks []ProxyAccessLogSink `json:"sinks"` +} + +type ProxyAccessLogFormatType string + +const ( + // ProxyAccessLogFormatTypeText defines the text accesslog format. + ProxyAccessLogFormatTypeText ProxyAccessLogFormatType = "Text" + // ProxyAccessLogFormatTypeJSON defines the JSON accesslog format. + ProxyAccessLogFormatTypeJSON ProxyAccessLogFormatType = "JSON" + // TODO: support format type "mix" in the future. +) + +// ProxyAccessLogFormat defines the format of accesslog. +// +union +type ProxyAccessLogFormat struct { + // Type defines the type of accesslog format. + // +kubebuilder:validation:Enum=Text;JSON + // +unionDiscriminator + Type ProxyAccessLogFormatType `json:"type,omitempty"` + // Text defines the text accesslog format, following Envoy accesslog formatting, + // empty value results in proxy's default access log format. + // It's required when the format type is "Text". + // Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. + // The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. + // +optional + Text *string `json:"text,omitempty"` + // JSON is additional attributes that describe the specific event occurrence. + // Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) + // can be used as values for fields within the Struct. + // It's required when the format type is "JSON". + // +optional + JSON map[string]string `json:"json,omitempty"` +} + +type ProxyAccessLogSinkType string + +const ( + // ProxyAccessLogSinkTypeFile defines the file accesslog sink. + ProxyAccessLogSinkTypeFile ProxyAccessLogSinkType = "File" + // ProxyAccessLogSinkTypeOpenTelemetry defines the OpenTelemetry accesslog sink. + ProxyAccessLogSinkTypeOpenTelemetry ProxyAccessLogSinkType = "OpenTelemetry" +) + +type ProxyAccessLogSink struct { + // Type defines the type of accesslog sink. + // +kubebuilder:validation:Enum=File;OpenTelemetry + Type ProxyAccessLogSinkType `json:"type,omitempty"` + // File defines the file accesslog sink. + // +optional + File *FileEnvoyProxyAccessLog `json:"file,omitempty"` + // OpenTelemetry defines the OpenTelemetry accesslog sink. + // +optional + OpenTelemetry *OpenTelemetryEnvoyProxyAccessLog `json:"openTelemetry,omitempty"` +} + +type FileEnvoyProxyAccessLog struct { + // Path defines the file path used to expose envoy access log(e.g. /dev/stdout). + // Empty value disables accesslog. + Path string `json:"path,omitempty"` +} + +// TODO: consider reuse ExtensionService? +type OpenTelemetryEnvoyProxyAccessLog struct { + // Host define the extension service hostname. + Host string `json:"host"` + // Port defines the port the extension service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + // Resources is a set of labels that describe the source of a log entry, including envoy node info. + // It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). + // +optional + Resources map[string]string `json:"resources,omitempty"` + + // TODO: support more OpenTelemetry accesslog options(e.g. TLS, auth etc.) in the future. +} +``` + +### Example + +1. The following is an example to disable access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/disable-accesslog.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: disable-accesslog + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + disable: true +``` + +2. The following is an example with text format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/text-accesslog.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: text-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: File + file: + path: /dev/stdout +``` + +1. The following is an example with json format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/json-accesslog.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: json-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + json: + status: "%RESPONSE_CODE%" + message: "%LOCAL_REPLY_BODY%" + sinks: + - type: File + file: + path: /dev/stdout +``` + +1. The following is an example with OpenTelemetry format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/otel-accesslog.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: otel-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" +``` + +1. The following is an example of sending same format to different sinks. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/multi-sinks.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: multi-sinks + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: File + file: + path: /dev/stdout + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" +``` diff --git a/site/content/en/v0.5.0/design/bootstrap.md b/site/content/en/v0.5.0/design/bootstrap.md new file mode 100644 index 00000000000..9a8f0c789ef --- /dev/null +++ b/site/content/en/v0.5.0/design/bootstrap.md @@ -0,0 +1,381 @@ +--- +title: "Bootstrap Design" +--- + +## Overview + +[Issue 31][] specifies the need for allowing advanced users to specify their custom +Envoy Bootstrap configuration rather than using the default Bootstrap configuration +defined in Envoy Gateway. This allows advanced users to extend Envoy Gateway and +support their custom use cases such setting up tracing and stats configuration +that is not supported by Envoy Gateway. + +## Goals + +* Define an API field to allow a user to specify a custom Bootstrap +* Provide tooling to allow the user to generate the default Bootstrap configuration + as well as validate their custom Bootstrap. + +## Non Goals + +* Allow user to configure only a section of the Bootstrap + +## API + +Leverage the existing [EnvoyProxy][] resource which can be attached to the [GatewayClass][] using +the [parametersRef][] field, and define a `Bootstrap` field within the resource. If this field is set, +the value is used as the Bootstrap configuration for all managed Envoy Proxies created by Envoy Gateway. + +```go +// EnvoyProxySpec defines the desired state of EnvoyProxy. +type EnvoyProxySpec struct { + ...... + // Bootstrap defines the Envoy Bootstrap as a YAML string. + // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + // to learn more about the syntax. + // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration + // set by Envoy Gateway. + // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources + // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. + // Backward compatibility across minor versions is not guaranteed. + // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default + // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. + // + // +optional + Bootstrap *string `json:"bootstrap,omitempty"` +} +``` + +## Tooling + +A CLI tool `egctl x translate` will be provided to the user to help generate a working Bootstrap configuration. +Here is an example where a user inputs a `GatewayClass` and the CLI generates the `EnvoyProxy` resource with the `Bootstrap` field populated. + +``` +cat < /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: {} +EOF +``` + +This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. + +The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: + foo: bar +EOF +``` + +__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for +illustration purposes only. + +The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the +File provider: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: File + file: + foo: bar +EOF +``` + +__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration +purposes only. + +Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use +default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to +manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: foo +``` + +With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: + +```shell +$ ./envoy-gateway +``` + +## Data Plane API + +The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. +Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through +`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure +configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: + +* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane + infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. +* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: + > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the + > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are + > not propagated down to existing Gateways. + +The initial `EnvoyProxy` API: + +```go +// gateway/api/config/v1alpha1/envoyproxy.go + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyProxy is the Schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvoyProxySpec `json:"spec,omitempty"` + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure +// configuration. +type EnvoyProxySpec struct { + // Undefined by this design spec. +} + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. +type EnvoyProxyStatus struct { + // Undefined by this design spec. +} +``` + +The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use +cases are better understood. + +### Data Plane Configuration + +GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is +running with the Kubernetes provider. + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration +parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening +on port 80. + +The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer +service: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + name: example-config + group: config.gateway.envoyproxy.io + kind: EnvoyProxy +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: example-config +spec: + networkPublishing: + type: ClusterIPService +``` + +__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. + +[issue_51]: https://github.com/envoyproxy/gateway/issues/51 +[design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md +[gw_api]: https://gateway-api.sigs.k8s.io/ +[gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/site/content/en/v0.5.0/design/egctl.md b/site/content/en/v0.5.0/design/egctl.md new file mode 100644 index 00000000000..0f67d99f100 --- /dev/null +++ b/site/content/en/v0.5.0/design/egctl.md @@ -0,0 +1,59 @@ +--- +title: "egctl Design" +--- + +## Motivation + +EG should provide a command line tool with following capabilities: + +- Collect configuration from envoy proxy and gateway +- Analyse system configuration to diagnose any issues in envoy gateway + +This tool is named `egctl`. + +## Syntax + +Use the following syntax to run `egctl` commands from your terminal window: + +```console +egctl [command] [entity] [name] [flags] +``` + +where `command`, `name`, and `flags` are: + +* `command`: Specifies the operation that you want to perform on one or more resources, + for example `config`, `version`. + +* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. + +* `name`: Specifies the name of the specified instance. + +* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. + +If you need help, run `egctl help` from the terminal window. + +## Operation + +The following table includes short descriptions and the general syntax for all the `egctl` operations: + +| Operation | Syntax | Description | +| --------------| -------------------------------- | -------------------------------------------------------------------------------------| +| `version` | `egctl version` | Prints out build version information. | +| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | +| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | +| `experimental`| `egctl experimental` | Subcommand for experimental features. These do not guarantee backwards compatibility | + +## Examples + +Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: + +```console +# Retrieve all information about proxy configuration from envoy +egctl config envoy-proxy all + +# Retrieve listener information about proxy configuration from envoy +egctl config envoy-proxy listener + +# Retrieve information about envoy gateway +egctl config envoy-gateway +``` diff --git a/site/content/en/v0.5.0/design/envoy-patch-policy.md b/site/content/en/v0.5.0/design/envoy-patch-policy.md new file mode 100644 index 00000000000..d34937d05ef --- /dev/null +++ b/site/content/en/v0.5.0/design/envoy-patch-policy.md @@ -0,0 +1,176 @@ +--- +title: "EnvoyPatchPolicy" +--- + +## Overview + +This design introduces the `EnvoyPatchPolicy` API allowing users to modify the generated Envoy xDS Configuration +that Envoy Gateway generates before sending it to Envoy Proxy. + +Envoy Gateway allows users to configure networking and security intent using the +upstream [Gateway API][] as well as implementation specific [Extension APIs][] defined in this project +to provide a more batteries included experience for application developers. +* These APIs are an abstracted version of the underlying Envoy xDS API to provide a better user experience for the application developer, exposing and setting only a subset of the fields for a specific feature, sometimes in a opinionated way (e.g [RateLimit][]) +* These APIs do not expose all the features capabilities that Envoy has either because these features are desired but the API +is not defined yet or the project cannot support such an extensive list of features. +To alleviate this problem, and provide an interim solution for a small section of advanced users who are well versed in +Envoy xDS API and its capabilities, this API is being introduced. + +## Goals +* Add an API allowing users to modify the generated xDS Configuration + +## Non Goals +* Support multiple patch mechanisims + +## Implementation +`EnvoyPatchPolicy` is a [Direct Policy Attachment][] type API that can be used to extend [Gateway API][] +Modifications to the generated xDS configuration can be provided as a JSON Patch which is defined in +[RFC 6902][]. This patching mechanism has been adopted in [Kubernetes][] as well as [Kustomize][] to update +resource objects. + +### Example +Here is an example highlighting how a user can configure global ratelimiting using an external rate limit service using this API. + +``` +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + name: ratelimit-patch-policy + namespace: default +spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + # The listener name is of the form // + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eag-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + # The route name is of the form // + name: default/eg/http + operation: + op: add + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 +``` + + +## Verification +* Offline - Leverage [egctl x translate][] to ensure that the `EnvoyPatchPolicy` can be successfully applied and the desired +output xDS is created. +* Runtime - Use the `Status` field within `EnvoyPatchPolicy` to highlight whether the patch was applied successfully or not. + +## State of the World +* Istio - Supports the [EnvoyFilter][] API which allows users to customize the output xDS using patches and proto based merge +semantics. + +## Design Decisions +* This API will only support a single `targetRef` and can bind to only a `Gateway` resource. This simplifies reasoning of how +patches will work. +* This API will always be an experimental API and cannot be graduated into a stable API because Envoy Gateway cannot garuntee + * that the naming scheme for the generated resources names will not change across releases + * that the underlying Envoy Proxy API will not change across releases +* This API needs to be explicitly enabled using the [EnvoyGateway][] API + +## Open Questions +* Should the value only support JSON or YAML as well (which is a JSON superset) ? + +## Alternatives +* Users can customize the Envoy [Bootstrap configuration using EnvoyProxy API][] and provide static xDS configuration. +* Users can extend functionality by [Extending the Control Plane][] and adding gRPC hooks to modify the generated xDS configuration. + + + +[Direct Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/#direct-policy-attachment +[RFC 6902]: https://datatracker.ietf.org/doc/html/rfc6902 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[Kubernetes]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ +[Kustomize]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md +[Extension APIs]: https://gateway.envoyproxy.io/latest/api/extension_types.html +[RateLimit]: https://gateway.envoyproxy.io/latest/user/rate-limit.html +[EnvoyGateway]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoygateway +[Extending the Control Plane]: https://gateway.envoyproxy.io/latest/design/extending-envoy-gateway.html +[EnvoyFilter]: https://istio.io/latest/docs/reference/config/networking/envoy-filter +[egctl x translate]: https://gateway.envoyproxy.io/latest/user/egctl.html#egctl-experimental-translate +[Bootstrap configuration using EnvoyProxy API]: https://gateway.envoyproxy.io/latest/user/customize-envoyproxy.html#customize-envoyproxy-bootstrap-config diff --git a/site/content/en/v0.5.0/design/extending-envoy-gateway.md b/site/content/en/v0.5.0/design/extending-envoy-gateway.md new file mode 100644 index 00000000000..04e211fa537 --- /dev/null +++ b/site/content/en/v0.5.0/design/extending-envoy-gateway.md @@ -0,0 +1,327 @@ +--- +title: "Envoy Gateway Extensions Design" +--- + +As outlined in the [official goals][] for the Envoy Gateway project, one of the main goals is to "provide a common foundation for vendors to build value-added products +without having to re-engineer fundamental interactions". Development of the Envoy Gateway project has been focused on developing the core features for the project and +Kubernetes Gateway API conformance. This system focuses on the “common foundation for vendors” component by introducing a way for vendors to extend Envoy Gateway. + +To meaningfully extend Envoy Gateway and provide additional features, Extensions need to be able to introduce their own custom resources and have a high level of control +over the configuration generated by Envoy Gateway. Simply applying some static xDS configuration patches or relying on the existing Gateway API resources are both insufficient on their own +as means to add larger features that require dynamic user-configuration. + +As an example, an extension developer may wish to provide their own out-of-the-box authentication filters that require configuration from the end-user. This is a scenario where the ability to introduce +custom resources and attach them to [HTTPRoute][]s as an [ExtensionRef][] is necessary. Providing the same feature through a series of xDS patch resources would be too cumbersome for many end-users that want to avoid +that level of complexity when managing their clusters. + +## Goals + +- Provide a foundation for extending the Envoy Gateway control plane +- Allow Extension Developers to introduce their own custom resources for extending the Gateway-API via [ExtensionRefs][], [policyAttachments][] (future) and [backendRefs][] (future). +- Extension developers should **NOT** have to maintain a custom fork of Envoy Gateway +- Provide a system for extending Envoy Gateway which allows extension projects to ship updates independent of Envoy Gateway's release schedule +- Modify the generated Envoy xDS config +- Setup a foundation for the initial iteration of Extending Envoy Gateway +- Allow an Extension to hook into the infra manager pipeline (future) + +## Non-Goals + +- The initial design does not capture every hook that Envoy Gateway will eventually support. +- Extend [Gateway API Policy Attachments][]. At some point, these will be addressed using this extension system, but the initial implementation omits these. +- Support multiple extensions at the same time. Due to the fact that extensions will be modifying xDS resources after they are generated, handling the order of extension execution for each individual hook point is a challenge. Additionally, there is no +real way to prevent one extension from overwriting or breaking modifications to xDS resources that were made by another extension that was executed first. + +## Overview + +Envoy Gateway can be extended by vendors by means of an extension server developed by the vendor and deployed alongside Envoy Gateway. +An extension server can make use of one or more pre/post hooks inside Envoy Gateway before and after its major components (translator, etc.) to allow the extension to modify the data going into or coming out of these components. +An extension can be created external to Envoy Gateway as its own Kubernetes deployment or loaded as a sidecar. gRPC is used for the calls between Envoy Gateway and an extension. In the hook call, Envoy Gateway sends data as well +as context information to the extension and expects a reply with a modified version of the data that was sent to the extension. Since extensions fundamentally alter the logic and data that Envoy Gateway provides, Extension projects assume responsibility for any bugs and issues +they create as a direct result of their modification of Envoy Gateway. + +## Diagram + +![Architecture](/img/extension-example.png) + +## Registering Extensions in Envoy Gateway + +Information about the extension that Envoy Gateway needs to load is configured in the Envoy Gateway config. + +An example configuration: + +```yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +extensionManager: + resources: + - group: example.myextension.io + version: v2 + kind: OAuth2Filter + hooks: + xdsTranslator: + post: + - Route + - VirtualHost + - HTTPListener + - Translation + service: + host: my-extension.example + port: 443 + tls: + certificateRef: + name: my-secret + namespace: default +``` + +An extension must supply connection information in the `extension.service` field so that Envoy Gateway can communicate with the extension. The `tls` configuration is optional. + +If the extension wants Envoy Gateway to watch resources for it then the extension must configure the optional `extension.resources` field and supply a list of: + +- `group`: the API group of the resource +- `version`: the API version of the resource +- `kind`: the Kind of resource + +The extension can configure the `extensionManager.hooks` field to specify which hook points it would like to support. If a given hook is not listed here then it will not be executed even +if the extension is configured properly. This allows extension developers to only opt-in to the hook points they want to make use of. + +This configuration is required to be provided at bootstrap and modifying the registered extension during runtime is not currently supported. +Envoy Gateway will keep track of the registered extension and its API `groups` and `kinds` when processing Gateway API resources. + +## Extending Gateway API and the Data Plane + +Envoy Gateway manages [Envoy][] deployments, which act as the data plane that handles actual user traffic. Users configure the data plane using the K8s Gateway API resources which Envoy +Gateway converts into [Envoy specific configuration (xDS)][] to send over to Envoy. + +Gateway API offers [ExtensionRef filters][] and [Policy Attachments][] as extension points for implementers to use. Envoy Gateway extends the Gateway API using these extension points to provide support for [rate limiting][] +and [authentication][] native to the project. The initial design of Envoy Gateway extensions will primarily focus on ExtensionRef filters so that extension developers can reference their own resources as HTTP Filters in the same way +that Envoy Gateway has native support for rate limiting and authentication filters. + +When Envoy Gateway encounters an [HTTPRoute][] or [GRPCRoute][] that has an `ExtensionRef` `filter` with a `group` and `kind` that Envoy Gateway does not support, it will first +check the registered extension to determine if it supports the referenced object before considering it a configuration error. + +This allows users to be able to reference additional filters provided by their Envoy Gateway Extension, in their `HTTPRoute`s / `GRPCRoute`s: + +```yaml +apiVersion: example.myextension.io/v1alpha1 +kind: OAuth2Filter +metadata: + name: oauth2-filter +spec: + ... + +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - clientSelectors: + - path: + type: PathPrefix + value: / + filters: + - type: ExtensionRef + extensionRef: + group: example.myextension.io + kind: OAuth2Filter + name: oauth2-filter + backendRefs: + - name: backend + port: 3000 +``` + +In order to enable the usage of new resources introduced by an extension for translation and xDS modification, Envoy Gateway provides hook points within the translation pipeline, where it calls out to the extension service registered in the [EnvoyGateway config][] +if they specify an `group` that matches the `group` of an `ExtensionRef` filter. The extension will then be able to modify the xDS that Envoy Gateway generated and send back the +modified configuration. If an extension is not registered or if the registered extension does not specify support for the `group` of an `ExtensionRef` filter then Envoy Gateway will treat it as an unknown resource +and provide an error to the user. + +**Note:** Currently (as of [v1beta1][]) Gateway API does not provide a means to specify the namespace or version of an object referenced as an `ExtensionRef`. The extension mechanism will assume that +the namespace of any `ExtensionRef` is the same as the namespace of the `HTTPRoute` or `GRPCRoute` it is attached to rather than treating the `name` field of an `ExtensionRef` as a `name.namespace` string. +If Gateway API adds support for these fields then the design of the Envoy Gateway extensions will be updated to support them. + +## Watching New Resources + +Envoy Gateway will dynamically create new watches on resources introduced by the registered Extension. It does so by using the [controller-runtime][] to create new watches on [Unstructured][] resources that match the `version`s, `group`s, and `kind`s that the +registered extension configured. When communicating with an extension, Envoy Gateway sends these Unstructured resources over to the extension. This eliminates the need for the extension to create its own watches which would have a strong chance of creating race conditions and reconciliation loops when resources change. When an extension receives the Unstructured resources from Envoy Gateway it can perform its own type validation on them. Currently we make the simplifying assumption that the registered extension's `Kinds` are filters referenced by `extensionRef` in `HTTPRouteFilter`s . Support for Policy attachments will be introduced at a later time. + +## xDS Hooks API + +Envoy Gateway supports the following hooks as the initial foundation of the Extension system. Additional hooks can be developed using this extension system at a later point as new use-cases and needs are discovered. The primary iteration of the extension hooks +focuses solely on the modification of xDS resources. + +### Route Modification Hook + +The [Route][] level Hook provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized. +Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the +Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to ext_authz filters. +The Post Route Modify hook also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that created this xDS route +This hook is always executed when an extension is loaded that has added `Route` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`, and only on Routes which were generated from an HTTPRoute that uses extension resources as externalRef filters. + +```go +// PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified +message PostRouteModifyRequest { + envoy.config.route.v3.Route route = 1; + PostRouteExtensionContext post_route_context = 2; +} + +// RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway +// additional context information can be added to this message as more use-cases are discovered +message PostRouteExtensionContext { + // Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute + repeated ExtensionResource extension_resources = 1; + + // hostnames are the fully qualified domain names attached to the HTTPRoute + repeated string hostnames = 2; +} + +// ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter +// extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension +// can marshal the bytes from this resource back into an unstructured.Unstructured and then +// perform type checking to obtain the resource. +message ExtensionResource { + bytes unstructured_bytes = 1; +} + +// PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent +// If an extension returns a nil Route then it will not be modified +message PostRouteModifyResponse { + envoy.config.route.v3.Route route = 1; +} +``` + +### VirtualHost Modification Hook + +The [VirtualHost][] Hook provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized. +An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway. +This hook is always executed when an extension is loaded that has added `VirtualHost` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. +An extension may return nil to not make any changes to the VirtualHost. + +```protobuf +// PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified +message PostVirtualHostModifyRequest { + envoy.config.route.v3.VirtualHost virtual_host = 1; + PostVirtualHostExtensionContext post_virtual_host_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostVirtualHostExtensionContext {} + +// PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent +// If an extension returns a nil Virtual Host then it will not be modified +message PostVirtualHostModifyResponse { + envoy.config.route.v3.VirtualHost virtual_host = 1; +} +``` + +### HTTP Listener Modification Hook + +The HTTP [Listener][] modification hook is the broadest xDS modification Hook available and allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized. +This hook is always executed when an extension is loaded that has added `HTTPListener` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. An extension may return nil +in order to not make any changes to the Listener. + +```protobuf +// PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified +message PostHTTPListenerModifyRequest { + envoy.config.listener.v3.Listener listener = 1; + PostHTTPListenerExtensionContext post_listener_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostHTTPListenerExtensionContext {} + +// PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent +// If an extension returns a nil Listener then it will not be modified +message PostHTTPListenerModifyResponse { + envoy.config.listener.v3.Listener listener = 1; +} +``` + +### Post xDS Translation Modify Hook + +The Post Translate Modify hook allows an extension to modify the clusters and secrets in the xDS config. +This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than +using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed. +An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension. +The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets +This hook is always executed when an extension is loaded that has added `Translation` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. + +```protobuf +// PostTranslateModifyRequest currently sends only clusters and secrets to an extension. +// The extension is free to add/modify/remove the resources it received. +message PostTranslateModifyRequest { + PostTranslateExtensionContext post_translate_context = 1; + repeated envoy.config.cluster.v3.Cluster clusters = 2; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3; +} + +// PostTranslateModifyResponse is the expected response from an extension and contains +// the full list of xDS clusters and secrets to be used for the xDS config. +message PostTranslateModifyResponse { + repeated envoy.config.cluster.v3.Cluster clusters = 1; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 2; +} +``` + +### Extension Service + +Currently, an extension must implement all of the following hooks although it may return the input(s) it received +if no modification of the resource is desired. A future expansion of the extension hooks will allow an Extension to specify +with config which Hooks it would like to "subscribe" to and which Hooks it does not wish to support. These specific Hooks were chosen +in order to provide extensions with the ability to have both broad and specific control over xDS resources and to minimize the amount of data being sent. + +```protobuf +service EnvoyGatewayExtension { + rpc PostRouteModify (PostRouteModifyRequest) returns (PostRouteModifyResponse) {}; + rpc PostVirtualHostModify(PostVirtualHostModifyRequest) returns (PostVirtualHostModifyResponse) {}; + rpc PostHTTPListenerModify(PostHTTPListenerModifyRequest) returns (PostHTTPListenerModifyResponse) {}; + rpc PostTranslateModify(PostTranslateModifyRequest) returns (PostTranslateModifyResponse) {}; +} +``` + +## Design Decisions + +- Envoy Gateway watches new custom resources introduced by a loaded extension and passes the resources back to the extension when they are used. + - This decision was made to solve the problem about how resources introduced by an extension get watched. If an extension server watches its own resources then it would need some way to trigger an Envoy Gateway reconfigure when a resource that Envoy Gateway is not watching gets updated. Having Envoy Gateway watch all resources removes any concern about creating race confitions or reconcile loops that would result from Envoy Gateway and the extension server both having so much separate state that needs to be synchronized. +- The Extension Server takes ownership of producing the correct xDS configuration in the hook responses +- The Extension Server will be responsible for ensuring the performance of the hook processing time +- The Post xDS level gRPC hooks all currently send a context field even though it contains nothing for several hooks. These fields exist so that they can be updadated in the future to pass +additional information to extensions as new use-cases and needs are discovered. +- The initial design supplies the scaffolding for both "pre xDS" and "post xDS" hooks. Only the post hooks are currently implemented which operate on xDS resources after they have been generated. +The pre hooks will be implemented at a later date along with one or more hooks in the infra manager. The infra manager level hook(s) will exist to power use-cases such as dynamically creating Deployments/Services for the extension the +whenever Envoy Gateway creates an instance of Envoy Proxy. An extension developer might want to take advantage of this functionality to inject a new authorization service as a sidecar on the Envoy Proxy deployment for reduced latency. +- Multiple extensions are not be supported at the same time. Preventing conflict between multiple extensions that are mangling xDS resources is too difficult to ensure compatibility with and is likely to only generate issues. + +## Known Challenges + +Extending Envoy Gateway by using an external extension server which makes use of hook points in Envoy Gateway does comes with a few trade-offs. One known trade-off is the impact of the time that it takes for the hook calls to be executed. Since an extension would make use of hook points in Envoy Gateway that use gRPC for communication, the time it takes to perform these requests could become a concern for some extension developers. One way to minimize the request time of the hook calls is to load the extension server as a sidecar to Envoy Gateway to minimize the impact of networking on the hook calls. + +[official goals]: https://github.com/envoyproxy/gateway/blob/main/GOALS.md#extensibility +[ExtensionRef filters]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference +[ExtensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.LocalObjectReference +[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendObjectReference +[Gateway API Policy attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Policy Attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[policyAttachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Envoy]: https://www.envoyproxy.io/ +[Envoy specific configuration (xDS)]: https://www.envoyproxy.io/docs/envoy/v1.25.1/configuration/configuration +[v1beta1]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1 +[rate limiting]: https://gateway.envoyproxy.io/v0.3.0/user/rate-limit.html +[authentication]: https://gateway.envoyproxy.io/v0.3.0/user/authn.html +[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[EnvoyGateway config]: https://gateway.envoyproxy.io/v0.3.0/api/config_types.html#envoygateway +[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime +[Unstructured]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured +[Listener]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener +[VirtualHost]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost +[Route]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-route diff --git a/site/content/en/v0.5.0/design/gatewayapi-translator.md b/site/content/en/v0.5.0/design/gatewayapi-translator.md new file mode 100644 index 00000000000..b83ed82588d --- /dev/null +++ b/site/content/en/v0.5.0/design/gatewayapi-translator.md @@ -0,0 +1,253 @@ +--- +title: "Gateway API Translator Design" +weight: 4 +--- + +The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate +Representation (IR). + +## Assumptions + +Initially target core conformance features only, to be followed by extended conformance features. + +## Inputs and Outputs + +The main inputs to the Gateway API translator are: + +- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. + +__Note:__ ReferenceGrant is not fully implemented as of v0.2. + +The outputs of the Gateway API translator are: + +- Xds and Infra Internal Representations (IRs). +- Status updates for GatewayClass, Gateways, HTTPRoutes + +## Listener Compatibility + +Envoy Gateway follows Gateway API listener compatibility spec: +> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. + +__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. + +### Listener Compatibility Examples + +#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1beta1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +## Computing Status + +Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway +must compute the appropriate status fields and conditions for managed resources. + +Status is computed and set for: + +- The managed GatewayClass (`gatewayclass.status.conditions`). +- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the + Envoy Deployment and Service status are also included to calculate Gateway status. +- Listeners for each Gateway (`gateway.status.listeners`). +- The ParentRef for each Route (`route.status.parents`). + +The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to +the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and +updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to +update resource status on the Kubernetes API server. + +## Outline + +The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway +API resources. + +1. Process Gateway Listeners + - Validate unique hostnames, ports, and protocols. + - Validate and compute supported kinds. + - Validate allowed namespaces (validate selector if specified). + - Validate TLS fields if specified, including resolving referenced Secrets. + +2. Process HTTPRoutes + - foreach route rule: + - compute matches + - [core] path exact, path prefix + - [core] header exact + - [extended] query param exact + - [extended] HTTP method + - compute filters + - [core] request header modifier (set/add/remove) + - [core] request redirect (hostname, statuscode) + - [extended] request mirror + - compute backends + - [core] Kubernetes services + - foreach route parent ref: + - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) + - foreach matching listener: + - foreach hostname intersection with route: + - add each computed route rule to host + +## Context Structs + +To help store, access and manipulate information as it's processed during the translation process, a set of context +structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support +processing. + +`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. + +```go +type GatewayContext struct { + // The managed Gateway + *v1beta1.Gateway + + // A list of Gateway ListenerContexts. + listeners []*ListenerContext +} +``` + +`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on +the associated Gateway. + +```go +type ListenerContext struct { + // The Gateway listener. + *v1beta1.Listener + + // The Gateway this Listener belongs to. + gateway *v1beta1.Gateway + + // An index used for managing this listener in the list of Gateway listeners. + listenerStatusIdx int + + // Only Routes in namespaces selected by the selector may be attached + // to the Gateway this listener belongs to. + namespaceSelector labels.Selector + + // The TLS Secret for this Listener, if applicable. + tlsSecret *v1.Secret +} +``` + +`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. + +```go +type RouteContext interface { + client.Object + + // GetRouteType returns the Kind of the Route object, HTTPRoute, + // TLSRoute, TCPRoute, UDPRoute etc. + GetRouteType() string + + // GetHostnames returns the hosts targeted by the Route object. + GetHostnames() []string + + // GetParentReferences returns the ParentReference of the Route object. + GetParentReferences() []v1beta1.ParentReference + + // GetRouteParentContext returns RouteParentContext by using the Route + // objects' ParentReference. + GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext +} +``` + +[message bus]: watching.md diff --git a/site/content/en/v0.5.0/design/goals.md b/site/content/en/v0.5.0/design/goals.md new file mode 100644 index 00000000000..fd38b2004c6 --- /dev/null +++ b/site/content/en/v0.5.0/design/goals.md @@ -0,0 +1,91 @@ +--- +title: "Goals" +weight: 1 +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption +through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing +use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer +fundamental interactions. + +## Objectives + +### Expressive API + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +### Batteries included + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +### All environments + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API][]. +Additional goals include multi-cluster support and various runtime environments. + +### Extensibility + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +## Non-objectives + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +## Personas + +_In order of priority_ + +### 1. Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +### 2. Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/v0.5.0/design/local-envoy-gateway.md b/site/content/en/v0.5.0/design/local-envoy-gateway.md new file mode 100644 index 00000000000..aad0dc5f6f2 --- /dev/null +++ b/site/content/en/v0.5.0/design/local-envoy-gateway.md @@ -0,0 +1,52 @@ +--- +title: "Running Envoy Gateway locally" +--- + +## Overview + +Today, Envoy Gateway runs only on Kubernetes. This is an ideal solution +when the applications are running in Kubernetes. +However there might be cases when the applications are running on the host which would +require Envoy Gateway to run locally. + +## Goals + +* Define an API to allow Envoy Gateway to retrieve configuration while running locally. +* Define an API to allow Envoy Gateway to deploy the managed Envoy Proxy fleet on the host +machine. + +## Non Goals + +* Support multiple ways to retrieve configuration while running locally. +* Support multiple ways to deploy the Envoy Proxy fleet locally on the host. + +## API + +* The `provider` field within the `EnvoyGateway` configuration only supports +`Kubernetes` today which provides two features - the ability to retrieve +resources from the Kubernetes API Server as well as deploy the managed +Envoy Proxy fleet on Kubernetes. +* This document proposes adding a new top level `provider` type called `Custom` +with two fields called `resource` and `infrastructure` to allow the user to configure +the sub providers for providing resource configuration and an infrastructure to deploy +the Envoy Proxy data plane in. +* A `File` resource provider will be introduced to enable retrieveing configuration locally +by reading from the configuration from a file. +* A `Host` infrastructure provider will be introduced to allow Envoy Gateway to spawn a +Envoy Proxy child process on the host. + +Here is an example configuration + +``` +provider: + type: Custom + custom: + resource: + type: File + file: + paths: + - "config.yaml" + infrastructure: + type: Host + host: {} +``` diff --git a/site/content/en/v0.5.0/design/metrics.md b/site/content/en/v0.5.0/design/metrics.md new file mode 100644 index 00000000000..b226db6a002 --- /dev/null +++ b/site/content/en/v0.5.0/design/metrics.md @@ -0,0 +1,108 @@ +--- +title: "Observability: Metrics" +--- + +## Overview + +Envoy provide robust platform for metrics, Envoy support three different kinds of stats: counter, gauges, histograms. + +Envoy enables prometheus format output via the `/stats/prometheus` [admin endpoint](https://www.envoyproxy.io/docs/envoy/latest/operations/admin). + +Envoy support different kinds of sinks, but EG will only support [Open Telemetry sink](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto). + +Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementers such as Envoy Gateway to expose features. Since metrics is not covered by `Core` or `Extended` APIs, EG should provide an easy to config metrics per `EnvoyProxy`. + +## Goals + +- Support expose metrics in prometheus way(reuse probe port). +- Support Open Telemetry stats sink. + +## Non-Goals + +- Support other stats sink. + +## Use-Cases + +- Enable prometheus metric +- Push metrics via Open Telemetry Sink +- TODO: Customize histogram buckets of target metric +- TODO: Support stats matcher + +### ProxyMetric API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/metric_types.go" +type ProxyMetrics struct { + // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. + Prometheus *PrometheusProvider `json:"prometheus,omitempty"` + // Sinks defines the metric sinks where metrics are sent to. + Sinks []MetricSink `json:"sinks,omitempty"` +} + +type MetricSinkType string + +const ( + MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" +) + +type MetricSink struct { + // Type defines the metric sink type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + OpenTelemetry *OpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type OpenTelemetrySink struct { + // Host define the service hostname. + Host string `json:"host"` + // Port defines the port the service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + + // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig +} + +type PrometheusProvider struct { +} +``` + +### Example + +1. The following is an example to enable prometheus metric. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/prometheus.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: prometheus + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + prometheus: {} +``` + +1. The following is an example to send metric via Open Telemetry sink. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/otel-sink.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: otel-sink + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 +``` diff --git a/site/content/en/v0.5.0/design/pprof.md b/site/content/en/v0.5.0/design/pprof.md new file mode 100644 index 00000000000..68a64695dd5 --- /dev/null +++ b/site/content/en/v0.5.0/design/pprof.md @@ -0,0 +1,51 @@ +--- +title: "Add Pprof support in Envoy Gateway" +--- + +## Overview + +Envoy Gateway exposes endpoints at `localhost:8899/debug/pprof` to run Golang profiles to aid in live debugging. The endpoints are equivalent to those found in the http/pprof package. `/debug/pprof/` returns an HTML page listing the available profiles. + +## Goals + +* Add Debug Pprof support to Envoy Gateway control plane. +* Define an API to allow Envoy Gateway to custom debug server configuration. + +The following are the different types of profiles end-user can run: + +PROFILE | FUNCTION +-- | -- +/debug/pprof/allocs | Returns a sampling of all past memory allocations. +/debug/pprof/block | Returns stack traces of goroutines that led to blocking on synchronization primitives. +/debug/pprof/cmdline | Returns the command line that was invoked by the current program. +/debug/pprof/goroutine | Returns stack traces of all current goroutines. +/debug/pprof/heap | Returns a sampling of memory allocations of live objects. +/debug/pprof/mutex | Returns stack traces of goroutines holding contended mutexes. +/debug/pprof/profile | Returns pprof-formatted cpu profile. You can specify the duration using the seconds GET parameter. The default duration is 30 seconds. +/debug/pprof/symbol | Returns the program counters listed in the request. +/debug/pprof/threadcreate | Returns stack traces that led to creation of new OS threads. +/debug/pprof/trace | Returns the execution trace in binary form. You can specify the duration using the seconds GET parameter. The default duration is 1 second. + +## Non Goals + +## API + +* Add `admin` field in EnvoyGateway config. +* Add `debug` field under `admin` field. +* Add `enable`, `port` and `host` under `address` field. + +Here is an example configuration + +``` yaml +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +gateway: + controllerName: "gateway.envoyproxy.io/gatewayclass-controller" +kind: EnvoyGateway +provider: + type: "Kubernetes" +admin: + debug: true + address: + port: 8899 + host: "127.0.0.1" +``` diff --git a/site/content/en/v0.5.0/design/rate-limit.md b/site/content/en/v0.5.0/design/rate-limit.md new file mode 100644 index 00000000000..5a8288a79d1 --- /dev/null +++ b/site/content/en/v0.5.0/design/rate-limit.md @@ -0,0 +1,419 @@ +--- +title: "Rate Limit Design" +--- + +## Overview + +Rate limit is a feature that allows the user to limit the number of incoming requests +to a predefined value based on attributes within the traffic flow. + +Here are some reasons why a user may want to implements Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +## Scope Types + +The rate limit type here describes the scope of rate limits. + +* Global - In this case, the rate limit is common across all the instances of Envoy proxies +where its applied i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is +10 requests/second, this limit is common and will be hit if 5 requests pass through the first replica +and 5 requests pass through the second replica within the same second. + +* Local - In this case, the rate limits are specific to each instance/replica of Envoy running. +Note - This is not part of the initial design and will be added as a future enhancement. + +## Match Types + +### Rate limit a specific traffic flow + +* Here is an example of a ratelimit implemented by the application developer to limit a specific user +by matching on a custom `x-user-id` header with a value set to `one` + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-specific-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-specific-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit all traffic flows + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route to safeguard health of internal application components. In this case, no specific `headers` match +is specified, and the rate limit is applied to all traffic flows accepted by this `HTTPRoute`. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-all-requests +spec: + type: Global + global: + rules: + - limit: + requests: 1000 + unit: Second +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-requests + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per distinct value + +* Here is an example of a rate limit implemented by the application developer to limit any unique user +by matching on a custom `x-user-id` header. Here, user A (recognised from the traffic flow using the header +`x-user-id` and value `a`) will be rate limited at 10 requests/hour and so will user B +(recognised from the traffic flow using the header `x-user-id` and value `b`). + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-per-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per source IP + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on source IP. In this case, requests from `x.x.x.x` will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-per-ip +spec: + type: Global + global: + rules: + - clientSelectors: + - sourceIP: x.x.x.x/32 + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit based on JWT claims + +* Here is an example of rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on the jwt claim. In this case, requests with jwt claim information of `{"name":"John Doe"}` +will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: jwt-example +spec: + type: JWT + jwtProviders: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + claimToHeaders: + - claim: name + header: custom-request-header +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-specific-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: custom-request-header + value: John Doe + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + filters: + - extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: jwt-example + type: ExtensionRef + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-specific-user + matches: + - path: + type: PathPrefix + value: /foo +``` + + +## Multiple RateLimitFilters, rules and clientSelectors +* Users can create multiple `RateLimitFilter`s and apply it to the same `HTTPRoute`. In such a case each +`RateLimitFilter` will be applied to the route and matched (and limited) in a mutually exclusive way, independent of each other. +* Rate limits are applied for each `RateLimitFilter` `rule` when ALL the conditions under `clientSelectors` hold true. + +Here's an example highlighting this - + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-all-safeguard-app +spec: + type: Global + global: + rules: + - limit: + requests: 100 + unit: Second +--- + +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: RateLimitFilter +metadata: + name: ratelimit-per-user +spec: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 1000 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-safeguard-app + backendRefs: + - name: backend + port: 3000 +``` + +* The user has created two `RateLimitFilter`s and has attached it to a `HTTPRoute` - one(`ratelimit-all-safeguard-app`) to +ensure that the backend does not get overwhelmed with requests, any excess requests are rate limited irrespective of +the attributes within the traffic flow, and another(`ratelimit-per-user`) to rate limit each distinct user client +who can be differentiated using the `x-user-id` header, to ensure that each client does not make exessive requests to the backend. +* If user `baz` (identified with the header and value of `x-user-id: baz`) sends 90 requests within the first second, and +user `bar` sends 11 more requests during that same interval of 1 second, and user `bar` sends the 101th request within that second, +the rule defined in `ratelimit-all-safeguard-app` gets activated and Envoy Gateway will ratelimit the request sent by `bar` (and any other +request sent within that 1 second). After 1 second, the rate limit counter associated with the `ratelimit-all-safeguard-app` rule +is reset and again evaluated. +* If user `bar` also ends up sending 90 more requests within the hour, summing up `bar`'s total request count to 101, the rate limit rule +defined within `ratelimit-per-user` will get activated, and `bar`'s requests will be rate limited again until the hour interval ends. +* Within the same above hour, if `baz` sends 991 more requests, summing up `baz`'s total request count to 1001, the rate limit rule defined +within `ratelimit-per-user` will get activated for `baz`, and `baz`'s requests will also be rate limited until the hour interval ends. + +## Design Decisions + +* The initial design uses an Extension filter to apply the Rate Limit functionality on a specific [HTTPRoute][]. +This was preferred over the [PolicyAttachment][] extension mechanism, because it is unclear whether Rate Limit +will be required to be enforced or overridden by the platform administrator or not. +* The RateLimitFilter can only be applied as a filter to a [HTTPRouteRule[], applying it across all backends within a [HTTPRoute][] +and cannot be applied a filter within a [HTTPBackendRef][] for a specific backend. +* The [HTTPRoute][] API has a [matches][] field within each [rule][] to select a specific traffic flow to be routed to +the destination backend. The RateLimitFilter API that can be attached to an HTTPRoute via an [extensionRef][] filter, +also has a `clientSelectors` field within each `rule` to select attributes within the traffic flow to rate limit specific clients. +The two levels of selectors/matches allow for flexibility and aim to hold match information specific to its use, allowing the author/owner +of each configuration to be different. It also allows the `clientSelectors` field within the RateLimitFilter to be enhanced with other matchable +attribute such as [IP subnet][] in the future that are not relevant in the [HTTPRoute][] API. + +## Implementation Details + +### Global Rate limiting + +* [Global rate limiting][] in Envoy Proxy can be achieved using the following - + * [Actions][] can be configured per [xDS Route][]. + * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] + defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined + using a [rate limit filter][]. + * Based on information received by the rate limit service and its programmed configuration, a decision is computed, whether to rate limit + the HTTP Request or not, and is sent back to Envoy, which enforces this decision on the data plane. +* Envoy Gateway will leverage this Envoy Proxy feature by - + * Translating the user facing RateLimitFilter API into Rate limit [Actions][] as well as Rate limit service configuration to implement + the desired API intent. + * Envoy Gateway will use the existing [reference implementation][] of the rate limit service. + * The Infrastructure administrator will need to enable the rate limit service using new settings that will be defined in the [EnvoyGateway][] config API. + * The xDS IR will be enhanced to hold the user facing rate limit intent. + * The xDS Translator will be enhanced to translate the rate limit field within the xDS IR into Rate limit [Actions][] as well as instantiate the [rate limit filter][]. + * A new runner called `rate-limit` will be added that subscribes to the xDS IR messages and translates it into a new Rate Limit Infra IR which contains + the [rate limit service configuration][] as well as other information needed to deploy the rate limit service. + * The infrastructure service will be enhanced to subscribe to the Rate Limit Infra IR and deploy a provider specific rate limit service runnable entity. + * A Status field within the RateLimitFilter API will be added to reflect whether the specific configuration was programmed correctly in these multiple locations or not. + +[PolicyAttachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPBackendRef +[matches]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch +[rule]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch +[extensionRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType +[IP subnet]: https://en.wikipedia.org/wiki/Subnetwork +[Actions]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit-action +[descriptors]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter.html?highlight=descriptor#example-1 +[Global rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[xDS Route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-routeaction +[rate limit filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[rate limit service]: https://www.envoyproxy.io/docs/envoy/latest/configuration/other_features/rate_limit#config-rate-limit-service +[reference implementation]: https://github.com/envoyproxy/ratelimit +[EnvoyGateway]: https://github.com/envoyproxy/gateway/blob/main/api/config/v1alpha1/envoygateway_types.go +[rate limit service configuration]: https://github.com/envoyproxy/ratelimit#configuration diff --git a/site/content/en/v0.5.0/design/request-authentication.md b/site/content/en/v0.5.0/design/request-authentication.md new file mode 100644 index 00000000000..82682bf2a0b --- /dev/null +++ b/site/content/en/v0.5.0/design/request-authentication.md @@ -0,0 +1,515 @@ +--- +title: "Request Authentication Design" +--- + +## Overview + +[Issue 336][] specifies the need for exposing a user-facing API to configure request authentication. Request +authentication is defined as an authentication mechanism to be enforced by Envoy on a per-request basis. A connection +will be rejected if it contains invalid authentication information, based on the `AuthenticationFilter` API type +proposed in this design document. + +Envoy Gateway leverages [Gateway API][] for configuring managed Envoy proxies. Gateway API defines core, extended, and +implementation-specific API [support levels][] for implementers such as Envoy Gateway to expose features. Since +implementing request authentication is not covered by `Core` or `Extended` APIs, an `Implementation-specific` API will +be created for this purpose. + +## Goals + +* Define an API for configuring request authentication. +* Implement [JWT] as the first supported authentication type. +* Allow users that manage routes, e.g. [HTTPRoute][], to authenticate matching requests before forwarding to a backend + service. +* Support HTTPRoutes as an Authentication API referent. HTTPRoute provides multiple [extension points][]. The + [HTTPRouteFilter][] is the extension point supported by the Authentication API. + +## Non-Goals + +* Allow infrastructure administrators to override or establish default authentication policies. +* Support referents other than HTTPRoute. +* Support Gateway API extension points other than HTTPRouteFilter. + +## Use-Cases + +These use-cases are presented as an aid for how users may attempt to utilize the outputs of the design. They are not an +exhaustive list of features for authentication support in Envoy Gateway. + +As a Service Producer, I need the ability to: +* Authenticate a request before forwarding it to a backend service. +* Have different authentication mechanisms per route rule. +* Choose from different authentication mechanisms supported by Envoy, e.g. OIDC. + +### Authentication API Type + +The Authentication API type defines authentication configuration for authenticating requests through managed Envoy +proxies. + +```go +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +) + +type AuthenticationFilter struct { + metav1.TypeMeta + metav1.ObjectMeta + + // Spec defines the desired state of the Authentication type. + Spec AuthenticationFilterSpec + + // Note: The status sub-resource has been excluded but may be added in the future. +} + +// AuthenticationFilterSpec defines the desired state of the AuthenticationFilter type. +// +union +type AuthenticationFilterSpec struct { + // Type defines the type of authentication provider to use. Supported provider types are: + // + // * JWT: A provider that uses JSON Web Token (JWT) for authenticating requests. + // + // +unionDiscriminator + Type AuthenticationFilterType + + // JWT defines the JSON Web Token (JWT) authentication provider type. When multiple + // jwtProviders are specified, the JWT is considered valid if any of the providers + // successfully validate the JWT. + JwtProviders []JwtAuthenticationFilterProvider +} + +... +``` + +Refer to [PR 773][] for the detailed AuthenticationFilter API spec. + +The status subresource is not included in the AuthenticationFilter API. Status will be surfaced by an HTTPRoute that +references an AuthenticationFilter. For example, an HTTPRoute will surface the `ResolvedRefs=False` status condition if it +references an AuthenticationFilter that does not exist. It may be beneficial to add AuthenticationFilter status fields in the future +based on defined use-cases. For example, a remote [JWKS][] can be validated based on the specified URI and have an +appropriate status condition surfaced. + +#### AuthenticationFilter Example + +The following is an AuthenticationFilter example with one JWT authentication provider: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example +spec: + type: JWT + jwtProviders: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJwks: + uri: https://foo.com/jwt/public-key/jwks.json + +``` + +__Note:__ `type` is a union type, allowing only one of any supported provider type such as `jwtProviders` to be +specified. + +The following is an example HTTPRoute configured to use the above JWT authentication provider: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example + backendRefs: + - name: backend + port: 3000 +``` + +Requests for `www.example.com/foo` will be authenticated using the referenced JWT provider before being forwarded to the +backend service named "backend". + +## Implementation Details + +The JWT authentication type is translated to an Envoy [JWT authentication filter][] and a cluster is created for each +remote [JWKS][]. The following examples provide additional details on how Gateway API and AuthenticationFilter resources are +translated into Envoy configuration. + +### Example 1: One Route with One JWT Provider + +The following cluster is created from the above HTTPRoute and AuthenticationFilter: + +```yaml +dynamic_clusters: + - name: foo.com|443 + load_assignment: + cluster_name: foo.com|443 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: foo.com + port_value: 443 + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: foo.com + common_tls_context: + validation_context: + match_subject_alt_names: + - exact: "*.foo.com" + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +``` + +A JWT authentication HTTP filter is added to the HTTP Connection Manager. For example: + +```yaml +dynamic_resources: + dynamic_listeners: + - name: example_listener + address: + socket_address: + address: 1.2.3.4 + port_value: 80 + filter_chains: + - filters: + - name: envoy.http_connection_manager + http_filters: + - name: envoy.filters.http.jwt_authn + typed_config: + "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication +``` + +This JWT authentication HTTP filter contains two fields: +* The `providers` field specifies how a JWT should be verified, such as where to extract the token, where to fetch the + public key ([JWKS][]) and where to output its payload. This field is built from the source resource `namespace-name`, and + the JWT provider name of an AuthenticationFilter. +* The `rules` field specifies matching rules and their requirements. If a request matches a rule, its requirement + applies. The requirement specifies which JWT providers should be used. This field is built from a HTTPRoute + `matches` rule that references the AuthenticationFilter. When a referenced Authentication specifies multiple + `jwtProviders`, the JWT is considered valid if __any__ of the providers successfully validate the JWT. + +The following JWT authentication HTTP filter `providers` configuration is created from the above AuthenticationFilter. + +```yaml +providers: + example: + issuer: https://www.example.com + audiences: + - foo.com + remote_jwks: + http_uri: + uri: https://foo.com/jwt/public-key/jwks.json + cluster: example_jwks_cluster + timeout: 1s +``` + +The following JWT authentication HTTP filter `rules` configuration is created from the above HTTPRoute. + +```yaml +rules: + - match: + prefix: /foo + requires: + provider_name: default-example-example +``` + +### Example 2: Two HTTPRoutes with Different AuthenticationFilters + +The following example contains: +* Two HTTPRoutes with different hostnames. +* Each HTTPRoute references a different AuthenticationFilter. +* Each AuthenticationFilter contains a different JWT provider. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example1 +spec: + type: JWT + jwtProviders: + - name: example1 + issuer: https://www.example1.com + audiences: + - foo.com + remoteJwks: + uri: https://foo.com/jwt/public-key/jwks.json +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: AuthenticationFilter +metadata: + name: example2 +spec: + type: JWT + jwtProviders: + - name: example2 + issuer: https://www.example2.com + audiences: + - bar.com + remoteJwks: + uri: https://bar.com/jwt/public-key/jwks.json +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example1 +spec: + hostnames: + - www.example1.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example1 + backendRefs: + - name: backend + port: 3000 +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example2 +spec: + hostnames: + - www.example2.com + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: eg + rules: + - matches: + - path: + type: PathPrefix + value: /bar + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: AuthenticationFilter + name: example2 + backendRefs: + - name: backend2 + port: 3000 +``` + +The following xDS configuration is created from the above example resources: + +```yaml +configs: +... +dynamic_listeners: + - name: default-eg-http + ... + default_filter_chain: + filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + '@type': >- + type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: http + rds: + config_source: + ... + route_config_name: default-eg-http + http_filters: + - name: envoy.filters.http.jwt_authn + typed_config: + '@type': >- + type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication + providers: + default-example1-example1: + issuer: https://www.example1.com + audiences: + - foo.com + remote_jwks: + http_uri: + uri: https://foo.com/jwt/public-key/jwks.json + cluster: default-example1-example1-jwt + default-example2-example2: + issuer: https://www.example2.com + audiences: + - bar.com + remote_jwks: + http_uri: + uri: https://bar.com/jwt/public-key/jwks.json + cluster: default-example2-example2-jwt + rules: + - match: + exact: /foo + requires: + provider_name: default-example1-example1 + - match: + exact: /bar + requires: + provider_name: default-example2-example2 + - name: envoy.filters.http.router + typed_config: + '@type': >- + type.googleapis.com/envoy.extensions.filters.http.router.v3.Router +dynamic_route_configs: + - route_config: + '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration + name: default-eg-http + virtual_hosts: + - name: default-eg-http + domains: + - '*' + routes: + - match: + prefix: /foo + headers: + - name: ':authority' + string_match: + exact: www.example1.com + route: + cluster: default-backend-rule-0-match-0-www.example1.com + - match: + prefix: /bar + headers: + - name: ':authority' + string_match: + exact: www.example2.com + route: + cluster: default-backend2-rule-0-match-0-www.example2.com +dynamic_active_clusters: + - cluster: + name: default-backend-rule-0-match-0-www.example.com + ... + endpoints: + - locality: {} + lb_endpoints: + - endpoint: + address: + socket_address: + address: $BACKEND_SERVICE1_IP + port_value: 3000 + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + name: default-backend-rule-1-match-0-www.example.com + ... + endpoints: + - locality: {} + lb_endpoints: + - endpoint: + address: + socket_address: + address: $BACKEND_SERVICE2_IP + port_value: 3000 +... +``` + +__Note:__ The JWT provider cluster and route is omitted from the above example for brevity. + +### Implementation Outline + +* Update the Kubernetes provider to get/watch AuthenticationFilter resources that are referenced by managed HTTPRoutes. + Add the referenced AuthenticationFilter object to the resource map and publish it. +* Update the resource translator to include the AuthenticationFilter API in HTTPRoute processing. +* Update the xDS translator to translate an AuthenticationFilter into xDS resources. The translator should perform the + following: + * Convert a list of JWT rules from the xds IR into an Envoy JWT filter config. + * Create a JWT authentication HTTP filter. + * Build the HTTP Connection Manager (HCM) HTTP filters. + * Build the HCM. + * When building the Listener, create an HCM for each filter-chain. + +## Adding Authentication Types + +Additional authentication types can be added in the future through the `AuthenticationFilterType` API. For +example, to add the `Foo` authentication type: + +Define the `Foo` authentication provider: + +```go +package v1alpha1 + +// FooAuthenticationFilterProvider defines the "Foo" authentication filter provider type. +type FooAuthenticationFilterProvider struct { + // TODO: Define fields of the Foo authentication filter provider type. +} +``` + +Add the `FooAuthenticationFilterProvider` type to `AuthenticationFilterSpec`: + +```go +package v1alpha1 + +type AuthenticationFilterSpec struct { + ... + + // Foo defines the Foo authentication type. For additional + // details, see: + // + // + // + // +optional + Foo *FooAuthenticationFilterProvider +} +``` + +Lastly, add the type to the `AuthenticationType` enum: + +```go +// AuthenticationType is a type of authentication provider. +// +kubebuilder:validation:Enum=JWT,FOO +type AuthenticationFilterType string + +const ( + // JwtAuthenticationProviderType is the JWT authentication provider type. + FooAuthenticationFilterProviderType AuthenticationFilterType = "FOO" +) +``` + +The AuthenticationFilter API should support additional authentication types in the future, for example: +- OAuth2 +- OIDC + +## Outstanding Questions + +- If Envoy Gateway owns the AuthenticationFilter API, is an xDS IR equivalent needed? +- Should local [JWKS][] be implemented before remote [JWKS][]? +- How should Envoy obtain the trusted CA for a remote [JWKS][]? +- Should HTTPS be the only supported scheme for remote [JWKS][]? +- Should OR'ing JWT providers be supported? +- Should Authentication provide status? +- Are the API field validation rules acceptable? + +[Issue 336]: https://github.com/envoyproxy/gateway/issues/336 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[support levels]: https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels +[JWT]: https://jwt.io/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[extension points]: https://gateway-api.sigs.k8s.io/concepts/api-overview/?h=extension#extension-points +[HTTPRouteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter +[JWKS]: https://www.rfc-editor.org/rfc/rfc7517 +[JWT authentication filter]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter#config-http-filters-jwt-authn +[PR 773]: https://github.com/envoyproxy/gateway/pull/733 diff --git a/site/content/en/v0.5.0/design/system-design.md b/site/content/en/v0.5.0/design/system-design.md new file mode 100644 index 00000000000..16123948ee7 --- /dev/null +++ b/site/content/en/v0.5.0/design/system-design.md @@ -0,0 +1,174 @@ +--- +title: "System Design" +weight: 2 +--- + +## Goals + +* Define the system components needed to satisfy the requirements of Envoy Gateway. + +## Non-Goals + +* Create a detailed design and interface specification for each system component. + +## Terminology + +* Control Plane- A collection of inter-related software components for providing application gateway and routing + functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. + These services are detailed in the [components](#components) section. +* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. + +## Architecture + +![Architecture](/img/architecture.png) + +## Configuration + +Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][gw_api] objects. + +### Static Configuration + +Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, +configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the +configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. + +### Dynamic Configuration + +Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using +reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is +defined as Kubernetes resources that provide the following services: + +* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is + expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be + referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, + e.g. expose Envoy network endpoints using a `ClusterIP` service instead of a `LoadBalancer` service. +* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP + requests for "www.example.com" to a backend service running a web server. This configuration is expressed through + [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. + Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] + reference. + +## Components + +Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described +in the [Watching Components Design][wcd]. + +### Provider + +A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve +services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap +via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A +provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). + +#### Kubernetes Provider + +* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the + [dynamic configuration](#dynamic-configuration). +* Manages the data plane through Kubernetes API CRUD operations. +* Uses Kubernetes for Service discovery. +* Uses etcd (via Kubernetes API) to persist data. + +#### File Provider + +* Uses a file watcher to watch files in a directory that define the data plane configuration. +* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. +* Uses the host's DNS for Service discovery. +* If needed, the local filesystem is used to persist data. + +### Resource Watcher + +The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The +mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes +provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator +as output. + +### Resource Translator + +The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate +Representation (IR). It is responsible for: + +* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. +* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. + +__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. + +### Intermediate Representation (IR) + +The Intermediate Representation defines internal data models that external resources are translated into. This allows +Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR +used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. + +* Infra IR- Used as the internal definition of the managed data plane infrastructure. +* xDS IR- Used as the internal definition of the managed data plane xDS configuration. + +### xDS Translator + +The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. + +### xDS Server + +The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server +Protocol and is responsible for using xDS to configure the data plane. + +### Infra Manager + +The Infra Manager is a provider-specific component responsible for managing the following infrastructure: + +* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, + Service, etc. resources to run Envoy in a Kubernetes cluster. +* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require + external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning + and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to + users through the [Custom Route Filters][crf] extension. + +The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. + +## Design Decisions + +* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with + `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy + Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. + `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy + infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. +* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. + * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. + * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners + in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are + met: + * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies + either the “HTTPS” or “TLS” Protocol. + * Each Listener within the group specifies a unique "Hostname". + * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no + other Listener matches. + * Envoy Gateway does __not__ merge listeners across multiple Gateways. +* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. + * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. +* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. + * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. +* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. + +The draft for this document is [here][draft_design]. + +[gw_api]: https://gateway-api.sigs.k8s.io +[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass +[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway +[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute +[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute +[go_cp]: https://github.com/envoyproxy/go-control-plane +[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[rls]: https://github.com/envoyproxy/ratelimit +[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional +[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts +[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners +[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route +[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional +[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster +[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[be]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.BackendObjectReference +[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ +[ wcd ]: ./watching.md +[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 +[roadmap]: roadmap.md diff --git a/site/content/en/v0.5.0/design/tcp-udp-design.md b/site/content/en/v0.5.0/design/tcp-udp-design.md new file mode 100644 index 00000000000..f517e24feda --- /dev/null +++ b/site/content/en/v0.5.0/design/tcp-udp-design.md @@ -0,0 +1,49 @@ +--- +title: "TCP and UDP Proxy Design " +--- + +Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP +and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the +design decision. + +Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) + and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) +, so ideally, Envoy Gateway should also be able to work in these two modes: + +## Non-transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the +TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when +proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see Envoy's IP address and port. + +## Transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies +the TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address +when proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see the original downstream IP address and Envoy's mac address. + +Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward +the port number. + +## The Implications of Transparent Proxy Mode + +### Escalated Privilege +Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated +CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. + +### Routing +The upstream can see the original source IP, but the original port number won't be passed, so the return +traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back +to the right port number of the downstream, which requires routing at the upstream side to be set up. +In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. + +## The Design Decision (For Now) + +The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and +port of the deployed Envoy instance instead of the client. diff --git a/site/content/en/v0.5.0/design/tracing.md b/site/content/en/v0.5.0/design/tracing.md new file mode 100644 index 00000000000..3ddfc2493ec --- /dev/null +++ b/site/content/en/v0.5.0/design/tracing.md @@ -0,0 +1,161 @@ +--- +title: "Observability: Tracing" +--- + +## Overview + +Envoy supports extensible tracing to different sinks, Zipkin, OpenTelemetry etc. Overview of Envoy tracing can be found [here](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/observability/tracing). + +Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementers such as Envoy Gateway to expose features. Since tracing is not covered by `Core` or `Extended` APIs, EG should provide an easy to config tracing per `EnvoyProxy`. + +Only OpenTelemetry sink can be configured currently, you can use [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) to export to other tracing backends. + +## Goals + +- Support send tracing to `OpenTelemetry` backend +- Support configurable sampling rate +- Support propagate tag from `Literal`, `Environment` and `Request Header` + +## Non-Goals + +- Support other tracing backend, e.g. `Zipkin`, `Jaeger` + +## Use-Cases + +- Configure accesslog for a `EnvoyProxy` to `File` + +### ProxyAccessLog API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/tracing_types.go" +type ProxyTracing struct { + // SamplingRate controls the rate at which traffic will be + // selected for tracing if no prior sampling decision has been made. + // Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=100 + // +kubebuilder:default=100 + // +optional + SamplingRate *uint32 `json:"samplingRate,omitempty"` + // CustomTags defines the custom tags to add to each span. + // If provider is kubernetes, pod name and namespace are added by default. + CustomTags map[string]CustomTag `json:"customTags,omitempty"` + // Provider defines the tracing provider. + // Only OpenTelemetry is supported currently. + Provider TracingProvider `json:"provider"` +} + +type TracingProviderType string + +const ( + TracingProviderTypeOpenTelemetry TracingProviderType = "OpenTelemetry" +) + +type TracingProvider struct { + // Type defines the tracing provider type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type TracingProviderType `json:"type"` + // Host define the provider service hostname. + Host string `json:"host"` + // Port defines the port the provider service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +type CustomTagType string + +const ( + // CustomTagTypeLiteral adds hard-coded value to each span. + CustomTagTypeLiteral CustomTagType = "Literal" + // CustomTagTypeEnvironment adds value from environment variable to each span. + CustomTagTypeEnvironment CustomTagType = "Environment" + // CustomTagTypeRequestHeader adds value from request header to each span. + CustomTagTypeRequestHeader CustomTagType = "RequestHeader" +) + +type CustomTag struct { + // Type defines the type of custom tag. + // +kubebuilder:validation:Enum=Literal;Environment;RequestHeader + // +unionDiscriminator + // +kubebuilder:default=Literal + Type CustomTagType `json:"type"` + // Literal adds hard-coded value to each span. + // It's required when the type is "Literal". + Literal *LiteralCustomTag `json:"literal,omitempty"` + // Environment adds value from environment variable to each span. + // It's required when the type is "Environment". + Environment *EnvironmentCustomTag `json:"environment,omitempty"` + // RequestHeader adds value from request header to each span. + // It's required when the type is "RequestHeader". + RequestHeader *RequestHeaderCustomTag `json:"requestHeader,omitempty"` + + // TODO: add support for Metadata tags in the future. + // EG currently doesn't support metadata for route or cluster. +} + +// LiteralCustomTag adds hard-coded value to each span. +type LiteralCustomTag struct { + // Value defines the hard-coded value to add to each span. + Value string `json:"value"` +} + +// EnvironmentCustomTag adds value from environment variable to each span. +type EnvironmentCustomTag struct { + // Name defines the name of the environment variable which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the environment variable is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} + +// RequestHeaderCustomTag adds value from request header to each span. +type RequestHeaderCustomTag struct { + // Name defines the name of the request header which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the request header is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} +``` + +### Example + +1. The following is an example to config tracing. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/tracing/default.yaml" +apiVersion: config.gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: tracing + namespace: envoy-gateway-system +spec: + telemetry: + tracing: + # sample 100% of requests + samplingRate: 100 + provider: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + customTags: + # This is an example of using a literal as a tag value + key1: + type: Literal + literal: + value: "val1" + # This is an example of using an environment variable as a tag value + env1: + type: Environment + environment: + name: ENV1 + defaultValue: "-" + # This is an example of using a header value as a tag value + header1: + type: RequestHeader + requestHeader: + name: X-Header-1 + defaultValue: "-" +``` diff --git a/site/content/en/v0.5.0/design/watching.md b/site/content/en/v0.5.0/design/watching.md new file mode 100644 index 00000000000..72a955043e0 --- /dev/null +++ b/site/content/en/v0.5.0/design/watching.md @@ -0,0 +1,120 @@ +--- +title: "Watching Components Design" +weight: 3 +--- + +Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch +external resources, and "publish" what they see for other components to consume; others watch what another publishes and +act on it (such as the resource translator watches what the providers publish, and then publishes its own results that +are watched by another component). Some of these internally published results are consumed by multiple components. + +To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the +standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub +pattern. + +## Pub + +Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" +tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if +we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by +the IR translator: + + ```go + type ResourceTable struct { + // gateway classes are cluster-scoped; no namespace + GatewayClasses watchable.Map[string, *gwapiv1b1.GatewayClass] + + // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. + Gateways watchable.Map[types.NamespacedName, *gwapiv1b1.Gateway] + + HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1b1.HTTPRoute] + } + ``` + +The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; +updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` +method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher +doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just +`.Store` its data and `watchable` will do the right thing. + +## Sub + +Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or +`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: + + ```go + func(ctx context.Context) error { + for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { + fullState := irInput{ + GatewayClasses: k8sTable.GatewayClasses.LoadAll(), + Gateways: k8sTable.Gateways.LoadAll(), + HTTPRoutes: snapshot.State, + } + translate(irInput) + } + } + ``` + +Or, to watch multiple maps in the same loop: + + ```go + func worker(ctx context.Context) error { + classCh := k8sTable.GatewayClasses.Subscribe(ctx) + gwCh := k8sTable.Gateways.Subscribe(ctx) + routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) + for ctx.Err() == nil { + var arg irInput + select { + case snapshot := <-classCh: + arg.GatewayClasses = snapshot.State + case snapshot := <-gwCh: + arg.Gateways = snapshot.State + case snapshot := <-routeCh: + arg.Routes = snapshot.State + } + if arg.GateWayClasses == nil { + arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() + } + if arg.GateWays == nil { + arg.Gateways = k8sTable.GateWays.LoadAll() + } + if arg.HTTPRoutes == nil { + arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() + } + translate(irInput) + } + } + ``` + +From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; +but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a +handy way to know when to run, `.Load` and friends can be used without subscribing. + +There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but +it's probably wise to just have one publisher for each map. + +The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when +`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple +mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in +to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of +each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need +to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. + +If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` +entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you +must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to +help with this. + +## Other Notes + +The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the +map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb +`chan`. + +A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types +be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and +so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can +generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate +methods for. + +[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/site/content/en/v0.5.0/install/_index.md b/site/content/en/v0.5.0/install/_index.md new file mode 100644 index 00000000000..b4c6f79c6fd --- /dev/null +++ b/site/content/en/v0.5.0/install/_index.md @@ -0,0 +1,5 @@ +--- +title: "Installation" +description: This section includes installation related contents of Envoy Gateway. +weight: 70 +--- diff --git a/site/content/en/v0.5.0/install/api.md b/site/content/en/v0.5.0/install/api.md new file mode 100644 index 00000000000..130c82250ce --- /dev/null +++ b/site/content/en/v0.5.0/install/api.md @@ -0,0 +1,57 @@ +--- +title: "Helm Chart Values" +--- + +![Version: v0.0.0-latest](https://img.shields.io/badge/Version-v0.0.0--latest-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square) + +The Helm chart for Envoy Gateway + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| envoy-gateway-steering-committee | | | +| envoy-gateway-maintainers | | | + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| config.envoyGateway.gateway.controllerName | string | `"gateway.envoyproxy.io/gatewayclass-controller"` | | +| config.envoyGateway.logging.level.default | string | `"info"` | | +| config.envoyGateway.provider.type | string | `"Kubernetes"` | | +| createNamespace | bool | `false` | | +| deployment.envoyGateway.image.repository | string | `"${ImageRepository}"` | | +| deployment.envoyGateway.image.tag | string | `"${ImageTag}"` | | +| deployment.envoyGateway.imagePullPolicy | string | `"Always"` | | +| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | +| deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | | +| deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | | +| deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | | +| deployment.kubeRbacProxy.image.repository | string | `"gcr.io/kubebuilder/kube-rbac-proxy"` | | +| deployment.kubeRbacProxy.image.tag | string | `"v0.11.0"` | | +| deployment.kubeRbacProxy.resources.limits.cpu | string | `"500m"` | | +| deployment.kubeRbacProxy.resources.limits.memory | string | `"128Mi"` | | +| deployment.kubeRbacProxy.resources.requests.cpu | string | `"5m"` | | +| deployment.kubeRbacProxy.resources.requests.memory | string | `"64Mi"` | | +| deployment.pod.annotations | object | `{}` | | +| deployment.pod.labels | object | `{}` | | +| deployment.ports[0].name | string | `"grpc"` | | +| deployment.ports[0].port | int | `18000` | | +| deployment.ports[0].targetPort | int | `18000` | | +| deployment.ports[1].name | string | `"ratelimit"` | | +| deployment.ports[1].port | int | `18001` | | +| deployment.ports[1].targetPort | int | `18001` | | +| deployment.replicas | int | `1` | | +| envoyGatewayMetricsService.ports[0].name | string | `"https"` | | +| envoyGatewayMetricsService.ports[0].port | int | `8443` | | +| envoyGatewayMetricsService.ports[0].protocol | string | `"TCP"` | | +| envoyGatewayMetricsService.ports[0].targetPort | string | `"https"` | | +| kubernetesClusterDomain | string | `"cluster.local"` | | + diff --git a/site/content/en/v0.5.0/install/install-egctl.md b/site/content/en/v0.5.0/install/install-egctl.md new file mode 100644 index 00000000000..8534acb7127 --- /dev/null +++ b/site/content/en/v0.5.0/install/install-egctl.md @@ -0,0 +1,57 @@ +--- +title: "Install egctl" +weight: -80 +--- + +{{% alert title="What is egctl?" color="primary" %}} + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + +{{% /alert %}} + + +This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. + +### From The Envoy Gateway Project + +The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. + +### From the Binary Releases + +Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. + +1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) +2. Unpack it (tar -zxvf egctl_latest_linux_amd64.tar.gz) +3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) + +From there, you should be able to run: `egctl help`. + +### From Script + +`egctl` now has an installer script that will automatically grab the latest release version of egctl and install it locally. + +You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. + +```shell +curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh + +chmod +x get-egctl.sh + +# get help info of the +bash get-egctl.sh --help + +# install the latest development version of egctl +bash VERSION=latest get-egctl.sh +``` + +Yes, you can just use the below command if you want to live on the edge. + +```shell +curl https://gateway.envoyproxy.io/get-egctl.sh | VERSION=latest bash +``` + +{{% alert title="Next Steps" color="warning" %}} + +You can refer to [User Guides](../../user/egctl) to more details about egctl. + +{{% /alert %}} diff --git a/site/content/en/v0.5.0/install/install-helm.md b/site/content/en/v0.5.0/install/install-helm.md new file mode 100644 index 00000000000..ad6f945bddb --- /dev/null +++ b/site/content/en/v0.5.0/install/install-helm.md @@ -0,0 +1,142 @@ ++++ +title = "Install with Helm" +weight = -100 ++++ + +[Helm](https://helm.sh) is a package manager for Kubernetes that automates the release and management of software on Kubernetes. + +Envoy Gateway can be installed via a Helm chart with a few simple steps, depending on if you are deploying for the first time, upgrading Envoy Gateway from an existing installation, or migrating from Envoy Gateway. + +## Before you begin + +{{% alert title="Compatibility Matrix" color="warning" %}} +Refer to the [Version Compatibility Matrix](/blog/2022/10/01/versions/) to learn more. +{{% /alert %}} + +The Envoy Gateway Helm chart is hosted by DockerHub. + +It is published at `oci://docker.io/envoyproxy/gateway-helm`. + +{{% alert title="Note" color="primary" %}} +We use `v0.0.0-latest` as the latest development version. + +You can visit [Envoy Gateway Helm Chart](https://hub.docker.com/r/envoyproxy/gateway-helm/tags) for more releases. +{{% /alert %}} + +## Install with Helm + +Envoy Gateway is typically deployed to Kubernetes from the command line. If you don't have Kubernetes, you should use `kind` to create one. + +{{% alert title="Developer Guide" color="primary" %}} +Refer to the [Developer Guide](/latest/contributions/develop) to learn more. +{{% /alert %}} + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml + +## Helm chart customizations + +Some of the quick ways of using the helm install command for envoy gateway installation are below. + +### Increase the replicas + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set deployment.replicas=2 +``` + +### Change the kubernetesClusterDomain name + +If you have installed your cluster with different domain name you can use below command. + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set kubernetesClusterDomain= +``` + +**Note**: Above are some of the ways we can directly use for customization of our installation. But if you are looking for more complex changes [values.yaml](https://helm.sh/docs/chart_template_guide/values_files/) comes to rescue. + +### Using values.yaml file for complex installation + +```yaml +deployment: + envoyGateway: + resources: + limits: + cpu: 700m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + ports: + - name: grpc + port: 18005 + targetPort: 18000 + - name: ratelimit + port: 18006 + targetPort: 18001 + +config: + envoyGateway: + logging: + level: + default: debug +``` + +Here we have made three changes to our values.yaml file. Increase the resources limit for cpu to `700m`, changed the port for grpc to `18005` and for ratelimit to `18006` and also updated the logging level to `debug`. + +You can use the below command to install the envoy gateway using values.yaml file. + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -f values.yaml +``` + +{{% alert title="Helm Chart Values" color="primary" %}} +If you want to know all the available fields inside the values.yaml file, please see the [Helm Chart Values](../api). +{{% /alert %}} + +## Open Ports + +These are the ports used by Envoy Gateway and the managed Envoy Proxy. + +### Envoy Gateway + +| Envoy Gateway | Address | Port | +|:----------------------:|:---------:|:------:| +| Xds EnvoyProxy Server | 0.0.0.0 | 18000 | +| Xds RateLimit Server | 0.0.0.0 | 18001 | +| Admin Server | 127.0.0.1 | 19000 | + +### EnvoyProxy + +| Envoy Proxy | Address | Port | +|:---------------------------------:|:-----------:| :-----: | +| Admin Server | 127.0.0.1 | 19000 | +| Heath Check | 0.0.0.0 | 19001 | + +{{% alert title="Next Steps" color="warning" %}} +Envoy Gateway should now be successfully installed and running, but in order to experience more abilities of Envoy Gateway, you can refer to [User Guides](../../user). +{{% /alert %}} diff --git a/site/content/en/v0.5.0/install/install-yaml.md b/site/content/en/v0.5.0/install/install-yaml.md new file mode 100644 index 00000000000..28f6bec4a76 --- /dev/null +++ b/site/content/en/v0.5.0/install/install-yaml.md @@ -0,0 +1,39 @@ ++++ +title = "Install with Kubernetes YAML" +weight = -99 ++++ + +In this guide, we'll walk you through installing Envoy Gateway in your Kubernetes cluster. + +The manual install process does not allow for as much control over configuration +as the [Helm install method](../install-helm), so if you need more control over your Envoy Gateway +installation, it is recommended that you use helm. + +## Before you begin + +Envoy Gateway is designed to run in Kubernetes for production. The most essential requirements are: + +* Kubernetes 1.25 or later +* The `kubectl` command-line tool + +{{% alert title="Compatibility Matrix" color="warning" %}} +Refer to the [Version Compatibility Matrix](/blog/2022/10/01/versions/) to learn more. +{{% /alert %}} + +## Install with YAML + +Envoy Gateway is typically deployed to Kubernetes from the command line. If you don't have Kubernetes, you should use `kind` to create one. + +{{% alert title="Developer Guide" color="primary" %}} +Refer to the [Developer Guide](../../contributions/develop) to learn more. +{{% /alert %}} + +1. In your terminal, run the following command: + + ```shell + kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/install.yaml + ``` + +2. Next Steps + + Envoy Gateway should now be successfully installed and running, but in order to experience more abilities of Envoy Gateway, you can refer to [User Guides](../../user). diff --git a/site/content/en/v0.5.0/user/_index.md b/site/content/en/v0.5.0/user/_index.md new file mode 100644 index 00000000000..2f23014d867 --- /dev/null +++ b/site/content/en/v0.5.0/user/_index.md @@ -0,0 +1,6 @@ +--- +title: "User Guides" +weight: 2 +description: This section includes User Guides of Envoy Gateway. +--- + diff --git a/site/content/en/v0.5.0/user/authn.md b/site/content/en/v0.5.0/user/authn.md new file mode 100644 index 00000000000..3762e6f814b --- /dev/null +++ b/site/content/en/v0.5.0/user/authn.md @@ -0,0 +1,96 @@ +--- +title: "Request Authentication" +--- + +This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks +if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only +supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. + +## Installation + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Configuration + +Allow requests with a valid JWT by creating an [AuthenticationFilter][] and referencing it from the example HTTPRoute. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.5.0/examples/kubernetes/authn/jwt.yaml +``` + +The HTTPRoute is now updated to authenticate requests for `/foo` and allow unauthenticated requests to `/bar`. The +`/foo` route rule references an AuthenticationFilter that provides the JWT authentication configuration. + +Verify the HTTPRoute configuration and status: + +```shell +kubectl get httproute/backend -o yaml +``` + +The AuthenticationFilter is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the AuthenticationFilter configuration: + +```shell +kubectl get authenticationfilter/jwt-example -o yaml +``` + +## Testing + +Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](../quickstart) guide is set. If not, follow the +Quickstart instructions to set the variable. + +```shell +echo $GATEWAY_HOST +``` + +Verify that requests to `/foo` are denied without a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `401` HTTP response code should be returned. + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `/foo` with a valid JWT is allowed: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `200` HTTP response code should be returned. + +Verify that requests to `/bar` are allowed __without__ a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar +``` + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the AuthenticationFilter: + +```shell +kubectl delete authenticationfilter/jwt-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[jwt]: https://tools.ietf.org/html/rfc7519 +[AuthenticationFilter]: https://gateway.envoyproxy.io/v0.5.0/api/extension_types.html#authenticationfilter +[jwks]: https://tools.ietf.org/html/rfc7517 diff --git a/site/content/en/v0.5.0/user/customize-envoyproxy.md b/site/content/en/v0.5.0/user/customize-envoyproxy.md new file mode 100644 index 00000000000..24bdbec7033 --- /dev/null +++ b/site/content/en/v0.5.0/user/customize-envoyproxy.md @@ -0,0 +1,315 @@ +--- +title: "Customize EnvoyProxy" +--- + +Envoy Gateway provides an [EnvoyProxy][] CRD that can be linked to the ParametersRef +in GatewayClass, allowing cluster admins to customize the managed EnvoyProxy Deployment and +Service. To learn more about GatewayClass and ParametersRef, please refer to [Gateway API documentation][]. + +## Installation + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Add GatewayClass ParametersRef + +First, you need to add ParametersRef in GatewayClass, and refer to EnvoyProxy Config: + +```shell +cat < Envoy Gateway has provided two initial `env` `ENVOY_GATEWAY_NAMESPACE` and `ENVOY_POD_NAME` for envoyproxy container. + +After applying the config, you can get the envoyproxy deployment, and see resources has been changed. + +## Customize EnvoyProxy Deployment Volumes or VolumeMounts + +You can customize the EnvoyProxy Deployment Volumes or VolumeMounts via EnvoyProxy Config like: + +```shell +cat < GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8888 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:19:42 GMT +< content-length: 521 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.marketing.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.157" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "c637977c-458a-48ae-92b3-f8c429849322" + ] + }, + "namespace": "marketing", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-bcs8f" +* Connection #0 to host localhost left intact +``` + +* Lets deploy Envoy Gateway in the `product` namespace + +``` +helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.5.0 -n product --create-namespace +``` + +Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. + +```shell +cat < GET /get HTTP/1.1 +> Host: www.product.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:20:17 GMT +< content-length: 517 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.product.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.156" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39196453-2250-4331-b756-54003b2853c2" + ] + }, + "namespace": "product", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-64fjs" +* Connection #0 to host localhost left intact +``` + +With the below command you can ensure that you are no able to access the marketing team's backend exposed using the `www.marketing.example.com` hostname +and the product team's data plane. + +```shell +curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get +``` + +``` +* Trying 127.0.0.1:8889... +* Connected to localhost (127.0.0.1) port 8889 (#0) +> GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 404 Not Found +< date: Thu, 20 Apr 2023 19:22:13 GMT +< server: envoy +< content-length: 0 +< +* Connection #0 to host localhost left intact +``` diff --git a/site/content/en/v0.5.0/user/egctl.md b/site/content/en/v0.5.0/user/egctl.md new file mode 100644 index 00000000000..a09b1b43481 --- /dev/null +++ b/site/content/en/v0.5.0/user/egctl.md @@ -0,0 +1,790 @@ +--- +title: "Use egctl" +--- + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + +## Installing egctl + +This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. + +### From The Envoy Gateway Project + +The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. + +### From the Binary Releases + +Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. + +1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) +2. Unpack it (tar -zxvf egctl_v0.5.0_linux_amd64.tar.gz) +3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) + +From there, you should be able to run: `egctl help`. + +### From Script + +`egctl` now has an installer script that will automatically grab the v0.5.0 release version of egctl and install it locally. + +You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. + +```shell +curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh + +chmod +x get-egctl.sh + +# get help info of the +bash get-egctl.sh --help + +# install the v0.5.0 development version of egctl +bash VERSION=v0.5.0 get-egctl.sh +``` + +Yes, you can just use the below command if you want to live on the edge. + +```shell +curl https://gateway.envoyproxy.io/get-egctl.sh | VERSION=v0.5.0 bash +``` + +## egctl experimental translate + +This subcommand allows users to translate from an input configuration type to an output configuration type. + +In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS +resources. + +```shell +cat <// + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/local_reply_config" + value: + mappers: + - filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + runtime_key: key_b + status_code: 406 + body: + inline_string: "could not find what you are looking for" +EOF +``` + +* Lets edit the HTTPRoute resource from the Quickstart to only match on paths with value `/get` + +``` +kubectl patch httproute backend --type=json --patch '[{ + "op": "add", + "path": "/spec/rules/0/matches/0/path/value", + "value": "/get", +}]' +``` + +* Lets test it out by specifying a path apart from `/get` + +``` +$ curl --header "Host: www.example.com" http://localhost:8888/find +Handling connection for 8888 +could not find what you are looking for +``` + +## Debugging + +### Runtime + +* The `Status` subresource should have information about the status of the resource. Make sure +`Accepted=True` and `Programmed=True` conditions are set to ensure that the policy has been +applied to Envoy Proxy. + +``` +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"gateway.envoyproxy.io/v1alpha1","kind":"EnvoyPatchPolicy","metadata":{"annotations":{},"name":"custom-response-patch-policy","namespace":"default"},"spec":{"jsonPatches":[{"name":"default/eg/http","operation":{"op":"add","path":"/default_filter_chain/filters/0/typed_config/local_reply_config","value":{"mappers":[{"body":{"inline_string":"could not find what you are looking for"},"filter":{"status_code_filter":{"comparison":{"op":"EQ","value":{"default_value":404}}}}}]}},"type":"type.googleapis.com/envoy.config.listener.v3.Listener"}],"priority":0,"targetRef":{"group":"gateway.networking.k8s.io","kind":"Gateway","name":"eg","namespace":"default"},"type":"JSONPatch"}} + creationTimestamp: "2023-07-31T21:47:53Z" + generation: 1 + name: custom-response-patch-policy + namespace: default + resourceVersion: "10265" + uid: a35bda6e-a0cc-46d7-a63a-cee765174bc3 +spec: + jsonPatches: + - name: default/eg/http + operation: + op: add + path: /default_filter_chain/filters/0/typed_config/local_reply_config + value: + mappers: + - body: + inline_string: could not find what you are looking for + filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + type: type.googleapis.com/envoy.config.listener.v3.Listener + priority: 0 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch +status: + conditions: + - lastTransitionTime: "2023-07-31T21:48:19Z" + message: EnvoyPatchPolicy has been accepted. + observedGeneration: 1 + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2023-07-31T21:48:19Z" + message: successfully applied patches. + reason: Programmed + status: "True" + type: Programmed +``` + +### Offline + +* You can use [egctl x translate][] to validate the translated xds output. + +## Caveats + +This API will always be an unstable API and the same outcome cannot be garunteed +across versions for these reasons +* The Envoy Proxy API might deprecate and remove API fields +* Envoy Gateway might alter the xDS translation creating a different xDS output +such as changing the `name` field of resources. + +[EnvoyPatchPolicy]: https://gateway.envoyproxy.io/v0.5.0/api/extension_types.html#envoypatchpolicy +[EnvoyGateway]: https://gateway.envoyproxy.io/v0.5.0/api/config_types.html#envoygateway +[JSON Patch]: https://datatracker.ietf.org/doc/html/rfc6902 +[xDS]: https://www.envoyproxy.io/docs/envoy/v0.5.0/intro/arch_overview/operations/dynamic_configuration +[Local Reply Modification]: https://www.envoyproxy.io/docs/envoy/v0.5.0/configuration/http/http_conn_man/local_reply +[egctl x translate]: https://gateway.envoyproxy.io/v0.5.0/user/egctl.html#egctl-experimental-translate diff --git a/site/content/en/v0.5.0/user/gateway-address.md b/site/content/en/v0.5.0/user/gateway-address.md new file mode 100644 index 00000000000..c3f264aa35e --- /dev/null +++ b/site/content/en/v0.5.0/user/gateway-address.md @@ -0,0 +1,69 @@ +--- +title: "Gateway Address" +--- + +The Gateway API provides an optional [Addresses][] field through which Envoy Gateway can set addresses for Envoy Proxy Service. The currently supported addresses are: + +- [External IPs](#External-IPs) + +## Installation + +Install Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.5.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## External IPs + +Using the addresses in `Gateway.Spec.Addresses` as the [External IPs][] of Envoy Proxy Service, this will __require__ the address to be of type `IPAddress`. + +Install the GatewayClass, Gateway from quickstart: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.5.0/quickstart.yaml -n default +``` + +Set the address of the Gateway, the address settings here are for reference only: + +```shell +kubectl patch gateway eg --type=json --patch '[{ + "op": "add", + "path": "/spec/addresses", + "value": [{ + "type": "IPAddress", + "value": "1.2.3.4" + }] +}]' +``` + +Verify the Gateway status: + +```shell +kubectl get gateway + +NAME CLASS ADDRESS PROGRAMMED AGE +eg eg 1.2.3.4 True 14m +``` + +Verify the Envoy Proxy Service status: + +```shell +kubectl get service -n envoy-gateway-system + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +envoy-default-eg-64656661 LoadBalancer 10.96.236.219 1.2.3.4 80:31017/TCP 15m +envoy-gateway ClusterIP 10.96.192.76 18000/TCP 15m +envoy-gateway-metrics-service ClusterIP 10.96.124.73 8443/TCP 15m +``` + +__Note:__ If the `Gateway.Spec.Addresses` is explicitly set, it will be the only addresses that populates the Gateway status. + +[Addresses]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayAddress +[External IPs]: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips diff --git a/site/content/en/v0.5.0/user/gatewayapi-support.md b/site/content/en/v0.5.0/user/gatewayapi-support.md new file mode 100644 index 00000000000..368745d2f75 --- /dev/null +++ b/site/content/en/v0.5.0/user/gatewayapi-support.md @@ -0,0 +1,118 @@ +--- +title: "Gateway API Support" +--- + +As mentioned in the [system design][] document, Envoy Gateway's managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][] objects. Envoy Gateway supports configuration using the following Gateway API resources. + +## GatewayClass + +A [GatewayClass][] represents a "class" of gateways, i.e. which Gateways should be managed by Envoy Gateway. +Envoy Gateway supports managing __a single__ GatewayClass resource that matches its configured `controllerName` and +follows Gateway API guidelines for [resolving conflicts][] when multiple GatewayClasses exist with a matching +`controllerName`. + +__Note:__ If specifying GatewayClass [parameters reference][], it must refer to an [EnvoyProxy][] resource. + +## Gateway + +When a [Gateway][] resource is created that references the managed GatewayClass, Envoy Gateway will create and manage a +new Envoy Proxy deployment. Gateway API resources that reference this Gateway will configure this managed Envoy Proxy +deployment. + +## HTTPRoute + +An [HTTPRoute][] configures routing of HTTP traffic through one or more Gateways. The following HTTPRoute filters are +supported by Envoy Gateway: + +- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. +- `requestRedirect`: [RequestRedirects](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + configure policied for how requests that match the HTTPRoute should be modified and then redirected. +- `urlRewrite`: [UrlRewrites](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) + allow for modification of the request's hostname and path before it is proxied to its destination. +- `extensionRef`: [ExtensionRefs][] are used by Envoy Gateway to implement extended filters. Currently, Envoy Gateway + supports rate limiting and request authentication filters. For more information about these filters, refer to the + [rate limiting][] and [request authentication][] documentation. + +__Notes:__ +- The only [BackendRef][] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other destinations such + as arbitrary URLs is not possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TCPRoute + +A [TCPRoute][] configures routing of raw TCP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a TCP port number. + +__Note:__ A TCPRoute only supports proxying in non-transparent mode, i.e. the backend will see the source IP and port of +the Envoy Proxy instance instead of the client. + +## UDPRoute + +A [UDPRoute][] configures routing of raw UDP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a UDP port number. + +__Note:__ Similar to TCPRoutes, UDPRoutes only support proxying in non-transparent mode i.e. the backend will see the +source IP and port of the Envoy Proxy instance instead of the client. + +## GRPCRoute + +A [GRPCRoute][] configures routing of [gRPC][] requests through one or more Gateways. They offer request matching by +hostname, gRPC service, gRPC method, or HTTP/2 Header. Envoy Gateway supports the following filters on GRPCRoutes to +provide additional traffic processing: + +- `requestHeaderModifier`: [RequestHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter) + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. + +__Notes:__ +- The only [BackendRef][grpc-filter] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other + destinations such as arbitrary URLs is not currently possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TLSRoute + +A [TLSRoute][] configures routing of TCP traffic through one or more Gateways. However, unlike TCPRoutes, TLSRoutes +can match against TLS-specific metadata. + +## ReferenceGrant + +A [ReferenceGrant][] is used to allow a resource to reference another resource in a different namespace. Normally an +HTTPRoute created in namespace `foo` is not allowed to reference a Service in namespace `bar`. A ReferenceGrant permits +these types of cross-namespace references. Envoy Gateway supports the following ReferenceGrant use-cases: + +- Allowing an HTTPRoute, GRPCRoute, TLSRoute, UDPRoute, or TCPRoute to reference a Service in a different namespace. +- Allowing an HTTPRoute's `requestMirror` filter to include a BackendRef that references a Service in a different + namespace. +- Allowing a Gateway's [SecretObjectReference][] to reference a secret in a different namespace. + +[system design]: https://gateway.envoyproxy.io/v0.5.0/design/system-design.html +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[GatewayClass]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.GatewayClass +[parameters reference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.ParametersReference +[Gateway]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute +[Service]: https://kubernetes.io/docs/concepts/services-networking/service/ +[BackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPBackendRef +[TCPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute +[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[gRPC]: https://grpc.io/ +[TLSRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute +[ReferenceGrant]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.ReferenceGrant +[SecretObjectReference]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference +[rate limiting]: https://gateway.envoyproxy.io/v0.5.0/user/rate-limit.html +[request authentication]: https://gateway.envoyproxy.io/v0.5.0/user/authn.html +[EnvoyProxy]: https://gateway.envoyproxy.io/v0.5.0/api/config_types.html#envoyproxy +[resolving conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/?h=conflict#conflicts +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilterType +[grpc-filter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter diff --git a/site/content/en/v0.5.0/user/grpc-routing.md b/site/content/en/v0.5.0/user/grpc-routing.md new file mode 100644 index 00000000000..f95785c32cf --- /dev/null +++ b/site/content/en/v0.5.0/user/grpc-routing.md @@ -0,0 +1,202 @@ +--- +title: "GRPC Routing" +--- + +The [GRPCRoute][] resource allows users to configure gRPC routing by matching HTTP/2 traffic and forwarding it to backend gRPC servers. +To learn more about gRPC routing, refer to the [Gateway API documentation][]. + +## Prerequisites + +Install Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.5.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## Installation + +Install the gRPC routing example resources: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.5.0/examples/kubernetes/grpc-routing.yaml +``` + +The manifest installs a [GatewayClass][], [Gateway][], a Deployment, a Service, and a GRPCRoute resource. +The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. + +__Note:__ Envoy Gateway is configured by default to manage a GatewayClass with +`controllerName: gateway.envoyproxy.io/gatewayclass-controller`. + +## Verification + +Check the status of the GatewayClass: + +```shell +kubectl get gc --selector=example=grpc-routing +``` + +The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass. + +A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is +provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this +Gateway. Check the status of the Gateway: + +```shell +kubectl get gateways --selector=example=grpc-routing +``` + +The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also +provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend +services. + +Check the status of the GRPCRoute: + +```shell +kubectl get grpcroutes --selector=example=grpc-routing -o yaml +``` + +The status for the GRPCRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway. +The `example-route` matches any traffic for "grpc-example.com" and forwards it to the "yages" Service. + +## Testing the Configuration + +Before testing GRPC routing to the `yages` backend, get the Gateway's address. + +```shell +export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}') +``` + +Test GRPC routing to the `yages` backend using the [grpcurl][] command. + +```shell +grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +{ + "text": "pong" +} +``` + +Envoy Gateway also supports [gRPC-Web][] requests for this configuration. The below `curl` command can be used to send a grpc-Web request with over HTTP/2. You should receive the same response seen in the previous command. + +```shell +curl --http2-prior-knowledge -s ${GATEWAY_HOST}:80/yages.Echo/Ping -H 'Host: grpc-example.com' -H 'Content-Type: application/grpc-web-text' -H 'Accept: application/grpc-web-text' -XPOST -d'AAAAAAA=' | base64 -d +``` + +## GRPCRoute Match +The `matches` field can be used to restrict the route to a specific set of requests based on GRPC's service and/or method names. +It supports two match types: `Exact` and `RegularExpression`. + +### Exact + +`Exact` match is the default match type. + +The following example shows how to match a request based on the service and method names for `grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo`, +as well as a match for all services with a method name `Ping` which matches `yages.Echo/Ping` in our deployment. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something", + "foo" + ], +... +``` + +## Setting Request Headers + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + "headers": { + "Accept": [ + "*/*" + ], + "Set-Header": [ + "foo" + ], +... +``` + +## Removing Request Headers + +Headers can be removed from a request by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something" + ], +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +Check the logs of the pods and you will see that the original deployment and the new deployment each got a request: + +```shell +$ kubectl logs deploy/backend && kubectl logs deploy/backend-2 +... +Starting server, listening on port 3000 (http) +Echoing back request made to /get to client (10.42.0.10:41566) +Starting server, listening on port 3000 (http) +Echoing back request made to /get to client (10.42.0.10:45096) +``` + +## Multiple BackendRefs + +When an `HTTPRoute` has multiple `backendRefs` and an `HTTPRequestMirrorFilter`, traffic splitting will still behave the same as it normally would for the main `backendRefs` while the `backendRef` of the `HTTPRequestMirrorFilter` will continue receiving mirrored copies of the incoming requests. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: X-Foo: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< x-foo: value1 +< add-header: foo +< +... + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "X-Foo: value1" + ] +... +``` + +## Setting Response Headers + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: set-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< set-header: foo +< + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "set-header": value1" + ] +... +``` + +## Removing Response Headers + +Headers can be removed from a response by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: remove-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "remove-header": value1" + ] +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +## Multiple backendRefs + +If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is +configured. + +First, create a second instance of the example app from the quickstart: + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-75bcd4c969-lsxpz" +... +``` + +## Weighted backendRefs + +If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` +field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is +assumed to be `1`. + +The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of +requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the +HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. + +The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the +backend-2 service. + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< server: envoy +< content-length: 0 +< +``` + +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[backendRefs]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.BackendRef diff --git a/site/content/en/v0.5.0/user/http-urlrewrite.md b/site/content/en/v0.5.0/user/http-urlrewrite.md new file mode 100644 index 00000000000..60c0bebeeef --- /dev/null +++ b/site/content/en/v0.5.0/user/http-urlrewrite.md @@ -0,0 +1,297 @@ +--- +title: "HTTP URL Rewrite" +--- + +[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be +used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Rewrite URL Prefix Path + +You can configure to rewrite the prefix in the url like below. In this example, any curls to +`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. + +```shell +cat < GET /get/origin/path HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> + +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:03:28 GMT +< content-length: 503 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/replace/origin/path", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "fd84b842-9937-4fb5-83c7-61470d854b90" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. + +## Rewrite URL Full Path + +You can configure to rewrite the fullpath in the url like below. In this example, any request sent to +`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to +`http://${GATEWAY_HOST}/force/replace/fullpath`. + +```shell +cat < GET /get/origin/path/extra HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:09:31 GMT +< content-length: 512 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/force/replace/fullpath", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path/extra" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "8ab774d6-9ffa-4faa-abbb-f45b0db00895" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is +`/force/replace/fullpath`. + +## Rewrite Host Name + +You can configure to rewrite the hostname like below. In this example, any requests sent to +`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. + +```shell +cat < GET /get HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:15:15 GMT +< content-length: 481 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "envoygateway.io", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Forwarded-Host": [ + "path.rewrite.example" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39aa447c-97b9-45a3-a675-9fb266ab1af0" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. + +[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPURLRewriteFilter diff --git a/site/content/en/v0.5.0/user/proxy-observability.md b/site/content/en/v0.5.0/user/proxy-observability.md new file mode 100644 index 00000000000..a002faefdb1 --- /dev/null +++ b/site/content/en/v0.5.0/user/proxy-observability.md @@ -0,0 +1,135 @@ +--- +title: "Proxy Observability" +--- + +Envoy Gateway provides observability for the ControlPlane and the underlying EnvoyProxy instances. +This guide show you how to config proxy observability, includes metrics, logs, and traces. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +[FluentBit](https://fluentbit.io/) is used to collect logs from the EnvoyProxy instances and forward them to Loki. Install FluentBit: + +```shell +helm repo add fluent https://fluent.github.io/helm-charts +helm repo update +helm upgrade --install fluent-bit fluent/fluent-bit -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/fluent-bit/helm-values.yaml -n monitoring --create-namespace --version 0.30.4 +``` + +[Loki](https://grafana.com/oss/loki/) is used to store logs. Install Loki: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/loki/loki.yaml -n monitoring +``` + +[Tempo](https://grafana.com/oss/tempo/) is used to store traces. Install Tempo: + +```shell +helm repo add grafana https://grafana.github.io/helm-charts +helm repo update +helm upgrade --install tempo grafana/tempo -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/tempo/helm-values.yaml -n monitoring --create-namespace --version 1.3.1 +``` + +[OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) offers a vendor-agnostic implementation of how to receive, process and export telemetry data. +Install OTel-Collector: + +```shell +helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts +helm repo update +helm upgrade --install otel-collector open-telemetry/opentelemetry-collector -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/otel-collector/helm-values.yaml -n monitoring --create-namespace --version 0.60.0 +``` + +Expose endpoints: + +```shell +LOKI_IP=$(kubectl get svc loki -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +TEMPO_IP=$(kubectl get svc tempo -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +## Metrics + +By default, Envoy Gateway doesn't expose metrics of the EnvoyProxy instances. +You can enable metrics by setting the `telemetry.metrics.prometheus` in the `EnvoyProxy` CRD. + +Expose prometheus metrics endpoints: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/metric/prometheus.yaml +``` + +Verify metrics: + +```shell +export ENVOY_POD_NAME=$(kubectl get pod -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +kubectl port-forward pod/$ENVOY_POD_NAME -n envoy-gateway-system 19001:19001 + +# check metrics +curl localhost:19001/stats/prometheus | grep "default/backend/rule/0/match/0-www" +``` + +Envoy Gateway can send metrics to OpenTelemetry Sink. +Send metrics to OTel-Collector: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/metric/otel-sink.yaml +``` + +Verify OTel-Collector metrics: + +```shell +export OTEL_POD_NAME=$(kubectl get pod -n monitoring --selector=app.kubernetes.io/name=opentelemetry-collector -o jsonpath='{.items[0].metadata.name}') +kubectl port-forward pod/$OTEL_POD_NAME -n monitoring 19001:19001 + +# check metrics +curl localhost:19001/metrics | grep "default/backend/rule/0/match/0-www" +``` + +## Logs + +By default, Envoy Gateway send logs to stdout in [default text format](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage.html#default-format-string). +Verify logs from loki: + +```shell +curl -s "http://$LOKI_IP:3100/loki/api/v1/query_range" --data-urlencode "query={job=\"fluentbit\"}" | jq '.data.result[0].values' +``` + +If you want to disable it, set the `telemetry.accesslog.disable` to `true` in the `EnvoyProxy` CRD. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/accesslog/disable-accesslog.yaml +``` + +Envoy Gateway can send logs to OpenTelemetry Sink. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/accesslog/otel-accesslog.yaml +``` + +Verify logs from loki: + +```shell +curl -s "http://$LOKI_IP:3100/loki/api/v1/query_range" --data-urlencode "query={exporter=\"OTLP\"}" | jq '.data.result[0].values' +``` + +## Traces + +By default, Envoy Gateway doesn't send traces to OpenTelemetry Sink. +You can enable traces by setting the `telemetry.tracing` in the `EnvoyProxy` CRD. + +***Note:*** Envoy Gateway use 100% sample rate, which means all requests will be traced. This may cause performance issues. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/latest/examples/kubernetes/tracing/default.yaml +``` + +Verify traces from tempo: + +```shell +curl -s "http://$TEMPO_IP:3100/api/search" --data-urlencode "q={ component=envoy }" | jq .traces +``` + +```shell +curl -s "http://$TEMPO_IP:3100/api/traces/" | jq +``` diff --git a/site/content/en/v0.5.0/user/quickstart.md b/site/content/en/v0.5.0/user/quickstart.md new file mode 100644 index 00000000000..8443d6f323f --- /dev/null +++ b/site/content/en/v0.5.0/user/quickstart.md @@ -0,0 +1,100 @@ +--- +title: "Quickstart" +weight: 1 +--- + +This guide will help you get started with Envoy Gateway in a few simple steps. + +## Prerequisites + +A Kubernetes cluster. + +__Note:__ Refer to the [Compatibility Matrix](/blog/2022/10/01/versions/) for supported Kubernetes versions. + +## Installation + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.5.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.5.0/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/v0.5.0/quickstart.yaml + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` + +### External LoadBalancer Support + +You can also test the same functionality by sending traffic to the External IP. To get the external IP of the +Envoy service, run: + +```shell +export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace +`ip` in the above command with `hostname`. + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get +``` + +## Clean-Up + +Use the steps in this section to uninstall everything from the quickstart guide. + +Delete the GatewayClass, Gateway, HTTPRoute and Example App: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.5.0/quickstart.yaml --ignore-not-found=true +``` + +Delete the Gateway API CRDs and Envoy Gateway: + +```shell +helm uninstall eg -n envoy-gateway-system +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. diff --git a/site/content/en/v0.5.0/user/rate-limit.md b/site/content/en/v0.5.0/user/rate-limit.md new file mode 100644 index 00000000000..bddca2d52bb --- /dev/null +++ b/site/content/en/v0.5.0/user/rate-limit.md @@ -0,0 +1,810 @@ +--- +title: "Rate Limit" +--- + +Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + +Here are some reasons why you may want to implements Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied +i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit +if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. + +Envoy Gateway introduces a new CRD called [RateLimitFilter][] that allows the user to describe their rate limit intent. This instantiated resource +can be linked to a [HTTPRoute][] resource using an [ExtensionRef][] filter. + +## Prerequisites + +### Install Envoy Gateway + +* Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +### Install Redis + +* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. +Lets install a Redis deployment in the `redis-system` namespce. + +```shell +cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com +; (1 server found) +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 +;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 +;; WARNING: recursion requested but not available + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +; COOKIE: 24fb86eba96ebf62 (echoed) +;; QUESTION SECTION: +;foo.bar.com. IN A + +;; ADDITIONAL SECTION: +foo.bar.com. 0 IN A 10.244.0.19 +_udp.foo.bar.com. 0 IN SRV 0 0 42376 . + +;; Query time: 1 msec +;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) +;; WHEN: Fri Jan 13 10:20:34 UTC 2023 +;; MSG SIZE rcvd: 114 +``` + +## Clean-Up + +Follow the steps from the [Quickstart Guide](../quickstart) to uninstall Envoy Gateway. + +Delete the CoreDNS example manifest and the UDPRoute: + +```shell +kubectl delete deploy/coredns +kubectl delete service/coredns +kubectl delete cm/coredns +kubectl delete udproute/coredns +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[UDPRoute]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/v0.5.0/configuration/listeners/udp_filters/udp_proxy diff --git a/site/content/en/v0.6.0/_index.md b/site/content/en/v0.6.0/_index.md new file mode 100644 index 00000000000..567b43bfe36 --- /dev/null +++ b/site/content/en/v0.6.0/_index.md @@ -0,0 +1,21 @@ ++++ +title = "Welcome to Envoy Gateway" +linktitle = "Documentation" +description = "Envoy Gateway Documents" + +[[cascade]] +type = "docs" ++++ + +{{% alert title="Note" color="primary" %}} + +This project is under **active** development. Many features are not complete. We would love for you to [Get Involved](contributions/)! + +{{% /alert %}} + +Envoy Gateway is an open source project for managing [Envoy Proxy](https://www.envoyproxy.io/) as a standalone or Kubernetes-based application +gateway. [Gateway API](https://gateway-api.sigs.k8s.io/) resources are used to dynamically provision and configure the managed Envoy Proxies. + +![architecture](/img/traffic.png) + +## Ready to get started? diff --git a/site/content/en/v0.6.0/api/_index.md b/site/content/en/v0.6.0/api/_index.md new file mode 100644 index 00000000000..396d9ffcefc --- /dev/null +++ b/site/content/en/v0.6.0/api/_index.md @@ -0,0 +1,5 @@ +--- +title: "API" +description: This section includes APIs of Envoy Gateway. +weight: 80 +--- diff --git a/site/content/en/v0.6.0/api/extension_types.md b/site/content/en/v0.6.0/api/extension_types.md new file mode 100644 index 00000000000..08b53e29c85 --- /dev/null +++ b/site/content/en/v0.6.0/api/extension_types.md @@ -0,0 +1,1706 @@ ++++ +title = "API Reference" ++++ + + +## Packages +- [gateway.envoyproxy.io/v1alpha1](#gatewayenvoyproxyiov1alpha1) + + +## gateway.envoyproxy.io/v1alpha1 + +Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io +API group. + + +### Resource Types +- [BackendTrafficPolicy](#backendtrafficpolicy) +- [BackendTrafficPolicyList](#backendtrafficpolicylist) +- [ClientTrafficPolicy](#clienttrafficpolicy) +- [ClientTrafficPolicyList](#clienttrafficpolicylist) +- [EnvoyGateway](#envoygateway) +- [EnvoyPatchPolicy](#envoypatchpolicy) +- [EnvoyPatchPolicyList](#envoypatchpolicylist) +- [EnvoyProxy](#envoyproxy) +- [SecurityPolicy](#securitypolicy) +- [SecurityPolicyList](#securitypolicylist) + + + +#### BackendTrafficPolicy + + + +BackendTrafficPolicy allows the user to configure the behavior of the connection between the downstream client and Envoy Proxy listener. + +_Appears in:_ +- [BackendTrafficPolicyList](#backendtrafficpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `BackendTrafficPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[BackendTrafficPolicySpec](#backendtrafficpolicyspec)_ | spec defines the desired state of BackendTrafficPolicy. | + + +#### BackendTrafficPolicyList + + + +BackendTrafficPolicyList contains a list of BackendTrafficPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `BackendTrafficPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[BackendTrafficPolicy](#backendtrafficpolicy) array_ | | + + +#### BackendTrafficPolicySpec + + + +spec defines the desired state of BackendTrafficPolicy. + +_Appears in:_ +- [BackendTrafficPolicy](#backendtrafficpolicy) + +| Field | Description | +| --- | --- | +| `targetRef` _[PolicyTargetReferenceWithSectionName](#policytargetreferencewithsectionname)_ | targetRef is the name of the resource this policy is being attached to. This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway. | +| `rateLimit` _[RateLimitSpec](#ratelimitspec)_ | RateLimit allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. | +| `loadBalancer` _[LoadBalancer](#loadbalancer)_ | LoadBalancer policy to apply when routing traffic from the gateway to the backend endpoints | + + + + +#### BootstrapType + +_Underlying type:_ `string` + +BootstrapType defines the types of bootstrap supported by Envoy Gateway. + +_Appears in:_ +- [ProxyBootstrap](#proxybootstrap) + + + +#### CORS + + + +CORS defines the configuration for Cross-Origin Resource Sharing (CORS). + +_Appears in:_ +- [SecurityPolicySpec](#securitypolicyspec) + +| Field | Description | +| --- | --- | +| `allowOrigins` _[StringMatch](#stringmatch) array_ | AllowOrigins defines the origins that are allowed to make requests. | +| `allowMethods` _string array_ | AllowMethods defines the methods that are allowed to make requests. | +| `allowHeaders` _string array_ | AllowHeaders defines the headers that are allowed to be sent with requests. | +| `exposeHeaders` _string array_ | ExposeHeaders defines the headers that can be exposed in the responses. | +| `maxAge` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ | MaxAge defines how long the results of a preflight request can be cached. | + + +#### ClaimToHeader + + + +ClaimToHeader defines a configuration to convert JWT claims into HTTP headers + +_Appears in:_ +- [JWTProvider](#jwtprovider) + +| Field | Description | +| --- | --- | +| `header` _string_ | Header defines the name of the HTTP request header that the JWT Claim will be saved into. | +| `claim` _string_ | Claim is the JWT Claim that should be saved into the header : it can be a nested claim of type (eg. "claim.nested.key", "sub"). The nested claim name must use dot "." to separate the JSON name path. | + + +#### ClientTrafficPolicy + + + +ClientTrafficPolicy allows the user to configure the behavior of the connection between the downstream client and Envoy Proxy listener. + +_Appears in:_ +- [ClientTrafficPolicyList](#clienttrafficpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `ClientTrafficPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[ClientTrafficPolicySpec](#clienttrafficpolicyspec)_ | Spec defines the desired state of ClientTrafficPolicy. | + + +#### ClientTrafficPolicyList + + + +ClientTrafficPolicyList contains a list of ClientTrafficPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `ClientTrafficPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[ClientTrafficPolicy](#clienttrafficpolicy) array_ | | + + +#### ClientTrafficPolicySpec + + + +ClientTrafficPolicySpec defines the desired state of ClientTrafficPolicy. + +_Appears in:_ +- [ClientTrafficPolicy](#clienttrafficpolicy) + +| Field | Description | +| --- | --- | +| `targetRef` _[PolicyTargetReferenceWithSectionName](#policytargetreferencewithsectionname)_ | TargetRef is the name of the Gateway resource this policy is being attached to. This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway. TargetRef | +| `tcpKeepalive` _[TCPKeepalive](#tcpkeepalive)_ | TcpKeepalive settings associated with the downstream client connection. If defined, sets SO_KEEPALIVE on the listener socket to enable TCP Keepalives. Disabled by default. | + + + + +#### ConsistentHash + + + +ConsistentHash defines the configuration related to the consistent hash load balancer policy + +_Appears in:_ +- [LoadBalancer](#loadbalancer) + +| Field | Description | +| --- | --- | +| `type` _[ConsistentHashType](#consistenthashtype)_ | | + + +#### ConsistentHashType + +_Underlying type:_ `string` + +ConsistentHashType defines the type of input to hash on. + +_Appears in:_ +- [ConsistentHash](#consistenthash) + + + +#### CustomTag + + + + + +_Appears in:_ +- [ProxyTracing](#proxytracing) + +| Field | Description | +| --- | --- | +| `type` _[CustomTagType](#customtagtype)_ | Type defines the type of custom tag. | +| `literal` _[LiteralCustomTag](#literalcustomtag)_ | Literal adds hard-coded value to each span. It's required when the type is "Literal". | +| `environment` _[EnvironmentCustomTag](#environmentcustomtag)_ | Environment adds value from environment variable to each span. It's required when the type is "Environment". | +| `requestHeader` _[RequestHeaderCustomTag](#requestheadercustomtag)_ | RequestHeader adds value from request header to each span. It's required when the type is "RequestHeader". | + + +#### CustomTagType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [CustomTag](#customtag) + + + +#### EnvironmentCustomTag + + + +EnvironmentCustomTag adds value from environment variable to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines the name of the environment variable which to extract the value from. | +| `defaultValue` _string_ | DefaultValue defines the default value to use if the environment variable is not set. | + + +#### EnvoyGateway + + + +EnvoyGateway is the schema for the envoygateways API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyGateway` +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | +| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | +| `telemetry` _[EnvoyGatewayTelemetry](#envoygatewaytelemetry)_ | Telemetry defines the desired control plane telemetry related abilities. If unspecified, the telemetry is used with default configuration. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | +| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | + + +#### EnvoyGatewayAdmin + + + +EnvoyGatewayAdmin defines the Envoy Gateway Admin configuration. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `address` _[EnvoyGatewayAdminAddress](#envoygatewayadminaddress)_ | Address defines the address of Envoy Gateway Admin Server. | +| `enableDumpConfig` _boolean_ | EnableDumpConfig defines if enable dump config in Envoy Gateway logs. | +| `enablePprof` _boolean_ | EnablePprof defines if enable pprof in Envoy Gateway Admin Server. | + + +#### EnvoyGatewayAdminAddress + + + +EnvoyGatewayAdminAddress defines the Envoy Gateway Admin Address configuration. + +_Appears in:_ +- [EnvoyGatewayAdmin](#envoygatewayadmin) + +| Field | Description | +| --- | --- | +| `port` _integer_ | Port defines the port the admin server is exposed on. | +| `host` _string_ | Host defines the admin server hostname. | + + +#### EnvoyGatewayCustomProvider + + + +EnvoyGatewayCustomProvider defines configuration for the Custom provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `resource` _[EnvoyGatewayResourceProvider](#envoygatewayresourceprovider)_ | Resource defines the desired resource provider. This provider is used to specify the provider to be used to retrieve the resource configurations such as Gateway API resources | +| `infrastructure` _[EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider)_ | Infrastructure defines the desired infrastructure provider. This provider is used to specify the provider to be used to provide an environment to deploy the out resources like the Envoy Proxy data plane. | + + +#### EnvoyGatewayFileResourceProvider + + + +EnvoyGatewayFileResourceProvider defines configuration for the File Resource provider. + +_Appears in:_ +- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) + +| Field | Description | +| --- | --- | +| `paths` _string array_ | Paths are the paths to a directory or file containing the resource configuration. Recursive sub directories are not currently supported. | + + +#### EnvoyGatewayHostInfrastructureProvider + + + +EnvoyGatewayHostInfrastructureProvider defines configuration for the Host Infrastructure provider. + +_Appears in:_ +- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) + + + +#### EnvoyGatewayInfrastructureProvider + + + +EnvoyGatewayInfrastructureProvider defines configuration for the Custom Infrastructure provider. + +_Appears in:_ +- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) + +| Field | Description | +| --- | --- | +| `type` _[InfrastructureProviderType](#infrastructureprovidertype)_ | Type is the type of infrastructure providers to use. Supported types are "Host". | +| `host` _[EnvoyGatewayHostInfrastructureProvider](#envoygatewayhostinfrastructureprovider)_ | Host defines the configuration of the Host provider. Host provides runtime deployment of the data plane as a child process on the host environment. | + + +#### EnvoyGatewayKubernetesProvider + + + +EnvoyGatewayKubernetesProvider defines configuration for the Kubernetes provider. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) + +| Field | Description | +| --- | --- | +| `rateLimitDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | RateLimitDeployment defines the desired state of the Envoy ratelimit deployment resource. If unspecified, default settings for the managed Envoy ratelimit deployment resource are applied. | +| `watch` _[KubernetesWatchMode](#kuberneteswatchmode)_ | Watch holds configuration of which input resources should be watched and reconciled. | +| `deploy` _[KubernetesDeployMode](#kubernetesdeploymode)_ | Deploy holds configuration of how output managed resources such as the Envoy Proxy data plane should be deployed | +| `overwrite_control_plane_certs` _boolean_ | OverwriteControlPlaneCerts updates the secrets containing the control plane certs, when set. | + + +#### EnvoyGatewayLogComponent + +_Underlying type:_ `string` + +EnvoyGatewayLogComponent defines a component that supports a configured logging level. + +_Appears in:_ +- [EnvoyGatewayLogging](#envoygatewaylogging) + + + +#### EnvoyGatewayLogging + + + +EnvoyGatewayLogging defines logging for Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[EnvoyGatewayLogComponent](#envoygatewaylogcomponent), values:[LogLevel](#loglevel))_ | Level is the logging level. If unspecified, defaults to "info". EnvoyGatewayLogComponent options: default/provider/gateway-api/xds-translator/xds-server/infrastructure/global-ratelimit. LogLevel options: debug/info/error/warn. | + + +#### EnvoyGatewayMetricSink + + + +EnvoyGatewayMetricSink defines control plane metric sinks where metrics are sent to. + +_Appears in:_ +- [EnvoyGatewayMetrics](#envoygatewaymetrics) + +| Field | Description | +| --- | --- | +| `type` _[MetricSinkType](#metricsinktype)_ | Type defines the metric sink type. EG control plane currently supports OpenTelemetry. | +| `openTelemetry` _[EnvoyGatewayOpenTelemetrySink](#envoygatewayopentelemetrysink)_ | OpenTelemetry defines the configuration for OpenTelemetry sink. It's required if the sink type is OpenTelemetry. | + + +#### EnvoyGatewayMetrics + + + +EnvoyGatewayMetrics defines control plane push/pull metrics configurations. + +_Appears in:_ +- [EnvoyGatewayTelemetry](#envoygatewaytelemetry) + +| Field | Description | +| --- | --- | +| `sinks` _[EnvoyGatewayMetricSink](#envoygatewaymetricsink) array_ | Sinks defines the metric sinks where metrics are sent to. | +| `prometheus` _[EnvoyGatewayPrometheusProvider](#envoygatewayprometheusprovider)_ | Prometheus defines the configuration for prometheus endpoint. | + + +#### EnvoyGatewayOpenTelemetrySink + + + + + +_Appears in:_ +- [EnvoyGatewayMetricSink](#envoygatewaymetricsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the sink service hostname. | +| `protocol` _string_ | Protocol define the sink service protocol. | +| `port` _integer_ | Port defines the port the sink service is exposed on. | + + +#### EnvoyGatewayPrometheusProvider + + + +EnvoyGatewayPrometheusProvider will expose prometheus endpoint in pull mode. + +_Appears in:_ +- [EnvoyGatewayMetrics](#envoygatewaymetrics) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable defines if disables the prometheus metrics in pull mode. | + + +#### EnvoyGatewayProvider + + + +EnvoyGatewayProvider defines the desired configuration of a provider. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of provider to use. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider)_ | Kubernetes defines the configuration of the Kubernetes provider. Kubernetes provides runtime configuration via the Kubernetes API. | +| `custom` _[EnvoyGatewayCustomProvider](#envoygatewaycustomprovider)_ | Custom defines the configuration for the Custom provider. This provider allows you to define a specific resource provider and a infrastructure provider. | + + +#### EnvoyGatewayResourceProvider + + + +EnvoyGatewayResourceProvider defines configuration for the Custom Resource provider. + +_Appears in:_ +- [EnvoyGatewayCustomProvider](#envoygatewaycustomprovider) + +| Field | Description | +| --- | --- | +| `type` _[ResourceProviderType](#resourceprovidertype)_ | Type is the type of resource provider to use. Supported types are "File". | +| `file` _[EnvoyGatewayFileResourceProvider](#envoygatewayfileresourceprovider)_ | File defines the configuration of the File provider. File provides runtime configuration defined by one or more files. | + + +#### EnvoyGatewaySpec + + + +EnvoyGatewaySpec defines the desired state of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) + +| Field | Description | +| --- | --- | +| `gateway` _[Gateway](#gateway)_ | Gateway defines desired Gateway API specific configuration. If unset, default configuration parameters will apply. | +| `provider` _[EnvoyGatewayProvider](#envoygatewayprovider)_ | Provider defines the desired provider and provider-specific configuration. If unspecified, the Kubernetes provider is used with default configuration parameters. | +| `logging` _[EnvoyGatewayLogging](#envoygatewaylogging)_ | Logging defines logging parameters for Envoy Gateway. | +| `admin` _[EnvoyGatewayAdmin](#envoygatewayadmin)_ | Admin defines the desired admin related abilities. If unspecified, the Admin is used with default configuration parameters. | +| `telemetry` _[EnvoyGatewayTelemetry](#envoygatewaytelemetry)_ | Telemetry defines the desired control plane telemetry related abilities. If unspecified, the telemetry is used with default configuration. | +| `rateLimit` _[RateLimit](#ratelimit)_ | RateLimit defines the configuration associated with the Rate Limit service deployed by Envoy Gateway required to implement the Global Rate limiting functionality. The specific rate limit service used here is the reference implementation in Envoy. For more details visit https://github.com/envoyproxy/ratelimit. This configuration is unneeded for "Local" rate limiting. | +| `extensionManager` _[ExtensionManager](#extensionmanager)_ | ExtensionManager defines an extension manager to register for the Envoy Gateway Control Plane. | +| `extensionApis` _[ExtensionAPISettings](#extensionapisettings)_ | ExtensionAPIs defines the settings related to specific Gateway API Extensions implemented by Envoy Gateway | + + +#### EnvoyGatewayTelemetry + + + +EnvoyGatewayTelemetry defines telemetry configurations for envoy gateway control plane. Control plane will focus on metrics observability telemetry and tracing telemetry later. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `metrics` _[EnvoyGatewayMetrics](#envoygatewaymetrics)_ | Metrics defines metrics configuration for envoy gateway. | + + +#### EnvoyJSONPatchConfig + + + +EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource using JSONPatch semantic + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyResourceType](#envoyresourcetype)_ | Type is the typed URL of the Envoy xDS Resource | +| `name` _string_ | Name is the name of the resource | +| `operation` _[JSONPatchOperation](#jsonpatchoperation)_ | Patch defines the JSON Patch Operation | + + +#### EnvoyPatchPolicy + + + +EnvoyPatchPolicy allows the user to modify the generated Envoy xDS resources by Envoy Gateway using this patch API + +_Appears in:_ +- [EnvoyPatchPolicyList](#envoypatchpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyPatchPolicySpec](#envoypatchpolicyspec)_ | Spec defines the desired state of EnvoyPatchPolicy. | + + +#### EnvoyPatchPolicyList + + + +EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[EnvoyPatchPolicy](#envoypatchpolicy) array_ | | + + +#### EnvoyPatchPolicySpec + + + +EnvoyPatchPolicySpec defines the desired state of EnvoyPatchPolicy. + +_Appears in:_ +- [EnvoyPatchPolicy](#envoypatchpolicy) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyPatchType](#envoypatchtype)_ | Type decides the type of patch. Valid EnvoyPatchType values are "JSONPatch". | +| `jsonPatches` _[EnvoyJSONPatchConfig](#envoyjsonpatchconfig) array_ | JSONPatch defines the JSONPatch configuration. | +| `targetRef` _[PolicyTargetReference](#policytargetreference)_ | TargetRef is the name of the Gateway API resource this policy is being attached to. Currently only attaching to Gateway is supported This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway TargetRef | +| `priority` _integer_ | Priority of the EnvoyPatchPolicy. If multiple EnvoyPatchPolicies are applied to the same TargetRef, they will be applied in the ascending order of the priority i.e. int32.min has the highest priority and int32.max has the lowest priority. Defaults to 0. | + + + + +#### EnvoyPatchType + +_Underlying type:_ `string` + +EnvoyPatchType specifies the types of Envoy patching mechanisms. + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + + + +#### EnvoyProxy + + + +EnvoyProxy is the schema for the envoyproxies API. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyProxy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyProxySpec](#envoyproxyspec)_ | EnvoyProxySpec defines the desired state of EnvoyProxy. | + + +#### EnvoyProxyKubernetesProvider + + + +EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource provider. + +_Appears in:_ +- [EnvoyProxyProvider](#envoyproxyprovider) + +| Field | Description | +| --- | --- | +| `envoyDeployment` _[KubernetesDeploymentSpec](#kubernetesdeploymentspec)_ | EnvoyDeployment defines the desired state of the Envoy deployment resource. If unspecified, default settings for the managed Envoy deployment resource are applied. | +| `envoyService` _[KubernetesServiceSpec](#kubernetesservicespec)_ | EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings for the managed Envoy service resource are applied. | + + +#### EnvoyProxyProvider + + + +EnvoyProxyProvider defines the desired state of a resource provider. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `type` _[ProviderType](#providertype)_ | Type is the type of resource provider to use. A resource provider provides infrastructure resources for running the data plane, e.g. Envoy proxy, and optional auxiliary control planes. Supported types are "Kubernetes". | +| `kubernetes` _[EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider)_ | Kubernetes defines the desired state of the Kubernetes resource provider. Kubernetes provides infrastructure resources for running the data plane, e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings for managed Kubernetes resources are applied. | + + +#### EnvoyProxySpec + + + +EnvoyProxySpec defines the desired state of EnvoyProxy. + +_Appears in:_ +- [EnvoyProxy](#envoyproxy) + +| Field | Description | +| --- | --- | +| `provider` _[EnvoyProxyProvider](#envoyproxyprovider)_ | Provider defines the desired resource provider and provider-specific configuration. If unspecified, the "Kubernetes" resource provider is used with default configuration parameters. | +| `logging` _[ProxyLogging](#proxylogging)_ | Logging defines logging parameters for managed proxies. | +| `telemetry` _[ProxyTelemetry](#proxytelemetry)_ | Telemetry defines telemetry parameters for managed proxies. | +| `bootstrap` _[ProxyBootstrap](#proxybootstrap)_ | Bootstrap defines the Envoy Bootstrap as a YAML string. Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap to learn more about the syntax. If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration set by Envoy Gateway. Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources from it are not configurable and will result in the `EnvoyProxy` resource being rejected. Backward compatibility across minor versions is not guaranteed. We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. | +| `concurrency` _integer_ | Concurrency defines the number of worker threads to run. If unset, it defaults to the number of cpuset threads on the platform. | +| `mergeGateways` _boolean_ | MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure. Setting this field to true would merge all Gateway Listeners under the parent Gateway Class. This means that the port, protocol and hostname tuple must be unique for every listener. If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. | + + + + +#### EnvoyResourceType + +_Underlying type:_ `string` + +EnvoyResourceType specifies the type URL of the Envoy resource. + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + + + +#### ExtensionAPISettings + + + +ExtensionAPISettings defines the settings specific to Gateway API Extensions. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `enableEnvoyPatchPolicy` _boolean_ | EnableEnvoyPatchPolicy enables Envoy Gateway to reconcile and implement the EnvoyPatchPolicy resources. | + + +#### ExtensionHooks + + + +ExtensionHooks defines extension hooks across all supported runners + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `xdsTranslator` _[XDSTranslatorHooks](#xdstranslatorhooks)_ | XDSTranslator defines all the supported extension hooks for the xds-translator runner | + + +#### ExtensionManager + + + +ExtensionManager defines the configuration for registering an extension manager to the Envoy Gateway control plane. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `resources` _[GroupVersionKind](#groupversionkind) array_ | Resources defines the set of K8s resources the extension will handle. | +| `hooks` _[ExtensionHooks](#extensionhooks)_ | Hooks defines the set of hooks the extension supports | +| `service` _[ExtensionService](#extensionservice)_ | Service defines the configuration of the extension service that the Envoy Gateway Control Plane will call through extension hooks. | + + +#### ExtensionService + + + +ExtensionService defines the configuration for connecting to a registered extension service. + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `tls` _[ExtensionTLS](#extensiontls)_ | TLS defines TLS configuration for communication between Envoy Gateway and the extension service. | + + +#### ExtensionTLS + + + +ExtensionTLS defines the TLS configuration when connecting to an extension service + +_Appears in:_ +- [ExtensionService](#extensionservice) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | CertificateRef contains a references to objects (Kubernetes objects or otherwise) that contains a TLS certificate and private keys. These certificates are used to establish a TLS handshake to the extension server. + CertificateRef can only reference a Kubernetes Secret at this time. | + + +#### FileEnvoyProxyAccessLog + + + + + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + +| Field | Description | +| --- | --- | +| `path` _string_ | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). | + + +#### Gateway + + + +Gateway defines the desired Gateway API configuration of Envoy Gateway. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `controllerName` _string_ | ControllerName defines the name of the Gateway API controller. If unspecified, defaults to "gateway.envoyproxy.io/gatewayclass-controller". See the following for additional details: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1.GatewayClass | + + +#### GlobalRateLimit + + + +GlobalRateLimit defines global rate limit configuration. + +_Appears in:_ +- [RateLimitSpec](#ratelimitspec) + +| Field | Description | +| --- | --- | +| `rules` _[RateLimitRule](#ratelimitrule) array_ | Rules are a list of RateLimit selectors and limits. Each rule and its associated limit is applied in a mutually exclusive way i.e. if multiple rules get selected, each of their associated limits get applied, so a single traffic request might increase the rate limit counters for multiple rules if selected. | + + +#### GroupVersionKind + + + +GroupVersionKind unambiguously identifies a Kind. It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind + +_Appears in:_ +- [ExtensionManager](#extensionmanager) + +| Field | Description | +| --- | --- | +| `group` _string_ | | +| `version` _string_ | | +| `kind` _string_ | | + + +#### HeaderMatch + + + +HeaderMatch defines the match attributes within the HTTP Headers of the request. + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + + + +#### InfrastructureProviderType + +_Underlying type:_ `string` + +InfrastructureProviderType defines the types of custom infrastructure providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayInfrastructureProvider](#envoygatewayinfrastructureprovider) + + + +#### JSONPatchOperation + + + +JSONPatchOperation defines the JSON Patch Operation as defined in https://datatracker.ietf.org/doc/html/rfc6902 + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + +| Field | Description | +| --- | --- | +| `op` _[JSONPatchOperationType](#jsonpatchoperationtype)_ | Op is the type of operation to perform | +| `path` _string_ | Path is the location of the target document/field where the operation will be performed Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. | +| `value` _[JSON](#json)_ | Value is the new value of the path location. | + + +#### JSONPatchOperationType + +_Underlying type:_ `string` + +JSONPatchOperationType specifies the JSON Patch operations that can be performed. + +_Appears in:_ +- [JSONPatchOperation](#jsonpatchoperation) + + + +#### JWT + + + +JWT defines the configuration for JSON Web Token (JWT) authentication. + +_Appears in:_ +- [SecurityPolicySpec](#securitypolicyspec) + +| Field | Description | +| --- | --- | +| `providers` _[JWTProvider](#jwtprovider) array_ | Providers defines the JSON Web Token (JWT) authentication provider type. + When multiple JWT providers are specified, the JWT is considered valid if any of the providers successfully validate the JWT. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | + + +#### JWTProvider + + + +JWTProvider defines how a JSON Web Token (JWT) can be verified. + +_Appears in:_ +- [JWT](#jwt) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines a unique name for the JWT provider. A name can have a variety of forms, including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels. | +| `issuer` _string_ | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. | +| `audiences` _string array_ | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. | +| `remoteJWKS` _[RemoteJWKS](#remotejwks)_ | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. | +| `claimToHeaders` _[ClaimToHeader](#claimtoheader) array_ | ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers For examples, following config: The claim must be of type; string, int, double, bool. Array type claims are not supported | + + +#### KubernetesContainerSpec + + + +KubernetesContainerSpec defines the desired state of the Kubernetes container resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#envvar-v1-core) array_ | List of environment variables to set in the container. | +| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources required by this container. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | +| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#securitycontext-v1-core)_ | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| `image` _string_ | Image specifies the EnvoyProxy container image to be used, instead of the default image. | +| `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volumemount-v1-core) array_ | VolumeMounts are volumes to mount into the container's filesystem. Cannot be updated. | + + +#### KubernetesDeployMode + + + +KubernetesDeployMode holds configuration for how to deploy managed resources such as the Envoy Proxy data plane fleet. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) + + + +#### KubernetesDeploymentSpec + + + +KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `replicas` _integer_ | Replicas is the number of desired pods. Defaults to 1. | +| `strategy` _[DeploymentStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#deploymentstrategy-v1-apps)_ | The deployment strategy to use to replace existing pods with new ones. | +| `pod` _[KubernetesPodSpec](#kubernetespodspec)_ | Pod defines the desired specification of pod. | +| `container` _[KubernetesContainerSpec](#kubernetescontainerspec)_ | Container defines the desired specification of main container. | +| `initContainers` _[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#container-v1-core) array_ | List of initialization containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | + + +#### KubernetesPodSpec + + + +KubernetesPodSpec defines the desired state of the Kubernetes pod resource. + +_Appears in:_ +- [KubernetesDeploymentSpec](#kubernetesdeploymentspec) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations are the annotations that should be appended to the pods. By default, no pod annotations are appended. | +| `labels` _object (keys:string, values:string)_ | Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. | +| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)_ | SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. | +| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#affinity-v1-core)_ | If specified, the pod's scheduling constraints. | +| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#toleration-v1-core) array_ | If specified, the pod's tolerations. | +| `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#volume-v1-core) array_ | Volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes | + + +#### KubernetesServiceSpec + + + +KubernetesServiceSpec defines the desired state of the Kubernetes service resource. + +_Appears in:_ +- [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) + +| Field | Description | +| --- | --- | +| `annotations` _object (keys:string, values:string)_ | Annotations that should be appended to the service. By default, no annotations are appended. | +| `type` _[ServiceType](#servicetype)_ | Type determines how the Service is exposed. Defaults to LoadBalancer. Valid options are ClusterIP, LoadBalancer and NodePort. "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | +| `loadBalancerClass` _string_ | LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider implementation if more than one are available or is otherwise expected to be specified | +| `allocateLoadBalancerNodePorts` _boolean_ | AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a value), those requests will be respected, regardless of this field. This field may only be set for services with type LoadBalancer and will be cleared if the type is changed to any other type. | +| `loadBalancerIP` _string_ | LoadBalancerIP defines the IP Address of the underlying load balancer service. This field may be ignored if the load balancer provider does not support this feature. This field has been deprecated in Kubernetes, but it is still used for setting the IP Address in some cloud providers such as GCP. | + + +#### KubernetesWatchMode + + + +KubernetesWatchMode holds the configuration for which input resources to watch and reconcile. + +_Appears in:_ +- [EnvoyGatewayKubernetesProvider](#envoygatewaykubernetesprovider) + +| Field | Description | +| --- | --- | +| `Type` _[KubernetesWatchModeType](#kuberneteswatchmodetype)_ | Type indicates what watch mode to use. KubernetesWatchModeTypeNamespaces and KubernetesWatchModeTypeNamespaceSelectors are currently supported By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources from all namespaces. | +| `Namespaces` _string array_ | Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped resources such as Gateway, HTTPRoute and Service. Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set | +| `namespaces` _string array_ | NamespaceSelectors holds a list of labels that namespaces have to have in order to be watched. Note this doesn't set the informer to watch the namespaces with the given labels. Informer still watches all namespaces. But the events for objects whois namespce have no given labels will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set | + + +#### KubernetesWatchModeType + +_Underlying type:_ `string` + +KubernetesWatchModeType defines the type of KubernetesWatchMode + +_Appears in:_ +- [KubernetesWatchMode](#kuberneteswatchmode) + + + +#### LiteralCustomTag + + + +LiteralCustomTag adds hard-coded value to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `value` _string_ | Value defines the hard-coded value to add to each span. | + + +#### LoadBalancer + + + +LoadBalancer defines the load balancer policy to be applied. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[LoadBalancerType](#loadbalancertype)_ | Type decides the type of Load Balancer policy. Valid LoadBalancerType values are "ConsistentHash", "LeastRequest", "Random", "RoundRobin", | +| `consistentHash` _[ConsistentHash](#consistenthash)_ | ConsistentHash defines the configuration when the load balancer type is set to ConsistentHash | + + +#### LoadBalancerType + +_Underlying type:_ `string` + +LoadBalancerType specifies the types of LoadBalancer. + +_Appears in:_ +- [LoadBalancer](#loadbalancer) + + + +#### LogLevel + +_Underlying type:_ `string` + +LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. + +_Appears in:_ +- [EnvoyGatewayLogging](#envoygatewaylogging) +- [ProxyLogging](#proxylogging) + + + +#### Match + + + +Match defines the stats match configuration. + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `type` _[MatcherType](#matchertype)_ | MatcherType defines the stats matcher type | +| `value` _string_ | | + + +#### MatchType + +_Underlying type:_ `string` + +MatchType specifies the semantics of how a string value should be compared. Valid MatchType values are "Exact", "Prefix", "Suffix", "RegularExpression". + +_Appears in:_ +- [StringMatch](#stringmatch) + + + +#### MatcherType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [Match](#match) + + + +#### MetricSinkType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [EnvoyGatewayMetricSink](#envoygatewaymetricsink) +- [ProxyMetricSink](#proxymetricsink) + + + +#### OpenTelemetryEnvoyProxyAccessLog + + + +TODO: consider reuse ExtensionService? + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the extension service hostname. | +| `port` _integer_ | Port defines the port the extension service is exposed on. | +| `resources` _object (keys:string, values:string)_ | Resources is a set of labels that describe the source of a log entry, including envoy node info. It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). | + + +#### ProviderType + +_Underlying type:_ `string` + +ProviderType defines the types of providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayProvider](#envoygatewayprovider) +- [EnvoyProxyProvider](#envoyproxyprovider) + + + +#### ProxyAccessLog + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable disables access logging for managed proxies if set to true. | +| `settings` _[ProxyAccessLogSetting](#proxyaccesslogsetting) array_ | Settings defines accesslog settings for managed proxies. If unspecified, will send default format to stdout. | + + +#### ProxyAccessLogFormat + + + +ProxyAccessLogFormat defines the format of accesslog. By default accesslogs are written to standard output. + +_Appears in:_ +- [ProxyAccessLogSetting](#proxyaccesslogsetting) + +| Field | Description | +| --- | --- | +| `type` _[ProxyAccessLogFormatType](#proxyaccesslogformattype)_ | Type defines the type of accesslog format. | +| `text` _string_ | Text defines the text accesslog format, following Envoy accesslog formatting, It's required when the format type is "Text". Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. | +| `json` _object (keys:string, values:string)_ | JSON is additional attributes that describe the specific event occurrence. Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) can be used as values for fields within the Struct. It's required when the format type is "JSON". | + + +#### ProxyAccessLogFormatType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [ProxyAccessLogFormat](#proxyaccesslogformat) + + + +#### ProxyAccessLogSetting + + + + + +_Appears in:_ +- [ProxyAccessLog](#proxyaccesslog) + +| Field | Description | +| --- | --- | +| `format` _[ProxyAccessLogFormat](#proxyaccesslogformat)_ | Format defines the format of accesslog. | +| `sinks` _[ProxyAccessLogSink](#proxyaccesslogsink) array_ | Sinks defines the sinks of accesslog. | + + +#### ProxyAccessLogSink + + + + + +_Appears in:_ +- [ProxyAccessLogSetting](#proxyaccesslogsetting) + +| Field | Description | +| --- | --- | +| `type` _[ProxyAccessLogSinkType](#proxyaccesslogsinktype)_ | Type defines the type of accesslog sink. | +| `file` _[FileEnvoyProxyAccessLog](#fileenvoyproxyaccesslog)_ | File defines the file accesslog sink. | +| `openTelemetry` _[OpenTelemetryEnvoyProxyAccessLog](#opentelemetryenvoyproxyaccesslog)_ | OpenTelemetry defines the OpenTelemetry accesslog sink. | + + +#### ProxyAccessLogSinkType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [ProxyAccessLogSink](#proxyaccesslogsink) + + + +#### ProxyBootstrap + + + +ProxyBootstrap defines Envoy Bootstrap configuration. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `type` _[BootstrapType](#bootstraptype)_ | Type is the type of the bootstrap configuration, it should be either Replace or Merge. If unspecified, it defaults to Replace. | +| `value` _string_ | Value is a YAML string of the bootstrap. | + + +#### ProxyLogComponent + +_Underlying type:_ `string` + +ProxyLogComponent defines a component that supports a configured logging level. + +_Appears in:_ +- [ProxyLogging](#proxylogging) + + + +#### ProxyLogging + + + +ProxyLogging defines logging parameters for managed proxies. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `level` _object (keys:[ProxyLogComponent](#proxylogcomponent), values:[LogLevel](#loglevel))_ | Level is a map of logging level per component, where the component is the key and the log level is the value. If unspecified, defaults to "default: warn". | + + +#### ProxyMetricSink + + + + + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `type` _[MetricSinkType](#metricsinktype)_ | Type defines the metric sink type. EG currently only supports OpenTelemetry. | +| `openTelemetry` _[ProxyOpenTelemetrySink](#proxyopentelemetrysink)_ | OpenTelemetry defines the configuration for OpenTelemetry sink. It's required if the sink type is OpenTelemetry. | + + +#### ProxyMetrics + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `prometheus` _[ProxyPrometheusProvider](#proxyprometheusprovider)_ | Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. | +| `sinks` _[ProxyMetricSink](#proxymetricsink) array_ | Sinks defines the metric sinks where metrics are sent to. | +| `matches` _[Match](#match) array_ | Matches defines configuration for selecting specific metrics instead of generating all metrics stats that are enabled by default. This helps reduce CPU and memory overhead in Envoy, but eliminating some stats may after critical functionality. Here are the stats that we strongly recommend not disabling: `cluster_manager.warming_clusters`, `cluster..membership_total`,`cluster..membership_healthy`, `cluster..membership_degraded`,reference https://github.com/envoyproxy/envoy/issues/9856, https://github.com/envoyproxy/envoy/issues/14610 | +| `enableVirtualHostStats` _boolean_ | EnableVirtualHostStats enables envoy stat metrics for virtual hosts. | + + +#### ProxyOpenTelemetrySink + + + + + +_Appears in:_ +- [ProxyMetricSink](#proxymetricsink) + +| Field | Description | +| --- | --- | +| `host` _string_ | Host define the service hostname. | +| `port` _integer_ | Port defines the port the service is exposed on. | + + +#### ProxyPrometheusProvider + + + + + +_Appears in:_ +- [ProxyMetrics](#proxymetrics) + +| Field | Description | +| --- | --- | +| `disable` _boolean_ | Disable the Prometheus endpoint. | + + +#### ProxyTelemetry + + + + + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Description | +| --- | --- | +| `accessLog` _[ProxyAccessLog](#proxyaccesslog)_ | AccessLogs defines accesslog parameters for managed proxies. If unspecified, will send default format to stdout. | +| `tracing` _[ProxyTracing](#proxytracing)_ | Tracing defines tracing configuration for managed proxies. If unspecified, will not send tracing data. | +| `metrics` _[ProxyMetrics](#proxymetrics)_ | Metrics defines metrics configuration for managed proxies. | + + +#### ProxyTracing + + + + + +_Appears in:_ +- [ProxyTelemetry](#proxytelemetry) + +| Field | Description | +| --- | --- | +| `samplingRate` _integer_ | SamplingRate controls the rate at which traffic will be selected for tracing if no prior sampling decision has been made. Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. | +| `customTags` _object (keys:string, values:[CustomTag](#customtag))_ | CustomTags defines the custom tags to add to each span. If provider is kubernetes, pod name and namespace are added by default. | +| `provider` _[TracingProvider](#tracingprovider)_ | Provider defines the tracing provider. Only OpenTelemetry is supported currently. | + + +#### RateLimit + + + +RateLimit defines the configuration associated with the Rate Limit Service used for Global Rate Limiting. + +_Appears in:_ +- [EnvoyGateway](#envoygateway) +- [EnvoyGatewaySpec](#envoygatewayspec) + +| Field | Description | +| --- | --- | +| `backend` _[RateLimitDatabaseBackend](#ratelimitdatabasebackend)_ | Backend holds the configuration associated with the database backend used by the rate limit service to store state associated with global ratelimiting. | +| `timeout` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ | Timeout specifies the timeout period for the proxy to access the ratelimit server If not set, timeout is 20ms. | +| `failClosed` _boolean_ | FailClosed is a switch used to control the flow of traffic when the response from the ratelimit server cannot be obtained. If FailClosed is false, let the traffic pass, otherwise, don't let the traffic pass and return 500. If not set, FailClosed is False. | + + +#### RateLimitDatabaseBackend + + + +RateLimitDatabaseBackend defines the configuration associated with the database backend used by the rate limit service. + +_Appears in:_ +- [RateLimit](#ratelimit) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitDatabaseBackendType](#ratelimitdatabasebackendtype)_ | Type is the type of database backend to use. Supported types are: * Redis: Connects to a Redis database. | +| `redis` _[RateLimitRedisSettings](#ratelimitredissettings)_ | Redis defines the settings needed to connect to a Redis database. | + + +#### RateLimitDatabaseBackendType + +_Underlying type:_ `string` + +RateLimitDatabaseBackendType specifies the types of database backend to be used by the rate limit service. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + + + +#### RateLimitRedisSettings + + + +RateLimitRedisSettings defines the configuration for connecting to redis database. + +_Appears in:_ +- [RateLimitDatabaseBackend](#ratelimitdatabasebackend) + +| Field | Description | +| --- | --- | +| `url` _string_ | URL of the Redis Database. | +| `tls` _[RedisTLSSettings](#redistlssettings)_ | TLS defines TLS configuration for connecting to redis database. | + + +#### RateLimitRule + + + +RateLimitRule defines the semantics for matching attributes from the incoming requests, and setting limits for them. + +_Appears in:_ +- [GlobalRateLimit](#globalratelimit) + +| Field | Description | +| --- | --- | +| `clientSelectors` _[RateLimitSelectCondition](#ratelimitselectcondition) array_ | ClientSelectors holds the list of select conditions to select specific clients using attributes from the traffic flow. All individual select conditions must hold True for this rule and its limit to be applied. If this field is empty, it is equivalent to True, and the limit is applied. | +| `limit` _[RateLimitValue](#ratelimitvalue)_ | Limit holds the rate limit values. This limit is applied for traffic flows when the selectors compute to True, causing the request to be counted towards the limit. The limit is enforced and the request is ratelimited, i.e. a response with 429 HTTP status code is sent back to the client when the selected requests have reached the limit. | + + +#### RateLimitSelectCondition + + + +RateLimitSelectCondition specifies the attributes within the traffic flow that can be used to select a subset of clients to be ratelimited. All the individual conditions must hold True for the overall condition to hold True. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `headers` _[HeaderMatch](#headermatch) array_ | Headers is a list of request headers to match. Multiple header values are ANDed together, meaning, a request MUST match all the specified headers. | +| `sourceCIDR` _[SourceMatch](#sourcematch)_ | SourceCIDR is the client IP Address range to match on. | + + +#### RateLimitSpec + + + +RateLimitSpec defines the desired state of RateLimitSpec. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[RateLimitType](#ratelimittype)_ | Type decides the scope for the RateLimits. Valid RateLimitType values are "Global". | +| `global` _[GlobalRateLimit](#globalratelimit)_ | Global defines global rate limit configuration. | + + +#### RateLimitType + +_Underlying type:_ `string` + +RateLimitType specifies the types of RateLimiting. + +_Appears in:_ +- [RateLimitSpec](#ratelimitspec) + + + +#### RateLimitUnit + +_Underlying type:_ `string` + +RateLimitUnit specifies the intervals for setting rate limits. Valid RateLimitUnit values are "Second", "Minute", "Hour", and "Day". + +_Appears in:_ +- [RateLimitValue](#ratelimitvalue) + + + +#### RateLimitValue + + + +RateLimitValue defines the limits for rate limiting. + +_Appears in:_ +- [RateLimitRule](#ratelimitrule) + +| Field | Description | +| --- | --- | +| `requests` _integer_ | | +| `unit` _[RateLimitUnit](#ratelimitunit)_ | | + + +#### RedisTLSSettings + + + +RedisTLSSettings defines the TLS configuration for connecting to redis database. + +_Appears in:_ +- [RateLimitRedisSettings](#ratelimitredissettings) + +| Field | Description | +| --- | --- | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | CertificateRef defines the client certificate reference for TLS connections. Currently only a Kubernetes Secret of type TLS is supported. | + + +#### RemoteJWKS + + + +RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. + +_Appears in:_ +- [JWTProvider](#jwtprovider) + +| Field | Description | +| --- | --- | +| `uri` _string_ | URI is the HTTPS URI to fetch the JWKS. Envoy's system trust bundle is used to validate the server certificate. | + + +#### RequestHeaderCustomTag + + + +RequestHeaderCustomTag adds value from request header to each span. + +_Appears in:_ +- [CustomTag](#customtag) + +| Field | Description | +| --- | --- | +| `name` _string_ | Name defines the name of the request header which to extract the value from. | +| `defaultValue` _string_ | DefaultValue defines the default value to use if the request header is not set. | + + +#### ResourceProviderType + +_Underlying type:_ `string` + +ResourceProviderType defines the types of custom resource providers supported by Envoy Gateway. + +_Appears in:_ +- [EnvoyGatewayResourceProvider](#envoygatewayresourceprovider) + + + +#### SecurityPolicy + + + +SecurityPolicy allows the user to configure various security settings for a Gateway. + +_Appears in:_ +- [SecurityPolicyList](#securitypolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `SecurityPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[SecurityPolicySpec](#securitypolicyspec)_ | Spec defines the desired state of SecurityPolicy. | + + +#### SecurityPolicyList + + + +SecurityPolicyList contains a list of SecurityPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `SecurityPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[SecurityPolicy](#securitypolicy) array_ | | + + +#### SecurityPolicySpec + + + +SecurityPolicySpec defines the desired state of SecurityPolicy. + +_Appears in:_ +- [SecurityPolicy](#securitypolicy) + +| Field | Description | +| --- | --- | +| `targetRef` _[PolicyTargetReferenceWithSectionName](#policytargetreferencewithsectionname)_ | TargetRef is the name of the Gateway resource this policy is being attached to. This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway. TargetRef | +| `cors` _[CORS](#cors)_ | CORS defines the configuration for Cross-Origin Resource Sharing (CORS). | +| `jwt` _[JWT](#jwt)_ | JWT defines the configuration for JSON Web Token (JWT) authentication. | + + + + +#### ServiceType + +_Underlying type:_ `string` + +ServiceType string describes ingress methods for a service + +_Appears in:_ +- [KubernetesServiceSpec](#kubernetesservicespec) + + + +#### SourceMatch + + + + + +_Appears in:_ +- [RateLimitSelectCondition](#ratelimitselectcondition) + + + +#### StringMatch + + + +StringMatch defines how to match any strings. This is a general purpose match condition that can be used by other EG APIs that need to match against a string. + +_Appears in:_ +- [CORS](#cors) + +| Field | Description | +| --- | --- | +| `type` _[MatchType](#matchtype)_ | Type specifies how to match against a string. | +| `value` _string_ | Value specifies the string value that the match must have. | + + +#### TCPKeepalive + + + +TCPKeepalive define the TCP Keepalive configuration. + +_Appears in:_ +- [ClientTrafficPolicySpec](#clienttrafficpolicyspec) + +| Field | Description | +| --- | --- | +| `probes` _integer_ | The total number of unacknowledged probes to send before deciding the connection is dead. Defaults to 9. | +| `idleTime` _Duration_ | The duration a connection needs to be idle before keep-alive probes start being sent. The duration format is Defaults to `7200s`. | +| `interval` _Duration_ | The duration between keep-alive probes. Defaults to `75s`. | + + +#### TracingProvider + + + + + +_Appears in:_ +- [ProxyTracing](#proxytracing) + +| Field | Description | +| --- | --- | +| `type` _[TracingProviderType](#tracingprovidertype)_ | Type defines the tracing provider type. EG currently only supports OpenTelemetry. | +| `host` _string_ | Host define the provider service hostname. | +| `port` _integer_ | Port defines the port the provider service is exposed on. | + + +#### TracingProviderType + +_Underlying type:_ `string` + + + +_Appears in:_ +- [TracingProvider](#tracingprovider) + + + +#### XDSTranslatorHook + +_Underlying type:_ `string` + +XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support for the xds-translator + +_Appears in:_ +- [XDSTranslatorHooks](#xdstranslatorhooks) + + + +#### XDSTranslatorHooks + + + +XDSTranslatorHooks contains all the pre and post hooks for the xds-translator runner. + +_Appears in:_ +- [ExtensionHooks](#extensionhooks) + +| Field | Description | +| --- | --- | +| `pre` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | +| `post` _[XDSTranslatorHook](#xdstranslatorhook) array_ | | + + diff --git a/site/content/en/v0.6.0/contributions/CODEOWNERS.md b/site/content/en/v0.6.0/contributions/CODEOWNERS.md new file mode 100644 index 00000000000..63b751abde5 --- /dev/null +++ b/site/content/en/v0.6.0/contributions/CODEOWNERS.md @@ -0,0 +1,19 @@ +--- +title: "Maintainers" +description: "This section includes Maintainers of Envoy Gateway." +--- + +## The following maintainers, listed in alphabetical order, own everything + +- @AliceProxy +- @arkodg +- @Xunzhuo +- @zirain +- @qicz + +## Emeritus Maintainers + +- @danehans +- @alexgervais +- @skriss +- @youngnick diff --git a/site/content/en/v0.6.0/contributions/CODE_OF_CONDUCT.md b/site/content/en/v0.6.0/contributions/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..e19da050dff --- /dev/null +++ b/site/content/en/v0.6.0/contributions/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ +--- +title: "Code of Conduct" +description: "This section includes Code of Conduct of Envoy Gateway." +--- + +Envoy Gateway follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/site/content/en/v0.6.0/contributions/CONTRIBUTING.md b/site/content/en/v0.6.0/contributions/CONTRIBUTING.md new file mode 100644 index 00000000000..f94b2c940e9 --- /dev/null +++ b/site/content/en/v0.6.0/contributions/CONTRIBUTING.md @@ -0,0 +1,190 @@ +--- +title: "Contributing" +description: "This section tells how to contribute to Envoy Gateway." +weight: 3 +--- + +We welcome contributions from the community. Please carefully review the [project goals](/about) +and following guidelines to streamline your contributions. + +## Communication + +* Before starting work on a major feature, please contact us via GitHub or Slack. We will ensure no + one else is working on it and ask you to open a GitHub issue. +* A "major feature" is defined as any change that is > 100 LOC altered (not including tests), or + changes any user-facing behavior. We will use the GitHub issue to discuss the feature and come to + agreement. This is to prevent your time being wasted, as well as ours. The GitHub review process + for major features is also important so that [affiliations with commit access](../codeowners) can + come to agreement on the design. If it's appropriate to write a design document, the document must + be hosted either in the GitHub issue, or linked to from the issue and hosted in a world-readable + location. +* Small patches and bug fixes don't need prior communication. + +## Inclusivity + +The Envoy Gateway community has an explicit goal to be inclusive to all. As such, all PRs must adhere +to the following guidelines for all code, APIs, and documentation: + +* The following words and phrases are not allowed: + * *Whitelist*: use allowlist instead. + * *Blacklist*: use denylist or blocklist instead. + * *Master*: use primary instead. + * *Slave*: use secondary or replica instead. +* Documentation should be written in an inclusive style. The [Google developer + documentation](https://developers.google.com/style/inclusive-documentation) contains an excellent + reference on this topic. +* The above policy is not considered definitive and may be amended in the future as industry best + practices evolve. Additional comments on this topic may be provided by maintainers during code + review. + +## Submitting a PR + +* Fork the repo. +* Hack +* DCO sign-off each commit. This can be done with `git commit -s`. +* Submit your PR. +* Tests will automatically run for you. +* We will **not** merge any PR that is not passing tests. +* PRs are expected to have 100% test coverage for added code. This can be verified with a coverage + build. If your PR cannot have 100% coverage for some reason please clearly explain why when you + open it. +* Any PR that changes user-facing behavior **must** have associated documentation in the [docs](https://github.com/envoyproxy/gateway/tree/main/site) folder of the repo as + well as the [changelog](/blog/releases). +* All code comments and documentation are expected to have proper English grammar and punctuation. + If you are not a fluent English speaker (or a bad writer ;-)) please let us know and we will try + to find some help but there are no guarantees. +* Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary + and summary followed by a colon. format `chore/docs/feat/fix/refactor/style/test: summary`. + Examples: + * "docs: fix grammar error" + * "feat(translator): add new feature" + * "fix: fix xx bug" + * "chore: change ci & build tools etc" +* Your PR commit message will be used as the commit message when your PR is merged. You should + update this field if your PR diverges during review. +* Your PR description should have details on what the PR does. If it fixes an existing issue it + should end with "Fixes #XXX". +* If your PR is co-authored or based on an earlier PR from another contributor, + please attribute them with `Co-authored-by: name `. See + GitHub's [multiple author + guidance](https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors) + for further details. +* When all tests are passing and all other conditions described herein are satisfied, a maintainer + will be assigned to review and merge the PR. +* Once you submit a PR, *please do not rebase it*. It's much easier to review if subsequent commits + are new commits and/or merges. We squash and merge so the number of commits you have in the PR + doesn't matter. +* We expect that once a PR is opened, it will be actively worked on until it is merged or closed. + We reserve the right to close PRs that are not making progress. This is generally defined as no + changes for 7 days. Obviously PRs that are closed due to lack of activity can be reopened later. + Closing stale PRs helps us to keep on top of all the work currently in flight. + +## Maintainer PR Review Policy + +* See [CODEOWNERS.md](../codeowners) for the current list of maintainers. +* A maintainer representing a different affiliation from the PR owner is required to review and + approve the PR. +* When the project matures, it is expected that a "domain expert" for the code the PR touches should + review the PR. This person does not require commit access, just domain knowledge. +* The above rules may be waived for PRs which only update docs or comments, or trivial changes to + tests and tools (where trivial is decided by the maintainer in question). +* If there is a question on who should review a PR please discuss in Slack. +* Anyone is welcome to review any PR that they want, whether they are a maintainer or not. +* Please make sure that the PR title, commit message, and description are updated if the PR changes + significantly during review. +* Please **clean up the title and body** before merging. By default, GitHub fills the squash merge + title with the original title, and the commit body with every individual commit from the PR. + The maintainer doing the merge should make sure the title follows the guidelines above and should + overwrite the body with the original commit message from the PR (cleaning it up if necessary) + while preserving the PR author's final DCO sign-off. + +## Decision making + +This is a new and complex project, and we need to make a lot of decisions very quickly. +To this end, we've settled on this process for making (possibly contentious) decisions: + +* For decisions that need a record, we create an issue. +* In that issue, we discuss opinions, then a maintainer can call for a vote in a comment. +* Maintainers can cast binding votes on that comment by reacting or replying in another comment. +* Non-maintainer community members are welcome to cast non-binding votes by either of these methods. +* Voting will be resolved by simple majority. +* In the event of deadlocks, the question will be put to steering instead. + +## DCO: Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign-off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +```bash +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` + +## Fixing DCO + +If your PR fails the DCO check, it's necessary to fix the entire commit history in the PR. Best +practice is to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +the commit history to a single commit, append the DCO sign-off as described above, and [force +push](https://git-scm.com/docs/git-push#git-push---force). For example, if you have 2 commits in +your history: + +```bash +git rebase -i HEAD^^ +(interactive squash + DCO append) +git push origin -f +``` + +Note, that in general rewriting history in this way is a hindrance to the review process and this +should only be done to correct a DCO mistake. diff --git a/site/content/en/v0.6.0/contributions/DEVELOP.md b/site/content/en/v0.6.0/contributions/DEVELOP.md new file mode 100644 index 00000000000..6f82c4a411f --- /dev/null +++ b/site/content/en/v0.6.0/contributions/DEVELOP.md @@ -0,0 +1,163 @@ +--- +title: "Developer Guide" +description: "This section tells how to develop Envoy Gateway." +weight: 2 +--- + +Envoy Gateway is built using a [make][]-based build system. Our CI is based on [Github Actions][] using [workflows][]. + +## Prerequisites + +### go + +* Version: 1.20 +* Installation Guide: https://go.dev/doc/install + +### make + +* Recommended Version: 4.0 or later +* Installation Guide: https://www.gnu.org/software/make + +### docker + +* Optional when you want to build a Docker image or run `make` inside Docker. +* Recommended Version: 20.10.16 +* Installation Guide: https://docs.docker.com/engine/install + +### python3 + +* Need a `python3` program +* Must have a functioning `venv` module; this is part of the standard + library, but some distributions (such as Debian and Ubuntu) replace + it with a stub and require you to install a `python3-venv` package + separately. + +## Quickstart + +* Run `make help` to see all the available targets to build, test and run Envoy Gateway. + +### Building + +* Run `make build` to build all the binaries. +* Run `make build BINS="envoy-gateway"` to build the Envoy Gateway binary. +* Run `make build BINS="egctl"` to build the egctl binary. + +__Note:__ The binaries get generated in the `bin/$OS/$ARCH` directory, for example, `bin/linux/amd64/`. + +### Testing + +* Run `make test` to run the golang tests. + +* Run `make testdata` to generate the golden YAML testdata files. + +### Running Linters + +* Run `make lint` to make sure your code passes all the linter checks. +__Note:__ The `golangci-lint` configuration resides [here](https://github.com/envoyproxy/gateway/blob/main/tools/linter/golangci-lint/.golangci.yml). + +### Building and Pushing the Image + +* Run `IMAGE=docker.io/you/gateway-dev make image` to build the docker image. +* Run `IMAGE=docker.io/you/gateway-dev make push-multiarch` to build and push the multi-arch docker image. + +__Note:__ Replace `IMAGE` with your registry's image name. + +### Deploying Envoy Gateway for Test/Dev + +* Run `make create-cluster` to create a [Kind][] cluster. + +#### Option 1: Use the Latest [gateway-dev][] Image + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway in the Kind cluster using the latest image. Replace `latest` + to use a different image tag. + +#### Option 2: Use a Custom Image + +* Run `make kube-install-image` to build an image from the tip of your current branch and load it in the Kind cluster. +* Run `IMAGE_PULL_POLICY=IfNotPresent make kube-deploy` to install Envoy Gateway into the Kind cluster using your custom image. + +### Deploying Envoy Gateway in Kubernetes + +* Run `TAG=latest make kube-deploy` to deploy Envoy Gateway using the latest image into a Kubernetes cluster (linked to + the current kube context). Preface the command with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or + tag. +* Run `make kube-undeploy` to uninstall Envoy Gateway from the cluster. + +__Note:__ Envoy Gateway is tested against Kubernetes v1.24.0. + +### Demo Setup + +* Run `make kube-demo` to deploy a demo backend service, gatewayclass, gateway and httproute resource +(similar to steps outlined in the [Quickstart][] docs) and test the configuration. +* Run `make kube-demo-undeploy` to delete the resources created by the `make kube-demo` command. + +### Run Gateway API Conformance Tests + +The commands below deploy Envoy Gateway to a Kubernetes cluster and run the Gateway API conformance tests. Refer to the +Gateway API [conformance homepage][] to learn more about the tests. If Envoy Gateway is already installed, run +`TAG=latest make run-conformance` to run the conformance tests. + +#### On a Linux Host + +* Run `TAG=latest make conformance` to create a Kind cluster, install Envoy Gateway using the latest [gateway-dev][] + image, and run Gateway API conformance tests. + +#### On a Mac Host + +Since Mac doesn't support [directly exposing][] the Docker network to the Mac host, use one of the following +workarounds to run conformance tests: + +* Deploy your own Kubernetes cluster or use Docker Desktop with [Kubernetes support][] and then run + `TAG=latest make kube-deploy run-conformance`. This will install Envoy Gateway using the latest [gateway-dev][] image + to the Kubernetes cluster using the current kubectl context and run the conformance tests. Use `make kube-undeploy` to + uninstall Envoy Gateway. +* Install and run [Docker Mac Net Connect][mac_connect] and then run `TAG=latest make conformance`. + +__Note:__ Preface commands with `IMAGE` or replace `TAG` to use a different Envoy Gateway image or tag. If `TAG` +is unspecified, the short SHA of your current branch is used. + +### Debugging the Envoy Config + +An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port +(currently `19000`) on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally. + +Get the name of the Envoy deployment. The following example is for Gateway `eg` in the `default` namespace: + +```shell +export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward the admin interface port: + +```shell +kubectl port-forward deploy/${ENVOY_DEPLOYMENT} -n envoy-gateway-system 19000:19000 +``` + +Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`. + +There are many other endpoints on the [Envoy admin interface][] that may be helpful when debugging. + +### JWT Testing + +An example [JSON Web Token (JWT)][jwt] and [JSON Web Key Set (JWKS)][jwks] are used for the [request authentication][] +user guide. The JWT was created by the [JWT Debugger][], using the `RS256` algorithm. The public key from the JWTs +verify signature was copied to [JWK Creator][] for generating the JWK. The JWK Creator was configured with matching +settings, i.e. `Signing` public key use and the `RS256` algorithm. The generated JWK was wrapped in a JWKS structure +and is hosted in the repo. + +[Quickstart]: https://github.com/envoyproxy/gateway/blob/main/docs/latest/user/quickstart.md +[make]: https://www.gnu.org/software/make/ +[Github Actions]: https://docs.github.com/en/actions +[workflows]: https://github.com/envoyproxy/gateway/tree/main/.github/workflows +[Kind]: https://kind.sigs.k8s.io/ +[conformance homepage]: https://gateway-api.sigs.k8s.io/concepts/conformance/ +[directly exposing]: https://kind.sigs.k8s.io/docs/user/loadbalancer/ +[Kubernetes support]: https://docs.docker.com/desktop/kubernetes/ +[gateway-dev]: https://hub.docker.com/r/envoyproxy/gateway-dev/tags +[mac_connect]: https://github.com/chipmk/docker-mac-net-connect +[Envoy admin interface]: https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[request authentication]: https://gateway.envoyproxy.io/latest/user/authn.html +[JWT Debugger]: https://jwt.io/ +[JWK Creator]: https://russelldavies.github.io/jwk-creator/ diff --git a/site/content/en/v0.6.0/contributions/DOCS.md b/site/content/en/v0.6.0/contributions/DOCS.md new file mode 100644 index 00000000000..ae19953a8b5 --- /dev/null +++ b/site/content/en/v0.6.0/contributions/DOCS.md @@ -0,0 +1,69 @@ +--- +title: "Working on Envoy Gateway Docs" +description: "This section tells the development of + Envoy Gateway Documents." +--- + +{{% alert title="Note" color="warning" %}} +We migrated from ***Sphinx*** to ***Hugo*** for Envoy Gateway Documents. + +Read blog: [Welcome to new website!](/blog/2023/10/08/welcome-to-new-website/) +{{% /alert %}} + +The documentation for the Envoy Gateway lives in the `site/content/en` directory. Any +individual document can be written using [Markdown]. + +## Documentation Structure + +We supported the versioned Docs now, the directory name under docs represents +the version of docs. The root of the latest site is in `site/content/en/latest`. +This is probably where to start if you're trying to understand how things fit together. + +Note that the new contents should be added to `site/content/en/latest` and will be cut off at +the next release. The contents under `site/content/en/v0.5.0` are auto-generated, +and usually do not need to make changes to them, unless if you find the current release pages have +some incorrect contents. If so, you should send a PR to update contents both of `site/content/en/latest` +and `site/content/en/v0.5.0`. + +You can access the website which represents the current release in default, +and you can access the website which contains the latest version changes in +[Here][latest-website] or at the footer of the pages. + +## Documentation Workflow + +To work with the docs, just edit Markdown files in `site/content/en/latest`, +then run + +```bash +make docs +``` + +This will create `site/public` with the built HTML pages. You can preview it +by running: + +``` shell +make docs-serve +``` + +If you want to generate a new release version of the docs, like `v0.6.0`, then run + +```bash +make docs-release TAG=v0.6.0 +``` + +This will update the VERSION file at the project root, which records current release version, +and it will be used in the pages version context and binary version output. Also, this will generate +new dir `site/content/en/v0.6.0`, which contains docs at v0.6.0 and updates artifact links to `v0.6.0` +in all files under `site/content/en/v0.6.0/user`, like `quickstart.md`, `http-routing.md` and etc. + +## Publishing Docs + +Whenever docs are pushed to `main`, CI will publish the built docs to GitHub +Pages. For more details, see `.github/workflows/docs.yaml`. + +## Reference + +Go to [Hugo](https://gohugo.io) and [Docsy](https://www.docsy.dev/docs) to learn more. + +[Markdown]: https://daringfireball.net/projects/markdown/syntax +[latest-website]: /latest diff --git a/site/content/en/v0.6.0/contributions/RELEASING.md b/site/content/en/v0.6.0/contributions/RELEASING.md new file mode 100644 index 00000000000..f81d5be457d --- /dev/null +++ b/site/content/en/v0.6.0/contributions/RELEASING.md @@ -0,0 +1,252 @@ +--- +title: "Release Process" +description: "This section tells the release process of Envoy Gateway." +--- + +This document guides maintainers through the process of creating an Envoy Gateway release. + +- [Release Candidate](#release-candidate) +- [Minor Release](#minor-release) +- [Announce the Release](#announce-the-release) + +## Release Candidate + +The following steps should be used for creating a release candidate. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export RELEASE_CANDIDATE_NUMBER=1 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and + the [Build and Test][] has successfully completed. +5. Create a new release branch from `main`. The release branch should be named + `release/v${MAJOR_VERSION}.${MINOR_VERSION}`, e.g. `release/v0.3`. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. Push the branch to the Envoy Gateway repo. + + ```shell + git push ${GITHUB_REMOTE} release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Create a topic branch for updating the Envoy proxy image and Envoy Ratelimit image to the tag supported by the release. Reference [PR #2098][] + for additional details on updating the image tag. +8. Sign, commit, and push your changes to your fork. +9. Submit a [Pull Request][] to merge the changes into the `release/v${MAJOR_VERSION}.${MINOR_VERSION}` branch. Do not + proceed until your PR has merged into the release branch and the [Build and Test][] has completed for your PR. +10. Ensure your release branch is up-to-date and tag the head of your release branch with the release candidate number. + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} Release Candidate' + ``` + +11. Push the tag to the Envoy Gateway repository. + + ```shell + git push ${GITHUB_REMOTE} v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER} + ``` + +12. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +13. Confirm that the [release workflow][] completed successfully. +14. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +15. Confirm that the [release][] was created. +16. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test + the quickstart steps using the release candidate by manually updating the links. +17. [Generate][] the GitHub changelog. +18. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release. +19. If you find any bugs in this process, please create an issue. + +### Setup cherry picker action + +After release branch cut, RM (Release Manager) should add job [cherrypick action](../../../.github/workflows/cherrypick.yaml) for target release. + +Configuration looks like following: + +```yaml + cherry_pick_release_v0_4: + runs-on: ubuntu-latest + name: Cherry pick into release-v0.4 + if: ${{ contains(github.event.pull_request.labels.*.name, 'cherrypick/release-v0.4') && github.event.pull_request.merged == true }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cherry pick into release/v0.4 + uses: carloscastrojumo/github-cherry-pick-action@v1.0.9 + with: + branch: release/v0.4 + title: "[release/v0.4] {old_title}" + body: "Cherry picking #{old_pull_request_id} onto release/v0.4" + labels: | + cherrypick/release-v0.4 + # put release manager here + reviewers: | + AliceProxy +``` + +Replace `v0.4` with real branch name, and `AliceProxy` with the real name of RM. + +## Minor Release + +The following steps should be used for creating a minor release. + +### Prerequisites + +- Permissions to push to the Envoy Gateway repository. +- A release branch that has been cut from the corresponding release candidate. Refer to the + [Release Candidate](#release-candidate) section for additional details on cutting a release candidate. + +Set environment variables for use in subsequent steps: + +```shell +export MAJOR_VERSION=0 +export MINOR_VERSION=3 +export GITHUB_REMOTE=origin +``` + +1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. +2. Create a topic branch for adding the release notes, release announcement, and versioned release docs. + + 1. Create the release notes. Reference previous [release notes][] for additional details. __Note:__ The release + notes should be an accumulation of the release candidate release notes and any changes since the release + candidate. + 2. Create a release announcement. Refer to [PR #635] as an example release announcement. + 3. Include the release in the compatibility matrix. Refer to [PR #1002] as an example. + 4. Generate the versioned release docs: + + ``` shell + make docs-release TAG=v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + + 5. Update the `Get Started` and `Contributing` button referred link in `site/content/en/_index.md`: + + ```shell + + Get Started + + + Contributing + + ``` + + 6. Uodate the `Documentation` referred link on the menu in `site/hugo.toml`: + + ```shell + [[menu.main]] + name = "Documentation" + weight = -101 + pre = "" + url = "/v0.5.0" + ``` + +3. Sign, commit, and push your changes to your fork. +4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until all your PRs have merged + and the [Build and Test][] has completed for your final PR. + +5. Checkout the release branch. + + ```shell + git checkout -b release/v${MAJOR_VERSION}.${MINOR_VERSION} $GITHUB_REMOTE/release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +6. If the tip of the release branch does not match the tip of `main`, perform the following: + + 1. Create a topic branch from the release branch. + 2. Cherry-pick the commits from `main` that differ from the release branch. + 3. Run tests locally, e.g. `make lint`. + 4. Sign, commit, and push your topic branch to your Envoy Gateway fork. + 5. Submit a PR to merge the topic from of your fork into the Envoy Gateway release branch. + 6. Do not proceed until the PR has merged and CI passes for the merged PR. + 7. If you are still on your topic branch, change to the release branch: + + ```shell + git checkout release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + + 8. Ensure your local release branch is up-to-date: + + ```shell + git pull $GITHUB_REMOTE release/v${MAJOR_VERSION}.${MINOR_VERSION} + ``` + +7. Tag the head of your release branch with the release tag. For example: + + ```shell + git tag -a v${MAJOR_VERSION}.${MINOR_VERSION}.0 -m 'Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0 Release' + ``` + + __Note:__ The tag version differs from the release branch by including the `.0` patch version. + +8. Push the tag to the Envoy Gateway repository. + + ```shell + git push origin v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +9. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc. +10. Confirm that the [release workflow][] completed successfully. +11. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub. +12. Confirm that the [release][] was created. +13. Confirm that the steps in the [Quickstart Guide][] work as expected. +14. [Generate][] the GitHub changelog and include the following text at the beginning of the release page: + + ```console + # Release Announcement + + Check out the [v${MAJOR_VERSION}.${MINOR_VERSION} release announcement] + (https://gateway.envoyproxy.io/releases/v${MAJOR_VERSION}.${MINOR_VERSION}.html) to learn more about the release. + ``` + +If you find any bugs in this process, please create an issue. + +## Announce the Release + +It's important that the world knows about the release. Use the following steps to announce the release. + +1. Set the release information in the Envoy Gateway Slack channel. For example: + + ```shell + Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION} has been released: https://github.com/envoyproxy/gateway/releases/tag/v${MAJOR_VERSION}.${MINOR_VERSION}.0 + ``` + +2. Send a message to the Envoy Gateway Slack channel. For example: + + ```shell + On behalf of the entire Envoy Gateway community, I am pleased to announce the release of Envoy Gateway + v${MAJOR_VERSION}.${MINOR_VERSION}. A big thank you to all the contributors that made this release possible. + Refer to the official v${MAJOR_VERSION}.${MINOR_VERSION} announcement for release details and the project docs + to start using Envoy Gateway. + ... + ``` + + Link to the GitHub release and release announcement page that highlights the release. + +[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes +[Pull Request]: https://github.com/envoyproxy/gateway/pulls +[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/quickstart.md +[Build and Test]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/build_and_test.yaml +[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml +[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml +[image]: https://hub.docker.com/r/envoyproxy/gateway/tags +[release]: https://github.com/envoyproxy/gateway/releases +[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +[PR #635]: https://github.com/envoyproxy/gateway/pull/635 +[PR #2098]: https://github.com/envoyproxy/gateway/pull/2098 +[PR #1002]: https://github.com/envoyproxy/gateway/pull/1002 +[VERSION]: https://github.com/envoyproxy/gateway/blob/main/VERSION diff --git a/site/content/en/v0.6.0/contributions/_index.md b/site/content/en/v0.6.0/contributions/_index.md new file mode 100644 index 00000000000..3255d996472 --- /dev/null +++ b/site/content/en/v0.6.0/contributions/_index.md @@ -0,0 +1,5 @@ +--- +title: Get Involved +description: "This section includes contents related to **Contributions**" +weight: 100 +--- diff --git a/site/content/en/v0.6.0/contributions/roadmap.md b/site/content/en/v0.6.0/contributions/roadmap.md new file mode 100644 index 00000000000..955af2a9623 --- /dev/null +++ b/site/content/en/v0.6.0/contributions/roadmap.md @@ -0,0 +1,96 @@ +--- +title: "Roadmap" +weight: -1 +description: "This section records the roadmap of Envoy Gateway." +--- + +This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of +the project. + +## Contributing to the Roadmap + +- To add a feature to the roadmap, create an [issue][issue] or join a [community meeting][meeting] to discuss your use + case. If your feature is accepted, a maintainer will assign your issue to a [release milestone][milestones] and update + this document accordingly. +- To help with an existing roadmap item, comment on or assign yourself to the associated issue. +- If a roadmap item doesn't have an issue, create one, assign yourself to the issue, and reference this document. A + maintainer will submit a [pull request][PR] to add the feature to the roadmap. __Note:__ The feature should be + discussed in an issue or a community meeting before implementing it. + +If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt. +Look for issues with the `help wanted` label to get started. + +## Details + +Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific +roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by +contributing to the project. + +`Last Updated: April 2023` + +### [v0.2.0][v0.2.0]: Establish a Solid Foundation + +- Complete the core Envoy Gateway implementation- [Issue #60][60]. +- Establish initial testing, e2e, integration, etc- [Issue #64][64]. +- Establish user and developer project documentation- [Issue #17][17]. +- Achieve Gateway API conformance (e.g. routing, LB, Header transformation, etc.)- [Issue #65][65]. +- Setup a CI/CD pipeline- [Issue #63][63]. + +### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms + +- Support extended Gateway API fields [Issue #707][707]. +- Support experimental Gateway APIs such as TCPRoute [Issue #643][643], UDPRoute [Issue #641][641] and GRPCRoute [Issue #642][642]. +- Establish guidelines for leveragaing Gateway API extensions [Issue #675][675]. +- Rate Limiting [Issue #670][670]. +- Authentication [Issue #336][336]. + +### [v0.4.0][v0.4.0]: Customizing Envoy Gateway + +- Extending Envoy Gateway control plane [Issue #20][20] +- Helm based installation for Envoy Gateway [Issue #650][650] +- Customizing managed Envoy Proxy Kubernetes resource fields [Issue #648][648] +- Configuring xDS Bootstrap [Issue #31][31] + +### [v0.5.0][v0.5.0]: Observability and Scale + +- Observability for data plane [Issue #699][699]. +- Allow users to configure xDS Resources [Issue #24][24]. + +### [v0.6.0][v0.6.0]: Preparation for GA + +- Observability for control plane [Issue #700][700]. +- Compute and document Envoy Gateway performance [Issue #1365][1365]. +- Add TrafficPolicy APIs for advanced features [Issue #1492][1492]. +- Envoy Gateway meets readiness criteria [Issue #1160][1160]. + +[issue]: https://github.com/envoyproxy/gateway/issues +[meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing +[pr]: https://github.com/envoyproxy/gateway/compare +[milestones]: https://github.com/envoyproxy/gateway/milestones +[v0.2.0]: https://github.com/envoyproxy/gateway/milestone/1 +[v0.3.0]: https://github.com/envoyproxy/gateway/milestone/7 +[v0.4.0]: https://github.com/envoyproxy/gateway/milestone/12 +[v0.5.0]: https://github.com/envoyproxy/gateway/milestone/13 +[v0.6.0]: https://github.com/envoyproxy/gateway/milestone/15 +[17]: https://github.com/envoyproxy/gateway/issues/17 +[20]: https://github.com/envoyproxy/gateway/issues/20 +[24]: https://github.com/envoyproxy/gateway/issues/24 +[31]: https://github.com/envoyproxy/gateway/issues/31 +[60]: https://github.com/envoyproxy/gateway/issues/60 +[63]: https://github.com/envoyproxy/gateway/issues/63 +[64]: https://github.com/envoyproxy/gateway/issues/64 +[65]: https://github.com/envoyproxy/gateway/issues/65 +[336]: https://github.com/envoyproxy/gateway/issues/336 +[641]: https://github.com/envoyproxy/gateway/issues/641 +[642]: https://github.com/envoyproxy/gateway/issues/642 +[648]: https://github.com/envoyproxy/gateway/issues/648 +[650]: https://github.com/envoyproxy/gateway/issues/650 +[643]: https://github.com/envoyproxy/gateway/issues/643 +[670]: https://github.com/envoyproxy/gateway/issues/670 +[675]: https://github.com/envoyproxy/gateway/issues/675 +[699]: https://github.com/envoyproxy/gateway/issues/699 +[700]: https://github.com/envoyproxy/gateway/issues/700 +[707]: https://github.com/envoyproxy/gateway/issues/707 +[1160]: https://github.com/envoyproxy/gateway/issues/1160 +[1365]: https://github.com/envoyproxy/gateway/issues/1365 +[1492]: https://github.com/envoyproxy/gateway/issues/1492 diff --git a/site/content/en/v0.6.0/design/_index.md b/site/content/en/v0.6.0/design/_index.md new file mode 100644 index 00000000000..2820db4c216 --- /dev/null +++ b/site/content/en/v0.6.0/design/_index.md @@ -0,0 +1,5 @@ +--- +title: "Design" +weight: 1 +description: This section includes Designs of Envoy Gateway. +--- diff --git a/site/content/en/v0.6.0/design/accesslog.md b/site/content/en/v0.6.0/design/accesslog.md new file mode 100644 index 00000000000..cb72f05fe43 --- /dev/null +++ b/site/content/en/v0.6.0/design/accesslog.md @@ -0,0 +1,237 @@ +--- +title: "Observability: Accesslog" +--- + +## Overview + +Envoy supports extensible accesslog to different sinks, File, gRPC etc. Envoy supports customizable access log formats using predefined fields as well as arbitrary HTTP request and response headers. Envoy supports several built-in access log filters and extension filters that are registered at runtime. + +Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementers such as Envoy Gateway to expose features. Since accesslog is not covered by `Core` or `Extended` APIs, EG should provide an easy to config access log formats and sinks per `EnvoyProxy`. + +## Goals + +- Support send accesslog to `File` or `OpenTelemetry` backend +- TODO: Support access log filters base on [CEL](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/filters/cel/v3/cel.proto#extension-envoy-access-loggers-extension-filters-cel) expression + +## Non-Goals + +- Support non-CEL filters, e.g. `status_code_filter`, `response_flag_filter` +- Support [HttpGrpcAccessLogConfig](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-httpgrpcaccesslogconfig) or [TcpGrpcAccessLogConfig](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#extensions-access-loggers-grpc-v3-tcpgrpcaccesslogconfig) + +## Use-Cases + +- Configure accesslog for a `EnvoyProxy` to `File` +- Configure accesslog for a `EnvoyProxy` to `OpenTelemetry` backend +- Configure multi accesslog providers for a `EnvoyProxy` + +### ProxyAccessLog API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/accesslogging_types.go" +type ProxyAccessLog struct { + // Disable disables access logging for managed proxies if set to true. + Disable bool `json:"disable,omitempty"` + // Settings defines accesslog settings for managed proxies. + // If unspecified, will send default format to stdout. + // +optional + Settings []ProxyAccessLogSetting `json:"settings,omitempty"` +} + +type ProxyAccessLogSetting struct { + // Format defines the format of accesslog. + Format ProxyAccessLogFormat `json:"format"` + // Sinks defines the sinks of accesslog. + // +kubebuilder:validation:MinItems=1 + Sinks []ProxyAccessLogSink `json:"sinks"` +} + +type ProxyAccessLogFormatType string + +const ( + // ProxyAccessLogFormatTypeText defines the text accesslog format. + ProxyAccessLogFormatTypeText ProxyAccessLogFormatType = "Text" + // ProxyAccessLogFormatTypeJSON defines the JSON accesslog format. + ProxyAccessLogFormatTypeJSON ProxyAccessLogFormatType = "JSON" + // TODO: support format type "mix" in the future. +) + +// ProxyAccessLogFormat defines the format of accesslog. +// +union +type ProxyAccessLogFormat struct { + // Type defines the type of accesslog format. + // +kubebuilder:validation:Enum=Text;JSON + // +unionDiscriminator + Type ProxyAccessLogFormatType `json:"type,omitempty"` + // Text defines the text accesslog format, following Envoy accesslog formatting, + // empty value results in proxy's default access log format. + // It's required when the format type is "Text". + // Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. + // The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. + // +optional + Text *string `json:"text,omitempty"` + // JSON is additional attributes that describe the specific event occurrence. + // Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) + // can be used as values for fields within the Struct. + // It's required when the format type is "JSON". + // +optional + JSON map[string]string `json:"json,omitempty"` +} + +type ProxyAccessLogSinkType string + +const ( + // ProxyAccessLogSinkTypeFile defines the file accesslog sink. + ProxyAccessLogSinkTypeFile ProxyAccessLogSinkType = "File" + // ProxyAccessLogSinkTypeOpenTelemetry defines the OpenTelemetry accesslog sink. + ProxyAccessLogSinkTypeOpenTelemetry ProxyAccessLogSinkType = "OpenTelemetry" +) + +type ProxyAccessLogSink struct { + // Type defines the type of accesslog sink. + // +kubebuilder:validation:Enum=File;OpenTelemetry + Type ProxyAccessLogSinkType `json:"type,omitempty"` + // File defines the file accesslog sink. + // +optional + File *FileEnvoyProxyAccessLog `json:"file,omitempty"` + // OpenTelemetry defines the OpenTelemetry accesslog sink. + // +optional + OpenTelemetry *OpenTelemetryEnvoyProxyAccessLog `json:"openTelemetry,omitempty"` +} + +type FileEnvoyProxyAccessLog struct { + // Path defines the file path used to expose envoy access log(e.g. /dev/stdout). + // Empty value disables accesslog. + Path string `json:"path,omitempty"` +} + +// TODO: consider reuse ExtensionService? +type OpenTelemetryEnvoyProxyAccessLog struct { + // Host define the extension service hostname. + Host string `json:"host"` + // Port defines the port the extension service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + // Resources is a set of labels that describe the source of a log entry, including envoy node info. + // It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). + // +optional + Resources map[string]string `json:"resources,omitempty"` + + // TODO: support more OpenTelemetry accesslog options(e.g. TLS, auth etc.) in the future. +} +``` + +### Example + +- The following is an example to disable access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/disable-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: disable-accesslog + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + disable: true +``` + +- The following is an example with text format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/text-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: text-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: File + file: + path: /dev/stdout +``` + +- The following is an example with json format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/json-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: json-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + json: + status: "%RESPONSE_CODE%" + message: "%LOCAL_REPLY_BODY%" + sinks: + - type: File + file: + path: /dev/stdout +``` + +- The following is an example with OpenTelemetry format access log. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/otel-accesslog.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: otel-access-logging + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" +``` + +- The following is an example of sending same format to different sinks. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/accesslog/multi-sinks.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: multi-sinks + namespace: envoy-gateway-system +spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + sinks: + - type: File + file: + path: /dev/stdout + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" +``` diff --git a/site/content/en/v0.6.0/design/backend-traffic-policy.md b/site/content/en/v0.6.0/design/backend-traffic-policy.md new file mode 100644 index 00000000000..82e2341cc02 --- /dev/null +++ b/site/content/en/v0.6.0/design/backend-traffic-policy.md @@ -0,0 +1,156 @@ +--- +title: "BackendTrafficPolicy" +--- + +## Overview + +This design document introduces the `BackendTrafficPolicy` API allowing users to configure +the behavior for how the Envoy Proxy server communicates with upstream backend services/endpoints. + +## Goals + +- Add an API definition to hold settings for configuring behavior of the connection between the backend services +and Envoy Proxy listener. + +## Non Goals + +- Define the API configuration fields in this API. + +## Implementation + +`BackendTrafficPolicy` is an implied hierarchy type API that can be used to extend [Gateway API][]. +It can target either a `Gateway`, or an xRoute (`HTTPRoute`/`GRPCRoute`/etc.). When targeting a `Gateway`, +it will apply the configured settings within ght `BackendTrafficPolicy` to all children xRoute resources of that `Gateway`. +If a `BackendTrafficPolicy` targets an xRoute and a different `BackendTrafficPolicy` targets the `Gateway` that route belongs to, +then the configuration from the policy that is targeting the xRoute resource will win in a conflict. + +### Example + +Here is an example highlighting how a user can configure this API. + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: ipv4-route + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.foo.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: ipv4-service + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: ipv6-route + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.bar.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: ipv6-service + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: default-ipv-policy + namespace: default +spec: + protocols: + enableIPv6: false + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ipv6-support-policy + namespace: default +spec: + protocols: + enableIPv6: true + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: ipv6-route + namespace: default +``` + +## Features / API Fields + +Here is a list of some features that can be included in this API. Note that this list is not exhaustive. + +- Protocol configuration +- Circuit breaking +- Retries +- Keep alive probes +- Health checking +- Load balancing +- Rate limit + +## Design Decisions + +- This API will only support a single `targetRef` and can bind to only a `Gateway` or xRoute (`HTTPRoute`/`GRPCRoute`/etc.) resource. +- This API resource MUST be part of same namespace as the resource it targets. +- There can be only be ONE policy resource attached to a specific `Listener` (section) within a `Gateway` +- If the policy targets a resource but cannot attach to it, this information should be reflected +in the Policy Status field using the `Conflicted=True` condition. +- If multiple polices target the same resource, the oldest resource (based on creation timestamp) will +attach to the Gateway Listeners, the others will not. +- If Policy A has a `targetRef` that includes a `sectionName` i.e. +it targets a specific Listener within a `Gateway` and Policy B has a `targetRef` that targets the same +entire Gateway then + - Policy A will be applied/attached to the specific Listener defined in the `targetRef.SectionName` + - Policy B will be applied to the remaining Listeners within the Gateway. Policy B will have an additional + status condition `Overridden=True`. + +## Alternatives + +- The project can indefinitely wait for these configuration parameters to be part of the [Gateway API][]. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/v0.6.0/design/bootstrap.md b/site/content/en/v0.6.0/design/bootstrap.md new file mode 100644 index 00000000000..c0581347a24 --- /dev/null +++ b/site/content/en/v0.6.0/design/bootstrap.md @@ -0,0 +1,381 @@ +--- +title: "Bootstrap Design" +--- + +## Overview + +[Issue 31][] specifies the need for allowing advanced users to specify their custom +Envoy Bootstrap configuration rather than using the default Bootstrap configuration +defined in Envoy Gateway. This allows advanced users to extend Envoy Gateway and +support their custom use cases such setting up tracing and stats configuration +that is not supported by Envoy Gateway. + +## Goals + +* Define an API field to allow a user to specify a custom Bootstrap +* Provide tooling to allow the user to generate the default Bootstrap configuration + as well as validate their custom Bootstrap. + +## Non Goals + +* Allow user to configure only a section of the Bootstrap + +## API + +Leverage the existing [EnvoyProxy][] resource which can be attached to the [GatewayClass][] using +the [parametersRef][] field, and define a `Bootstrap` field within the resource. If this field is set, +the value is used as the Bootstrap configuration for all managed Envoy Proxies created by Envoy Gateway. + +```go +// EnvoyProxySpec defines the desired state of EnvoyProxy. +type EnvoyProxySpec struct { + ...... + // Bootstrap defines the Envoy Bootstrap as a YAML string. + // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + // to learn more about the syntax. + // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration + // set by Envoy Gateway. + // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources + // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. + // Backward compatibility across minor versions is not guaranteed. + // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default + // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. + // + // +optional + Bootstrap *string `json:"bootstrap,omitempty"` +} +``` + +## Tooling + +A CLI tool `egctl x translate` will be provided to the user to help generate a working Bootstrap configuration. +Here is an example where a user inputs a `GatewayClass` and the CLI generates the `EnvoyProxy` resource with the `Bootstrap` field populated. + +``` +cat < /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: {} +EOF +``` + +This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters. + +The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar": + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes + kubernetes: + foo: bar +EOF +``` + +__Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for +illustration purposes only. + +The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the +File provider: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: File + file: + foo: bar +EOF +``` + +__Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration +purposes only. + +Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use +default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to +manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`: + +```yaml +$ cat << EOF > /etc/envoy-gateway/config.yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: foo +EOF +``` + +With any of the above configuration examples, Envoy Gateway can be started without any additional arguments: + +```shell +$ ./envoy-gateway +``` + +## Data Plane API + +The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects. +Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through +`spec.parametersRef` of the managed GatewayClass. The `EnvoyProxy` API defines the data plane infrastructure +configuration and is represented as the CR referenced by the managed GatewayClass. Key points of this API are: + +* If unreferenced by `gatewayclass.spec.parametersRef`, default parameters will be used to configure the data plane + infrastructure, e.g. expose Envoy network endpoints using a LoadBalancer service. +* Envoy Gateway will follow Gateway API [recommendations][gc] regarding updates to the EnvoyProxy CR: + > It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the + > state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are + > not propagated down to existing Gateways. + +The initial `EnvoyProxy` API: + +```go +// gateway/api/config/v1alpha1/envoyproxy.go + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvoyProxy is the Schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvoyProxySpec `json:"spec,omitempty"` + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// EnvoyProxySpec defines the desired state of Envoy Proxy infrastructure +// configuration. +type EnvoyProxySpec struct { + // Undefined by this design spec. +} + +// EnvoyProxyStatus defines the observed state of EnvoyProxy. +type EnvoyProxyStatus struct { + // Undefined by this design spec. +} +``` + +The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use +cases are better understood. + +### Data Plane Configuration + +GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is +running with the Kubernetes provider. + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration +parameters. The Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening +on port 80. + +The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer +service: + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: example-class +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + name: example-config + group: gateway.envoyproxy.io + kind: EnvoyProxy +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: example-gateway +spec: + gatewayClassName: example-class + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: example-config +spec: + networkPublishing: + type: ClusterIPService +``` + +__Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only. + +[issue_51]: https://github.com/envoyproxy/gateway/issues/51 +[design_doc]: ../system-design/ +[gw_api]: https://gateway-api.sigs.k8s.io/ +[gc]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.GatewayClass +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions diff --git a/site/content/en/v0.6.0/design/eg-metrics.md b/site/content/en/v0.6.0/design/eg-metrics.md new file mode 100644 index 00000000000..60b93840852 --- /dev/null +++ b/site/content/en/v0.6.0/design/eg-metrics.md @@ -0,0 +1,225 @@ +--- +date: 2023-10-10 +title: "Control Plane Observability: Metrics" +--- + +This document aims to cover all aspects of envoy gateway control plane metrics observability. + +{{% alert title="Note" color="secondary" %}} +**Data plane** observability (while important) is outside of scope for this document. For dataplane observability, refer to [here](../metrics). +{{% /alert %}} + +## Current State + +At present, the Envoy Gateway control plane provides logs and controller-runtime metrics, without traces. Logs are managed through our proprietary library (`internal/logging`, a shim to `zap`) and are written to `/dev/stdout`. + +## Goals + +Our objectives include: + ++ Supporting **PULL** mode for Prometheus metrics and exposing these metrics on the admin address. ++ Supporting **PUSH** mode for Prometheus metrics, thereby sending metrics to the Open Telemetry Stats sink via gRPC or HTTP. + +## Non-Goals + +Our non-goals include: + ++ Supporting other stats sinks. + +## Use-Cases + +The use-cases include: + ++ Exposing Prometheus metrics in the Envoy Gateway Control Plane. ++ Pushing Envoy Gateway Control Plane metrics via the Open Telemetry Sink. + +## Design + +### Standards + +Our metrics, will be built upon the [OpenTelemetry](https://opentelemetry.io/) standards. All metrics will be configured via the [OpenTelemetry SDK](https://opentelemetry.io/docs/specs/otel/metrics/sdk/), which offers neutral libraries that can be connected to various backends. + +This approach allows the Envoy Gateway code to concentrate on the crucial aspect - generating the metrics - and delegate all other tasks to systems designed for telemetry ingestion. + +### Attributes + +OpenTelemetry defines a set of [Semantic Conventions](https://opentelemetry.io/docs/concepts/semantic-conventions/), including [Kubernetes specific ones](https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/k8s/). + +These attributes can be expressed in logs (as keys of structured logs), traces (as attributes), and metrics (as labels). + +We aim to use attributes consistently where applicable. Where possible, these should adhere to codified Semantic Conventions; when not possible, they should maintain consistency across the project. + +### Extensibility + +Envoy Gateway supports both **PULL/PUSH** mode metrics, with Metrics exported via Prometheus by default. + +Additionally, Envoy Gateway can export metrics using both the [OTEL gRPC metrics exporter](https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/otlp/#general) and [OTEL HTTP metrics exporter](https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/otlp/#general), which pushes metrics by grpc/http to a remote OTEL collector. + +Users can extend these in two ways: + +#### Downstream Collection + +Based on the exported data, other tools can collect, process, and export telemetry as needed. Some examples include: + ++ Metrics in **PULL** mode: The OTEL collector can scrape Prometheus and export to X. ++ Metrics in **PUSH** mode: The OTEL collector can receive OTEL gRPC/HTTP exporter metrics and export to X. + +While the examples above involve OTEL collectors, there are numerous other systems available. + +#### Vendor extensions + +The OTEL libraries allow for the registration of Providers/Handlers. While we will offer the default ones (PULL via Prometheus, PUSH via OTEL HTTP metrics exporter) mentioned in Envoy Gateway's extensibility, we can easily allow custom builds of Envoy Gateway to plug in alternatives if the default options don't meet their needs. + +For instance, users may prefer to write metrics over the OTLP gRPC metrics exporter instead of the HTTP metrics exporter. This is perfectly acceptable -- and almost impossible to prevent. The OTEL has ways to register their providers/exporters, and Envoy Gateway can ensure its usage is such that it's not overly difficult to swap out a different provider/exporter. + +### Stability + +Observability is, in essence, a user-facing API. Its primary purpose is to be consumed - by both humans and tooling. Therefore, having well-defined guarantees around their formats is crucial. + +Please note that this refers only to the contents of the telemetry - what we emit, the names of things, semantics, etc. Other settings like Prometheus vs OTLP, JSON vs plaintext, logging levels, etc., are not considered. + +I propose the following: + +#### Metrics + +Metrics offer the greatest potential for providing guarantees. They often directly influence alerts and dashboards, making changes highly impactful. This contrasts with traces and logs, which are often used for ad-hoc analysis, where minor changes to information can be easily understood by a human. + +Moreover, there is precedent for this: [Kubernetes Metrics Lifecycle](https://kubernetes.io/docs/concepts/cluster-administration/system-metrics/#metric-lifecycle) has well-defined processes, and Envoy Gateway's dataplane (Envoy Proxy) metrics are de facto stable. + +Currently, all Envoy Gateway metrics lack defined stability. I suggest we categorize all existing metrics as either: + ++ ***Deprecated***: a metric that is intended to be phased out. ++ ***Experimental***: a metric that is off by default. ++ ***Alpha***: a metric that is on by default. + +We should aim to promote a core set of metrics to **Stable** within a few releases. + +## Envoy Gateway API Types + +New APIs will be added to Envoy Gateway config, which are used to manage Control Plane Telemetry bootstrap configs. + +### EnvoyGatewayTelemetry + +``` go +// EnvoyGatewayTelemetry defines telemetry configurations for envoy gateway control plane. +// Control plane will focus on metrics observability telemetry and tracing telemetry later. +type EnvoyGatewayTelemetry struct { + // Metrics defines metrics configuration for envoy gateway. + Metrics *EnvoyGatewayMetrics `json:"metrics,omitempty"` +} +``` + +### EnvoyGatewayMetrics + +> Prometheus will be exposed on 0.0.0.0:19001, which is not supported to be configured yet. + +``` go +// EnvoyGatewayMetrics defines control plane push/pull metrics configurations. +type EnvoyGatewayMetrics struct { + // Sinks defines the metric sinks where metrics are sent to. + Sinks []EnvoyGatewayMetricSink `json:"sinks,omitempty"` + // Prometheus defines the configuration for prometheus endpoint. + Prometheus *EnvoyGatewayPrometheusProvider `json:"prometheus,omitempty"` +} + +// EnvoyGatewayMetricSink defines control plane +// metric sinks where metrics are sent to. +type EnvoyGatewayMetricSink struct { + // Type defines the metric sink type. + // EG control plane currently supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + OpenTelemetry *EnvoyGatewayOpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type EnvoyGatewayOpenTelemetrySink struct { + // Host define the sink service hostname. + Host string `json:"host"` + // Protocol define the sink service protocol. + // +kubebuilder:validation:Enum=grpc;http + Protocol string `json:"protocol"` + // Port defines the port the sink service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +// EnvoyGatewayPrometheusProvider will expose prometheus endpoint in pull mode. +type EnvoyGatewayPrometheusProvider struct { + // Disable defines if disables the prometheus metrics in pull mode. + // + Disable bool `json:"disable,omitempty"` +} + +``` + +#### Example + ++ The following is an example to disable prometheus metric. + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: null + default: info +provider: + type: Kubernetes +telemetry: + metrics: + prometheus: + disable: true +``` + ++ The following is an example to send metric via Open Telemetry sink to OTEL gRPC Collector. + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: null + default: info +provider: + type: Kubernetes +telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: grpc +``` + ++ The following is an example to disable prometheus metric and send metric via Open Telemetry sink to OTEL HTTP Collector at the same time. + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: null + default: info +provider: + type: Kubernetes +telemetry: + metrics: + prometheus: + disable: false + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4318 + protocol: http +``` diff --git a/site/content/en/v0.6.0/design/egctl.md b/site/content/en/v0.6.0/design/egctl.md new file mode 100644 index 00000000000..0f67d99f100 --- /dev/null +++ b/site/content/en/v0.6.0/design/egctl.md @@ -0,0 +1,59 @@ +--- +title: "egctl Design" +--- + +## Motivation + +EG should provide a command line tool with following capabilities: + +- Collect configuration from envoy proxy and gateway +- Analyse system configuration to diagnose any issues in envoy gateway + +This tool is named `egctl`. + +## Syntax + +Use the following syntax to run `egctl` commands from your terminal window: + +```console +egctl [command] [entity] [name] [flags] +``` + +where `command`, `name`, and `flags` are: + +* `command`: Specifies the operation that you want to perform on one or more resources, + for example `config`, `version`. + +* `entity`: Specifies the entity the operation is being performed on such as `envoy-proxy` or `envoy-gateway`. + +* `name`: Specifies the name of the specified instance. + +* `flags`: Specifies optional flags. For example, you can use the `-c` or `--config` flags to specify the values for installing. + +If you need help, run `egctl help` from the terminal window. + +## Operation + +The following table includes short descriptions and the general syntax for all the `egctl` operations: + +| Operation | Syntax | Description | +| --------------| -------------------------------- | -------------------------------------------------------------------------------------| +| `version` | `egctl version` | Prints out build version information. | +| `config` | `egctl config ENTITY` | Retrieve information about proxy configuration from envoy proxy and gateway | +| `analyze` | `egctl analyze` | Analyze EG configuration and print validation messages | +| `experimental`| `egctl experimental` | Subcommand for experimental features. These do not guarantee backwards compatibility | + +## Examples + +Use the following set of examples to help you familiarize yourself with running the commonly used `egctl` operations: + +```console +# Retrieve all information about proxy configuration from envoy +egctl config envoy-proxy all + +# Retrieve listener information about proxy configuration from envoy +egctl config envoy-proxy listener + +# Retrieve information about envoy gateway +egctl config envoy-gateway +``` diff --git a/site/content/en/v0.6.0/design/envoy-patch-policy.md b/site/content/en/v0.6.0/design/envoy-patch-policy.md new file mode 100644 index 00000000000..83ccb035b4d --- /dev/null +++ b/site/content/en/v0.6.0/design/envoy-patch-policy.md @@ -0,0 +1,176 @@ +--- +title: "EnvoyPatchPolicy" +--- + +## Overview + +This design introduces the `EnvoyPatchPolicy` API allowing users to modify the generated Envoy xDS Configuration +that Envoy Gateway generates before sending it to Envoy Proxy. + +Envoy Gateway allows users to configure networking and security intent using the +upstream [Gateway API][] as well as implementation specific [Extension APIs][] defined in this project +to provide a more batteries included experience for application developers. +* These APIs are an abstracted version of the underlying Envoy xDS API to provide a better user experience for the application developer, exposing and setting only a subset of the fields for a specific feature, sometimes in a opinionated way (e.g [RateLimit][]) +* These APIs do not expose all the features capabilities that Envoy has either because these features are desired but the API +is not defined yet or the project cannot support such an extensive list of features. +To alleviate this problem, and provide an interim solution for a small section of advanced users who are well versed in +Envoy xDS API and its capabilities, this API is being introduced. + +## Goals +* Add an API allowing users to modify the generated xDS Configuration + +## Non Goals +* Support multiple patch mechanisms + +## Implementation +`EnvoyPatchPolicy` is a [Direct Policy Attachment][] type API that can be used to extend [Gateway API][] +Modifications to the generated xDS configuration can be provided as a JSON Patch which is defined in +[RFC 6902][]. This patching mechanism has been adopted in [Kubernetes][] as well as [Kustomize][] to update +resource objects. + +### Example +Here is an example highlighting how a user can configure global ratelimiting using an external rate limit service using this API. + +``` +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + name: ratelimit-patch-policy + namespace: default +spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + # The listener name is of the form // + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eag-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + # The route name is of the form // + name: default/eg/http + operation: + op: add + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 +``` + + +## Verification +* Offline - Leverage [egctl x translate][] to ensure that the `EnvoyPatchPolicy` can be successfully applied and the desired +output xDS is created. +* Runtime - Use the `Status` field within `EnvoyPatchPolicy` to highlight whether the patch was applied successfully or not. + +## State of the World +* Istio - Supports the [EnvoyFilter][] API which allows users to customize the output xDS using patches and proto based merge +semantics. + +## Design Decisions +* This API will only support a single `targetRef` and can bind to only a `Gateway` resource. This simplifies reasoning of how +patches will work. +* This API will always be an experimental API and cannot be graduated into a stable API because Envoy Gateway cannot garuntee + * that the naming scheme for the generated resources names will not change across releases + * that the underlying Envoy Proxy API will not change across releases +* This API needs to be explicitly enabled using the [EnvoyGateway][] API + +## Open Questions +* Should the value only support JSON or YAML as well (which is a JSON superset) ? + +## Alternatives +* Users can customize the Envoy [Bootstrap configuration using EnvoyProxy API][] and provide static xDS configuration. +* Users can extend functionality by [Extending the Control Plane][] and adding gRPC hooks to modify the generated xDS configuration. + + + +[Direct Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/#direct-policy-attachment +[RFC 6902]: https://datatracker.ietf.org/doc/html/rfc6902 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[Kubernetes]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ +[Kustomize]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md +[Extension APIs]: ../../api/extension_types/ +[RateLimit]: ../../user/rate-limit/ +[EnvoyGateway]: ../../api/extension_types#envoygateway +[Extending the Control Plane]: ../extending-envoy-gateway/ +[EnvoyFilter]: https://istio.io/latest/docs/reference/config/networking/envoy-filter +[egctl x translate]: ../../user/egctl#egctl-experimental-translate +[Bootstrap configuration using EnvoyProxy API]: ../../user/customize-envoyproxy#customize-envoyproxy-bootstrap-config diff --git a/site/content/en/v0.6.0/design/extending-envoy-gateway.md b/site/content/en/v0.6.0/design/extending-envoy-gateway.md new file mode 100644 index 00000000000..d84f553efde --- /dev/null +++ b/site/content/en/v0.6.0/design/extending-envoy-gateway.md @@ -0,0 +1,327 @@ +--- +title: "Envoy Gateway Extensions Design" +--- + +As outlined in the [official goals][] for the Envoy Gateway project, one of the main goals is to "provide a common foundation for vendors to build value-added products +without having to re-engineer fundamental interactions". Development of the Envoy Gateway project has been focused on developing the core features for the project and +Kubernetes Gateway API conformance. This system focuses on the “common foundation for vendors” component by introducing a way for vendors to extend Envoy Gateway. + +To meaningfully extend Envoy Gateway and provide additional features, Extensions need to be able to introduce their own custom resources and have a high level of control +over the configuration generated by Envoy Gateway. Simply applying some static xDS configuration patches or relying on the existing Gateway API resources are both insufficient on their own +as means to add larger features that require dynamic user-configuration. + +As an example, an extension developer may wish to provide their own out-of-the-box authentication filters that require configuration from the end-user. This is a scenario where the ability to introduce +custom resources and attach them to [HTTPRoute][]s as an [ExtensionRef][] is necessary. Providing the same feature through a series of xDS patch resources would be too cumbersome for many end-users that want to avoid +that level of complexity when managing their clusters. + +## Goals + +- Provide a foundation for extending the Envoy Gateway control plane +- Allow Extension Developers to introduce their own custom resources for extending the Gateway-API via [ExtensionRefs][], [policyAttachments][] (future) and [backendRefs][] (future). +- Extension developers should **NOT** have to maintain a custom fork of Envoy Gateway +- Provide a system for extending Envoy Gateway which allows extension projects to ship updates independent of Envoy Gateway's release schedule +- Modify the generated Envoy xDS config +- Setup a foundation for the initial iteration of Extending Envoy Gateway +- Allow an Extension to hook into the infra manager pipeline (future) + +## Non-Goals + +- The initial design does not capture every hook that Envoy Gateway will eventually support. +- Extend [Gateway API Policy Attachments][]. At some point, these will be addressed using this extension system, but the initial implementation omits these. +- Support multiple extensions at the same time. Due to the fact that extensions will be modifying xDS resources after they are generated, handling the order of extension execution for each individual hook point is a challenge. Additionally, there is no +real way to prevent one extension from overwriting or breaking modifications to xDS resources that were made by another extension that was executed first. + +## Overview + +Envoy Gateway can be extended by vendors by means of an extension server developed by the vendor and deployed alongside Envoy Gateway. +An extension server can make use of one or more pre/post hooks inside Envoy Gateway before and after its major components (translator, etc.) to allow the extension to modify the data going into or coming out of these components. +An extension can be created external to Envoy Gateway as its own Kubernetes deployment or loaded as a sidecar. gRPC is used for the calls between Envoy Gateway and an extension. In the hook call, Envoy Gateway sends data as well +as context information to the extension and expects a reply with a modified version of the data that was sent to the extension. Since extensions fundamentally alter the logic and data that Envoy Gateway provides, Extension projects assume responsibility for any bugs and issues +they create as a direct result of their modification of Envoy Gateway. + +## Diagram + +![Architecture](/img/extension-example.png) + +## Registering Extensions in Envoy Gateway + +Information about the extension that Envoy Gateway needs to load is configured in the Envoy Gateway config. + +An example configuration: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +extensionManager: + resources: + - group: example.myextension.io + version: v2 + kind: OAuth2Filter + hooks: + xdsTranslator: + post: + - Route + - VirtualHost + - HTTPListener + - Translation + service: + host: my-extension.example + port: 443 + tls: + certificateRef: + name: my-secret + namespace: default +``` + +An extension must supply connection information in the `extension.service` field so that Envoy Gateway can communicate with the extension. The `tls` configuration is optional. + +If the extension wants Envoy Gateway to watch resources for it then the extension must configure the optional `extension.resources` field and supply a list of: + +- `group`: the API group of the resource +- `version`: the API version of the resource +- `kind`: the Kind of resource + +The extension can configure the `extensionManager.hooks` field to specify which hook points it would like to support. If a given hook is not listed here then it will not be executed even +if the extension is configured properly. This allows extension developers to only opt-in to the hook points they want to make use of. + +This configuration is required to be provided at bootstrap and modifying the registered extension during runtime is not currently supported. +Envoy Gateway will keep track of the registered extension and its API `groups` and `kinds` when processing Gateway API resources. + +## Extending Gateway API and the Data Plane + +Envoy Gateway manages [Envoy][] deployments, which act as the data plane that handles actual user traffic. Users configure the data plane using the K8s Gateway API resources which Envoy +Gateway converts into [Envoy specific configuration (xDS)][] to send over to Envoy. + +Gateway API offers [ExtensionRef filters][] and [Policy Attachments][] as extension points for implementers to use. Envoy Gateway extends the Gateway API using these extension points to provide support for [rate limiting][] +and [authentication][] native to the project. The initial design of Envoy Gateway extensions will primarily focus on ExtensionRef filters so that extension developers can reference their own resources as HTTP Filters in the same way +that Envoy Gateway has native support for rate limiting and authentication filters. + +When Envoy Gateway encounters an [HTTPRoute][] or [GRPCRoute][] that has an `ExtensionRef` `filter` with a `group` and `kind` that Envoy Gateway does not support, it will first +check the registered extension to determine if it supports the referenced object before considering it a configuration error. + +This allows users to be able to reference additional filters provided by their Envoy Gateway Extension, in their `HTTPRoute`s / `GRPCRoute`s: + +```yaml +apiVersion: example.myextension.io/v1alpha1 +kind: OAuth2Filter +metadata: + name: oauth2-filter +spec: + ... + +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - clientSelectors: + - path: + type: PathPrefix + value: / + filters: + - type: ExtensionRef + extensionRef: + group: example.myextension.io + kind: OAuth2Filter + name: oauth2-filter + backendRefs: + - name: backend + port: 3000 +``` + +In order to enable the usage of new resources introduced by an extension for translation and xDS modification, Envoy Gateway provides hook points within the translation pipeline, where it calls out to the extension service registered in the [EnvoyGateway config][] +if they specify an `group` that matches the `group` of an `ExtensionRef` filter. The extension will then be able to modify the xDS that Envoy Gateway generated and send back the +modified configuration. If an extension is not registered or if the registered extension does not specify support for the `group` of an `ExtensionRef` filter then Envoy Gateway will treat it as an unknown resource +and provide an error to the user. + +**Note:** Currently (as of [v1][]) Gateway API does not provide a means to specify the namespace or version of an object referenced as an `ExtensionRef`. The extension mechanism will assume that +the namespace of any `ExtensionRef` is the same as the namespace of the `HTTPRoute` or `GRPCRoute` it is attached to rather than treating the `name` field of an `ExtensionRef` as a `name.namespace` string. +If Gateway API adds support for these fields then the design of the Envoy Gateway extensions will be updated to support them. + +## Watching New Resources + +Envoy Gateway will dynamically create new watches on resources introduced by the registered Extension. It does so by using the [controller-runtime][] to create new watches on [Unstructured][] resources that match the `version`s, `group`s, and `kind`s that the +registered extension configured. When communicating with an extension, Envoy Gateway sends these Unstructured resources over to the extension. This eliminates the need for the extension to create its own watches which would have a strong chance of creating race conditions and reconciliation loops when resources change. When an extension receives the Unstructured resources from Envoy Gateway it can perform its own type validation on them. Currently we make the simplifying assumption that the registered extension's `Kinds` are filters referenced by `extensionRef` in `HTTPRouteFilter`s . Support for Policy attachments will be introduced at a later time. + +## xDS Hooks API + +Envoy Gateway supports the following hooks as the initial foundation of the Extension system. Additional hooks can be developed using this extension system at a later point as new use-cases and needs are discovered. The primary iteration of the extension hooks +focuses solely on the modification of xDS resources. + +### Route Modification Hook + +The [Route][] level Hook provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized. +Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the +Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to ext_authz filters. +The Post Route Modify hook also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that created this xDS route +This hook is always executed when an extension is loaded that has added `Route` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`, and only on Routes which were generated from an HTTPRoute that uses extension resources as externalRef filters. + +```go +// PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified +message PostRouteModifyRequest { + envoy.config.route.v3.Route route = 1; + PostRouteExtensionContext post_route_context = 2; +} + +// RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway +// additional context information can be added to this message as more use-cases are discovered +message PostRouteExtensionContext { + // Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute + repeated ExtensionResource extension_resources = 1; + + // hostnames are the fully qualified domain names attached to the HTTPRoute + repeated string hostnames = 2; +} + +// ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter +// extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension +// can marshal the bytes from this resource back into an unstructured.Unstructured and then +// perform type checking to obtain the resource. +message ExtensionResource { + bytes unstructured_bytes = 1; +} + +// PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent +// If an extension returns a nil Route then it will not be modified +message PostRouteModifyResponse { + envoy.config.route.v3.Route route = 1; +} +``` + +### VirtualHost Modification Hook + +The [VirtualHost][] Hook provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized. +An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway. +This hook is always executed when an extension is loaded that has added `VirtualHost` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. +An extension may return nil to not make any changes to the VirtualHost. + +```protobuf +// PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified +message PostVirtualHostModifyRequest { + envoy.config.route.v3.VirtualHost virtual_host = 1; + PostVirtualHostExtensionContext post_virtual_host_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostVirtualHostExtensionContext {} + +// PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent +// If an extension returns a nil Virtual Host then it will not be modified +message PostVirtualHostModifyResponse { + envoy.config.route.v3.VirtualHost virtual_host = 1; +} +``` + +### HTTP Listener Modification Hook + +The HTTP [Listener][] modification hook is the broadest xDS modification Hook available and allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized. +This hook is always executed when an extension is loaded that has added `HTTPListener` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. An extension may return nil +in order to not make any changes to the Listener. + +```protobuf +// PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified +message PostHTTPListenerModifyRequest { + envoy.config.listener.v3.Listener listener = 1; + PostHTTPListenerExtensionContext post_listener_context = 2; +} + +// Empty for now but we can add fields to the context as use-cases are discovered without +// breaking any clients that use the API +// additional context information can be added to this message as more use-cases are discovered +message PostHTTPListenerExtensionContext {} + +// PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent +// If an extension returns a nil Listener then it will not be modified +message PostHTTPListenerModifyResponse { + envoy.config.listener.v3.Listener listener = 1; +} +``` + +### Post xDS Translation Modify Hook + +The Post Translate Modify hook allows an extension to modify the clusters and secrets in the xDS config. +This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than +using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed. +An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension. +The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets +This hook is always executed when an extension is loaded that has added `Translation` to the `EnvoyProxy.extensionManager.hooks.xdsTranslator.post`. + +```protobuf +// PostTranslateModifyRequest currently sends only clusters and secrets to an extension. +// The extension is free to add/modify/remove the resources it received. +message PostTranslateModifyRequest { + PostTranslateExtensionContext post_translate_context = 1; + repeated envoy.config.cluster.v3.Cluster clusters = 2; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3; +} + +// PostTranslateModifyResponse is the expected response from an extension and contains +// the full list of xDS clusters and secrets to be used for the xDS config. +message PostTranslateModifyResponse { + repeated envoy.config.cluster.v3.Cluster clusters = 1; + repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 2; +} +``` + +### Extension Service + +Currently, an extension must implement all of the following hooks although it may return the input(s) it received +if no modification of the resource is desired. A future expansion of the extension hooks will allow an Extension to specify +with config which Hooks it would like to "subscribe" to and which Hooks it does not wish to support. These specific Hooks were chosen +in order to provide extensions with the ability to have both broad and specific control over xDS resources and to minimize the amount of data being sent. + +```protobuf +service EnvoyGatewayExtension { + rpc PostRouteModify (PostRouteModifyRequest) returns (PostRouteModifyResponse) {}; + rpc PostVirtualHostModify(PostVirtualHostModifyRequest) returns (PostVirtualHostModifyResponse) {}; + rpc PostHTTPListenerModify(PostHTTPListenerModifyRequest) returns (PostHTTPListenerModifyResponse) {}; + rpc PostTranslateModify(PostTranslateModifyRequest) returns (PostTranslateModifyResponse) {}; +} +``` + +## Design Decisions + +- Envoy Gateway watches new custom resources introduced by a loaded extension and passes the resources back to the extension when they are used. + - This decision was made to solve the problem about how resources introduced by an extension get watched. If an extension server watches its own resources then it would need some way to trigger an Envoy Gateway reconfigure when a resource that Envoy Gateway is not watching gets updated. Having Envoy Gateway watch all resources removes any concern about creating race confitions or reconcile loops that would result from Envoy Gateway and the extension server both having so much separate state that needs to be synchronized. +- The Extension Server takes ownership of producing the correct xDS configuration in the hook responses +- The Extension Server will be responsible for ensuring the performance of the hook processing time +- The Post xDS level gRPC hooks all currently send a context field even though it contains nothing for several hooks. These fields exist so that they can be updadated in the future to pass +additional information to extensions as new use-cases and needs are discovered. +- The initial design supplies the scaffolding for both "pre xDS" and "post xDS" hooks. Only the post hooks are currently implemented which operate on xDS resources after they have been generated. +The pre hooks will be implemented at a later date along with one or more hooks in the infra manager. The infra manager level hook(s) will exist to power use-cases such as dynamically creating Deployments/Services for the extension the +whenever Envoy Gateway creates an instance of Envoy Proxy. An extension developer might want to take advantage of this functionality to inject a new authorization service as a sidecar on the Envoy Proxy deployment for reduced latency. +- Multiple extensions are not be supported at the same time. Preventing conflict between multiple extensions that are mangling xDS resources is too difficult to ensure compatibility with and is likely to only generate issues. + +## Known Challenges + +Extending Envoy Gateway by using an external extension server which makes use of hook points in Envoy Gateway does come with a few trade-offs. One known trade-off is the impact of the time that it takes for the hook calls to be executed. Since an extension would make use of hook points in Envoy Gateway that use gRPC for communication, the time it takes to perform these requests could become a concern for some extension developers. One way to minimize the request time of the hook calls is to load the extension server as a sidecar to Envoy Gateway to minimize the impact of networking on the hook calls. + +[official goals]: ../goals#extensibility +[ExtensionRef filters]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.LocalObjectReference +[ExtensionRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.LocalObjectReference +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.LocalObjectReference +[backendRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPBackendRef +[Gateway API Policy attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Policy Attachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[policyAttachments]: https://gateway-api.sigs.k8s.io/references/policy-attachment/?h=policy +[Envoy]: https://www.envoyproxy.io/ +[Envoy specific configuration (xDS)]: https://www.envoyproxy.io/docs/envoy/v1.25.1/configuration/configuration +[v1]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1 +[rate limiting]: ../../user/rate-limit/ +[authentication]: ../../user/jwt-authentication/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[EnvoyGateway config]: ../../api/extension_types#envoygateway +[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime +[Unstructured]: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured +[Listener]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener +[VirtualHost]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost +[Route]: https://www.envoyproxy.io/docs/envoy/v1.23.0/api-v3/config/route/v3/route_components.proto#config-route-v3-route diff --git a/site/content/en/v0.6.0/design/gatewayapi-translator.md b/site/content/en/v0.6.0/design/gatewayapi-translator.md new file mode 100644 index 00000000000..a086b80bfee --- /dev/null +++ b/site/content/en/v0.6.0/design/gatewayapi-translator.md @@ -0,0 +1,253 @@ +--- +title: "Gateway API Translator Design" +weight: 4 +--- + +The Gateway API translates external resources, e.g. GatewayClass, from the configured Provider to the Intermediate +Representation (IR). + +## Assumptions + +Initially target core conformance features only, to be followed by extended conformance features. + +## Inputs and Outputs + +The main inputs to the Gateway API translator are: + +- GatewayClass, Gateway, HTTPRoute, TLSRoute, Service, ReferenceGrant, Namespace, and Secret resources. + +__Note:__ ReferenceGrant is not fully implemented as of v0.2. + +The outputs of the Gateway API translator are: + +- Xds and Infra Internal Representations (IRs). +- Status updates for GatewayClass, Gateways, HTTPRoutes + +## Listener Compatibility + +Envoy Gateway follows Gateway API listener compatibility spec: +> Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. 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”. + +__Note:__ Envoy Gateway does not collapse listeners across multiple Gateways. + +### Listener Compatibility Examples + +#### Example 1: Gateway with compatible Listeners (same port & protocol, different hostnames) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 2: Gateway with compatible Listeners (same port & protocol, one hostname specified, one not) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: "*.envoygateway.io" + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +#### Example 3: Gateway with incompatible Listeners (same port, protocol and hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + hostname: whales.envoygateway.io +``` + +#### Example 4: Gateway with incompatible Listeners (neither specify a hostname) + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gateway-1 + namespace: envoy-gateway +spec: + gatewayClassName: envoy-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +``` + +## Computing Status + +Gateway API specifies a rich set of status fields & conditions for each resource. To achieve conformance, Envoy Gateway +must compute the appropriate status fields and conditions for managed resources. + +Status is computed and set for: + +- The managed GatewayClass (`gatewayclass.status.conditions`). +- Each managed Gateway, based on its Listeners' status (`gateway.status.conditions`). For the Kubernetes provider, the + Envoy Deployment and Service status are also included to calculate Gateway status. +- Listeners for each Gateway (`gateway.status.listeners`). +- The ParentRef for each Route (`route.status.parents`). + +The Gateway API translator is responsible for calculating status conditions while translating Gateway API resources to +the IR and publishing status over the [message bus][]. The Status Manager subscribes to these status messages and +updates the resource status using the configured provider. For example, the Status Manager uses a Kubernetes client to +update resource status on the Kubernetes API server. + +## Outline + +The following roughly outlines the translation process. Each step may produce (1) IR; and (2) status updates on Gateway +API resources. + +1. Process Gateway Listeners + - Validate unique hostnames, ports, and protocols. + - Validate and compute supported kinds. + - Validate allowed namespaces (validate selector if specified). + - Validate TLS fields if specified, including resolving referenced Secrets. + +2. Process HTTPRoutes + - foreach route rule: + - compute matches + - [core] path exact, path prefix + - [core] header exact + - [extended] query param exact + - [extended] HTTP method + - compute filters + - [core] request header modifier (set/add/remove) + - [core] request redirect (hostname, statuscode) + - [extended] request mirror + - compute backends + - [core] Kubernetes services + - foreach route parent ref: + - get matching listeners (check Gateway, section name, listener validation status, listener allowed routes, hostname intersection) + - foreach matching listener: + - foreach hostname intersection with route: + - add each computed route rule to host + +## Context Structs + +To help store, access and manipulate information as it's processed during the translation process, a set of context +structs are used. These structs wrap a given Gateway API type, and add additional fields and methods to support +processing. + +`GatewayContext` wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. + +```go +type GatewayContext struct { + // The managed Gateway + *v1beta1.Gateway + + // A list of Gateway ListenerContexts. + listeners []*ListenerContext +} +``` + +`ListenerContext` wraps a Listener and provides helper methods for setting conditions and other status information on +the associated Gateway. + +```go +type ListenerContext struct { + // The Gateway listener. + *v1beta1.Listener + + // The Gateway this Listener belongs to. + gateway *v1beta1.Gateway + + // An index used for managing this listener in the list of Gateway listeners. + listenerStatusIdx int + + // Only Routes in namespaces selected by the selector may be attached + // to the Gateway this listener belongs to. + namespaceSelector labels.Selector + + // The TLS Secret for this Listener, if applicable. + tlsSecret *v1.Secret +} +``` + +`RouteContext` represents a generic Route object (HTTPRoute, TLSRoute, etc.) that can reference Gateway objects. + +```go +type RouteContext interface { + client.Object + + // GetRouteType returns the Kind of the Route object, HTTPRoute, + // TLSRoute, TCPRoute, UDPRoute etc. + GetRouteType() string + + // GetHostnames returns the hosts targeted by the Route object. + GetHostnames() []string + + // GetParentReferences returns the ParentReference of the Route object. + GetParentReferences() []v1beta1.ParentReference + + // GetRouteParentContext returns RouteParentContext by using the Route + // objects' ParentReference. + GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext +} +``` + +[message bus]: ../watching/ diff --git a/site/content/en/v0.6.0/design/goals.md b/site/content/en/v0.6.0/design/goals.md new file mode 100644 index 00000000000..fd38b2004c6 --- /dev/null +++ b/site/content/en/v0.6.0/design/goals.md @@ -0,0 +1,91 @@ +--- +title: "Goals" +weight: 1 +--- + +The high-level goal of the Envoy Gateway project is to attract more users to Envoy by lowering barriers to adoption +through expressive, extensible, role-oriented APIs that support a multitude of ingress and L7/L4 traffic routing +use cases; and provide a common foundation for vendors to build value-added products without having to re-engineer +fundamental interactions. + +## Objectives + +### Expressive API + +The Envoy Gateway project will expose a simple and expressive API, with defaults set for many capabilities. + +The API will be the Kubernetes-native [Gateway API][], plus Envoy-specific extensions and extension points. This +expressive and familiar API will make Envoy accessible to more users, especially application developers, and make Envoy +a stronger option for "getting started" as compared to other proxies. Application developers will use the API out of +the box without needing to understand in-depth concepts of Envoy Proxy or use OSS wrappers. The API will use familiar +nouns that [users](#personas) understand. + +The core full-featured Envoy xDS APIs will remain available for those who need more capability and for those who +add functionality on top of Envoy Gateway, such as commercial API gateway products. + +This expressive API will not be implemented by Envoy Proxy, but rather an officially supported translation layer +on top. + +### Batteries included + +Envoy Gateway will simplify how Envoy is deployed and managed, allowing application developers to focus on +delivering core business value. + +The project plans to include additional infrastructure components required by users to fulfill their Ingress and API +gateway needs: It will handle Envoy infrastructure provisioning (e.g. Kubernetes Service, Deployment, et cetera), and +possibly infrastructure provisioning of related sidecar services. It will include sensible defaults with the ability to +override. It will include channels for improving ops by exposing status through API conditions and Kubernetes status +sub-resources. + +Making an application accessible needs to be a trivial task for any developer. Similarly, infrastructure administrators +will enjoy a simplified management model that doesn't require extensive knowledge of the solution's architecture to +operate. + +### All environments + +Envoy Gateway will support running natively in Kubernetes environments as well as non-Kubernetes deployments. + +Initially, Kubernetes will receive the most focus, with the aim of having Envoy Gateway become the de facto +standard for Kubernetes ingress supporting the [Gateway API][]. +Additional goals include multi-cluster support and various runtime environments. + +### Extensibility + +Vendors will have the ability to provide value-added products built on the Envoy Gateway foundation. + +It will remain easy for end-users to leverage common Envoy Proxy extension points such as providing an implementation +for authentication methods and rate-limiting. For advanced use cases, users will have the ability to use the full power +of xDS. + +Since a general-purpose API cannot address all use cases, Envoy Gateway will provide additional extension points +for flexibility. As such, Envoy Gateway will form the base of vendor-provided managed control plane solutions, +allowing vendors to shift to a higher management plane layer. + +## Non-objectives + +### Cannibalize vendor models + +Vendors need to have the ability to drive commercial value, so the goal is not to cannibalize any existing vendor +monetization model, though some vendors may be affected by it. + +### Disrupt current Envoy usage patterns + +Envoy Gateway is purely an additive convenience layer and is not meant to disrupt any usage pattern of any user +with Envoy Proxy, xDS, or go-control-plane. + +## Personas + +_In order of priority_ + +### 1. Application developer + +The application developer spends the majority of their time developing business logic code. They require the ability to +manage access to their application. + +### 2. Infrastructure administrators + +The infrastructure administrators are responsible for the installation, maintenance, and operation of +API gateways appliances in infrastructure, such as CRDs, roles, service accounts, certificates, etc. +Infrastructure administrators support the needs of application developers by managing instances of Envoy Gateway. + +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/v0.6.0/design/local-envoy-gateway.md b/site/content/en/v0.6.0/design/local-envoy-gateway.md new file mode 100644 index 00000000000..81265297e14 --- /dev/null +++ b/site/content/en/v0.6.0/design/local-envoy-gateway.md @@ -0,0 +1,52 @@ +--- +title: "Running Envoy Gateway locally" +--- + +## Overview + +Today, Envoy Gateway runs only on Kubernetes. This is an ideal solution +when the applications are running in Kubernetes. +However there might be cases when the applications are running on the host which would +require Envoy Gateway to run locally. + +## Goals + +* Define an API to allow Envoy Gateway to retrieve configuration while running locally. +* Define an API to allow Envoy Gateway to deploy the managed Envoy Proxy fleet on the host +machine. + +## Non Goals + +* Support multiple ways to retrieve configuration while running locally. +* Support multiple ways to deploy the Envoy Proxy fleet locally on the host. + +## API + +* The `provider` field within the `EnvoyGateway` configuration only supports +`Kubernetes` today which provides two features - the ability to retrieve +resources from the Kubernetes API Server as well as deploy the managed +Envoy Proxy fleet on Kubernetes. +* This document proposes adding a new top level `provider` type called `Custom` +with two fields called `resource` and `infrastructure` to allow the user to configure +the sub providers for providing resource configuration and an infrastructure to deploy +the Envoy Proxy data plane in. +* A `File` resource provider will be introduced to enable retrieving configuration locally +by reading from the configuration from a file. +* A `Host` infrastructure provider will be introduced to allow Envoy Gateway to spawn a +Envoy Proxy child process on the host. + +Here is an example configuration + +``` +provider: + type: Custom + custom: + resource: + type: File + file: + paths: + - "config.yaml" + infrastructure: + type: Host + host: {} +``` diff --git a/site/content/en/v0.6.0/design/metrics.md b/site/content/en/v0.6.0/design/metrics.md new file mode 100644 index 00000000000..a154a31e871 --- /dev/null +++ b/site/content/en/v0.6.0/design/metrics.md @@ -0,0 +1,112 @@ +--- +title: "Observability: Metrics" +--- + +## Overview + +Envoy provide robust platform for metrics, Envoy support three different kinds of stats: counter, gauges, histograms. + +Envoy enables prometheus format output via the `/stats/prometheus` [admin endpoint](https://www.envoyproxy.io/docs/envoy/latest/operations/admin). + +Envoy support different kinds of sinks, but EG will only support [Open Telemetry sink](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto). + +Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementers such as Envoy Gateway to expose features. Since metrics is not covered by `Core` or `Extended` APIs, EG should provide an easy to config metrics per `EnvoyProxy`. + +## Goals + +- Support expose metrics in prometheus way(reuse probe port). +- Support Open Telemetry stats sink. + +## Non-Goals + +- Support other stats sink. + +## Use-Cases + +- Enable prometheus metric by default +- Disable prometheus metric +- Push metrics via Open Telemetry Sink +- TODO: Customize histogram buckets of target metric +- TODO: Support stats matcher + +### ProxyMetric API Type + +```golang mdox-exec="sed '1,7d' api/v1alpha1/metric_types.go" +type ProxyMetrics struct { + // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. + Prometheus *PrometheusProvider `json:"prometheus,omitempty"` + // Sinks defines the metric sinks where metrics are sent to. + Sinks []MetricSink `json:"sinks,omitempty"` +} + +type MetricSinkType string + +const ( + MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" +) + +type MetricSink struct { + // Type defines the metric sink type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + OpenTelemetry *OpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +type OpenTelemetrySink struct { + // Host define the service hostname. + Host string `json:"host"` + // Port defines the port the service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + + // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig +} + +type PrometheusProvider struct { + // Disable the Prometheus endpoint. + Disable bool `json:"disable,omitempty"` +} +``` + +### Example + +- The following is an example to disable prometheus metric. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/disable-prometheus.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: prometheus + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + prometheus: + disable: true +``` + +- The following is an example to send metric via Open Telemetry sink. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/metric/otel-sink.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: otel-sink + namespace: envoy-gateway-system +spec: + telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 +``` diff --git a/site/content/en/v0.6.0/design/pprof.md b/site/content/en/v0.6.0/design/pprof.md new file mode 100644 index 00000000000..e2dd3082b26 --- /dev/null +++ b/site/content/en/v0.6.0/design/pprof.md @@ -0,0 +1,70 @@ +--- +title: "Debug support in Envoy Gateway" +--- + +## Overview + +Envoy Gateway exposes endpoints at `localhost:19000/debug/pprof` to run Golang profiles to aid in live debugging. + +The endpoints are equivalent to those found in the http/pprof package. `/debug/pprof/` returns an HTML page listing the available profiles. + +## Goals + +* Add admin server to Envoy Gateway control plane, separated with admin server. +* Add pprof support to Envoy Gateway control plane. +* Define an API to allow Envoy Gateway to custom admin server configuration. +* Define an API to allow Envoy Gateway to open envoy gateway config dump in logs. + +The following are the different types of profiles end-user can run: + +PROFILE | FUNCTION +-- | -- +/debug/pprof/allocs | Returns a sampling of all past memory allocations. +/debug/pprof/block | Returns stack traces of goroutines that led to blocking on synchronization primitives. +/debug/pprof/cmdline | Returns the command line that was invoked by the current program. +/debug/pprof/goroutine | Returns stack traces of all current goroutines. +/debug/pprof/heap | Returns a sampling of memory allocations of live objects. +/debug/pprof/mutex | Returns stack traces of goroutines holding contended mutexes. +/debug/pprof/profile | Returns pprof-formatted cpu profile. You can specify the duration using the seconds GET parameter. The default duration is 30 seconds. +/debug/pprof/symbol | Returns the program counters listed in the request. +/debug/pprof/threadcreate | Returns stack traces that led to creation of new OS threads. +/debug/pprof/trace | Returns the execution trace in binary form. You can specify the duration using the seconds GET parameter. The default duration is 1 second. + +## Non Goals + +## API + +* Add `admin` field in EnvoyGateway config. +* Add `address` field under `admin` field. +* Add `port` and `host` under `address` field. +* Add `enableDumpConfig` field under `admin field. +* Add `enablePprof` field under `admin field. + +Here is an example configuration to open admin server and enable Pprof: + +``` yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +gateway: + controllerName: "gateway.envoyproxy.io/gatewayclass-controller" +kind: EnvoyGateway +provider: + type: "Kubernetes" +admin: + enablePprof: true + address: + host: 127.0.0.1 + port: 19000 +``` + +Here is an example configuration to open envoy gateway config dump in logs: + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +gateway: + controllerName: "gateway.envoyproxy.io/gatewayclass-controller" +kind: EnvoyGateway +provider: + type: "Kubernetes" +admin: + enableDumpConfig: true +``` diff --git a/site/content/en/v0.6.0/design/rate-limit.md b/site/content/en/v0.6.0/design/rate-limit.md new file mode 100644 index 00000000000..166e769c9ca --- /dev/null +++ b/site/content/en/v0.6.0/design/rate-limit.md @@ -0,0 +1,447 @@ +--- +title: "Rate Limit Design" +--- + +## Overview + +Rate limit is a feature that allows the user to limit the number of incoming requests +to a predefined value based on attributes within the traffic flow. + +Here are some reasons why a user may want to implement Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +## Scope Types + +The rate limit type here describes the scope of rate limits. + +* Global - In this case, the rate limit is common across all the instances of Envoy proxies +where its applied i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is +10 requests/second, this limit is common and will be hit if 5 requests pass through the first replica +and 5 requests pass through the second replica within the same second. + +* Local - In this case, the rate limits are specific to each instance/replica of Envoy running. +Note - This is not part of the initial design and will be added as a future enhancement. + +## Match Types + +### Rate limit a specific traffic flow + +* Here is an example of a ratelimit implemented by the application developer to limit a specific user +by matching on a custom `x-user-id` header with a value set to `one` + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-specific-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-specific-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit all traffic flows + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route to safeguard health of internal application components. In this case, no specific `headers` match +is specified, and the rate limit is applied to all traffic flows accepted by this `HTTPRoute`. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-all-requests +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - limit: + requests: 1000 + unit: Second +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-requests + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per distinct value + +* Here is an example of a rate limit implemented by the application developer to limit any unique user +by matching on a custom `x-user-id` header. Here, user A (recognised from the traffic flow using the header +`x-user-id` and value `a`) will be rate limited at 10 requests/hour and so will user B +(recognised from the traffic flow using the header `x-user-id` and value `b`). + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-per-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit per source IP + +* Here is an example of a rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on source IP. In this case, requests from `x.x.x.x` will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-per-ip +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - sourceIP: x.x.x.x/32 + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + backendRefs: + - name: backend + port: 3000 +``` + +### Rate limit based on JWT claims + +* Here is an example of rate limit implemented by the application developer that limits the total requests made +to a specific route by matching on the jwt claim. In this case, requests with jwt claim information of `{"name":"John Doe"}` +will be rate limited at 10 requests/hour. + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-example +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + claimToHeaders: + - claim: name + header: custom-request-header +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-specific-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: custom-request-header + value: John Doe + limit: + requests: 10 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: /foo +``` + + +## Multiple RateLimitFilters, rules and clientSelectors +* Users can create multiple `RateLimitFilter`s and apply it to the same `HTTPRoute`. In such a case each +`RateLimitFilter` will be applied to the route and matched (and limited) in a mutually exclusive way, independent of each other. +* Rate limits are applied for each `RateLimitFilter` `rule` when ALL the conditions under `clientSelectors` hold true. + +Here's an example highlighting this - + +```yaml +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-all-safeguard-app +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - limit: + requests: 100 + unit: Hour +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-per-user +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - type: Distinct + name: x-user-id + limit: + requests: 100 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: example +spec: + parentRefs: + - name: eg + hostnames: + - www.example.com + rules: + - matches: + - path: + type: PathPrefix + value: /foo + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-per-user + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: RateLimitFilter + name: ratelimit-all-safeguard-app + backendRefs: + - name: backend + port: 3000 +``` + +* The user has created two `RateLimitFilter`s and has attached it to a `HTTPRoute` - one(`ratelimit-all-safeguard-app`) to +ensure that the backend does not get overwhelmed with requests, any excess requests are rate limited irrespective of +the attributes within the traffic flow, and another(`ratelimit-per-user`) to rate limit each distinct user client +who can be differentiated using the `x-user-id` header, to ensure that each client does not make excessive requests to the backend. +* If user `baz` (identified with the header and value of `x-user-id: baz`) sends 90 requests within the first second, and +user `bar` sends 11 more requests during that same interval of 1 second, and user `bar` sends the 101th request within that second, +the rule defined in `ratelimit-all-safeguard-app` gets activated and Envoy Gateway will ratelimit the request sent by `bar` (and any other +request sent within that 1 second). After 1 second, the rate limit counter associated with the `ratelimit-all-safeguard-app` rule +is reset and again evaluated. +* If user `bar` also ends up sending 90 more requests within the hour, summing up `bar`'s total request count to 101, the rate limit rule +defined within `ratelimit-per-user` will get activated, and `bar`'s requests will be rate limited again until the hour interval ends. +* Within the same above hour, if `baz` sends 991 more requests, summing up `baz`'s total request count to 1001, the rate limit rule defined +within `ratelimit-per-user` will get activated for `baz`, and `baz`'s requests will also be rate limited until the hour interval ends. + +## Design Decisions + +* The initial design uses an Extension filter to apply the Rate Limit functionality on a specific [HTTPRoute][]. +This was preferred over the [PolicyAttachment][] extension mechanism, because it is unclear whether Rate Limit +will be required to be enforced or overridden by the platform administrator or not. +* The RateLimitFilter can only be applied as a filter to a [HTTPRouteRule][], applying it across all backends within a [HTTPRoute][] +and cannot be applied a filter within a [HTTPBackendRef][] for a specific backend. +* The [HTTPRoute][] API has a [matches][] field within each [rule][] to select a specific traffic flow to be routed to +the destination backend. The RateLimitFilter API that can be attached to an HTTPRoute via an [extensionRef][] filter, +also has a `clientSelectors` field within each `rule` to select attributes within the traffic flow to rate limit specific clients. +The two levels of selectors/matches allow for flexibility and aim to hold match information specific to its use, allowing the author/owner +of each configuration to be different. It also allows the `clientSelectors` field within the RateLimitFilter to be enhanced with other matchable +attribute such as [IP subnet][] in the future that are not relevant in the [HTTPRoute][] API. + +## Implementation Details + +### Global Rate limiting + +* [Global rate limiting][] in Envoy Proxy can be achieved using the following - + * [Actions][] can be configured per [xDS Route][]. + * If the match criteria defined within these actions is met for a specific HTTP Request, a set of key value pairs called [descriptors][] + defined within the above actions is sent to a remote [rate limit service][], whose configuration (such as the URL for the rate limit service) is defined + using a [rate limit filter][]. + * Based on information received by the rate limit service and its programmed configuration, a decision is computed, whether to rate limit + the HTTP Request or not, and is sent back to Envoy, which enforces this decision on the data plane. +* Envoy Gateway will leverage this Envoy Proxy feature by - + * Translating the user facing RateLimitFilter API into Rate limit [Actions][] as well as Rate limit service configuration to implement + the desired API intent. + * Envoy Gateway will use the existing [reference implementation][] of the rate limit service. + * The Infrastructure administrator will need to enable the rate limit service using new settings that will be defined in the [EnvoyGateway][] config API. + * The xDS IR will be enhanced to hold the user facing rate limit intent. + * The xDS Translator will be enhanced to translate the rate limit field within the xDS IR into Rate limit [Actions][] as well as instantiate the [rate limit filter][]. + * A new runner called `rate-limit` will be added that subscribes to the xDS IR messages and translates it into a new Rate Limit Infra IR which contains + the [rate limit service configuration][] as well as other information needed to deploy the rate limit service. + * The infrastructure service will be enhanced to subscribe to the Rate Limit Infra IR and deploy a provider specific rate limit service runnable entity. + * A Status field within the RateLimitFilter API will be added to reflect whether the specific configuration was programmed correctly in these multiple locations or not. + +[PolicyAttachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/ +[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRoute +[HTTPRouteRule]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPBackendRef +[matches]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteMatch +[rule]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteMatch +[extensionRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilterType +[IP subnet]: https://en.wikipedia.org/wiki/Subnetwork +[Actions]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit-action +[descriptors]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter.html?highlight=descriptor#example-1 +[Global rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[xDS Route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-routeaction +[rate limit filter]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[rate limit service]: https://www.envoyproxy.io/docs/envoy/latest/configuration/other_features/rate_limit#config-rate-limit-service +[reference implementation]: https://github.com/envoyproxy/ratelimit +[EnvoyGateway]: https://github.com/envoyproxy/gateway/blob/main/api/v1alpha1/envoygateway_types.go +[rate limit service configuration]: https://github.com/envoyproxy/ratelimit#configuration diff --git a/site/content/en/v0.6.0/design/security-policy.md b/site/content/en/v0.6.0/design/security-policy.md new file mode 100644 index 00000000000..b39165feb03 --- /dev/null +++ b/site/content/en/v0.6.0/design/security-policy.md @@ -0,0 +1,115 @@ +--- +title: "SecurityPolicy " +--- + +## Overview + +This design document introduces the `SecurityPolicy` API allowing system administrators to configure +authentication and authorization policies to the traffic entering the gateway. + +## Goals +* Add an API definition to hold settings for configuring authentication and authorization rules +on the traffic entering the gateway. + +## Non Goals +* Define the API configuration fields in this API. + +## Implementation +`SecurityPolicy` is a [Policy Attachment][] type API that can be used to extend [Gateway API][] +to define authentication and authorization rules. + +### Example +Here is an example highlighting how a user can configure this API. + +``` +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: https + protocol: HTTPS + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: jwt-authn-policy + namespace: default +spec: + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default +``` + +## Features / API Fields +Here is a list of features that can be included in this API +* JWT based authentication +* OIDC Authentication +* External Authorization +* Basic Auth +* API Key Auth +* CORS + +## Design Decisions +* This API will only support a single `targetRef` and can bind to a `Gateway` resource or a `HTTPRoute` or `GRPCRoute`. +* This API resource MUST be part of same namespace as the targetRef resource +* There can be only be ONE policy resource attached to a specific targetRef e.g. a `Listener` (section) within a `Gateway` +* If the policy targets a resource but cannot attach to it, this information should be reflected +in the Policy Status field using the `Conflicted=True` condition. +* If multiple polices target the same resource, the oldest resource (based on creation timestamp) will +attach to the Gateway Listeners, the others will not. +* If Policy A has a `targetRef` that includes a `sectionName` i.e. +it targets a specific Listener within a `Gateway` and Policy B has a `targetRef` that targets the same +entire Gateway then + * Policy A will be applied/attached to the specific Listener defined in the `targetRef.SectionName` + * Policy B will be applied to the remaining Listeners within the Gateway. Policy B will have an additional + status condition `Overridden=True`. +* A Policy targeting the most specific scope wins over a policy targeting a lesser specific scope. + i.e. A Policy targeting a xRoute (`HTTPRoute` or `GRPCRoute`) overrides a Policy targeting a Listener that is +this route's parentRef which in turn overrides a Policy targeting the Gateway the listener/section is a part of. + +## Alternatives +* The project can indefinitely wait for these configuration parameters to be part of the [Gateway API][]. + +[Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment +[Gateway API]: https://gateway-api.sigs.k8s.io/ diff --git a/site/content/en/v0.6.0/design/system-design.md b/site/content/en/v0.6.0/design/system-design.md new file mode 100644 index 00000000000..956482ffcc3 --- /dev/null +++ b/site/content/en/v0.6.0/design/system-design.md @@ -0,0 +1,174 @@ +--- +title: "System Design" +weight: 2 +--- + +## Goals + +* Define the system components needed to satisfy the requirements of Envoy Gateway. + +## Non-Goals + +* Create a detailed design and interface specification for each system component. + +## Terminology + +* Control Plane- A collection of inter-related software components for providing application gateway and routing + functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane. + These services are detailed in the [components](#components) section. +* Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies. + +## Architecture + +![Architecture](/img/architecture.png) + +## Configuration + +Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][gw_api] objects. + +### Static Configuration + +Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName, +configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the +configuration file is not provided, Envoy Gateway starts-up with default configuration parameters. + +### Dynamic Configuration + +Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using +reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is +defined as Kubernetes resources that provide the following services: + +* Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is + expressed through [GatewayClass][gc] and [Gateway][gw] resources. The `EnvoyProxy` [Custom Resource][cr] can be + referenced by `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters, + e.g. expose Envoy network endpoints using a `ClusterIP` service instead of a `LoadBalancer` service. +* Traffic Routing- Define how to handle application-level requests to backend services. For example, route all HTTP + requests for "www.example.com" to a backend service running a web server. This configuration is expressed through + [HTTPRoute][hroute] and [TLSRoute][troute] resources that match, filter, and route traffic to a [backend][be]. + Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc] + reference. + +## Components + +Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described +in the [Watching Components Design][wcd]. + +### Provider + +A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve +services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A file provider is on the roadmap +via [Issue #37][]. Other providers can be added in the future as Envoy Gateway use cases are better understood. A +provider is configured at start up through Envoy Gateway's [static configuration](#static-configuration). + +#### Kubernetes Provider + +* Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the + [dynamic configuration](#dynamic-configuration). +* Manages the data plane through Kubernetes API CRUD operations. +* Uses Kubernetes for Service discovery. +* Uses etcd (via Kubernetes API) to persist data. + +#### File Provider + +* Uses a file watcher to watch files in a directory that define the data plane configuration. +* Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`. +* Uses the host's DNS for Service discovery. +* If needed, the local filesystem is used to persist data. + +### Resource Watcher + +The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The +mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes +provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator +as output. + +### Resource Translator + +The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate +Representation (IR). It is responsible for: + +* Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR. +* Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR. + +__Note:__ The Resource Translator is implemented as the `Translator` API type in the `gatewayapi` package. + +### Intermediate Representation (IR) + +The Intermediate Representation defines internal data models that external resources are translated into. This allows +Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR +used as input for the Infra Manager and an xDS IR used as input for the xDS Translator. + +* Infra IR- Used as the internal definition of the managed data plane infrastructure. +* xDS IR- Used as the internal definition of the managed data plane xDS configuration. + +### xDS Translator + +The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server. + +### xDS Server + +The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the Delta xDS Server +Protocol and is responsible for using xDS to configure the data plane. + +### Infra Manager + +The Infra Manager is a provider-specific component responsible for managing the following infrastructure: + +* Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, + Service, etc. resources to run Envoy in a Kubernetes cluster. +* Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require + external integrations with the managed Envoy proxies. For example, [Global Rate Limiting][grl] requires provisioning + and configuring the [Envoy Rate Limit Service][rls] and the [Rate Limit filter][rlf]. Such features are exposed to + users through the [Custom Route Filters][crf] extension. + +The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure. + +## Design Decisions + +* Envoy Gateway consumes one [GatewayClass][gc] by comparing its configured controller name with + `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy + Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve the conflict. + `gatewayclass.spec.parametersRef` refers to the `EnvoyProxy` custom resource for configuring the managed proxy + infrastructure. If unspecified, default configuration parameters are used for the managed proxy infrastructure. +* Envoy Gateway manages [Gateways][gw] that reference its GatewayClass. + * A Gateway resource causes Envoy Gateway to provision managed Envoy proxy infrastructure. + * Envoy Gateway groups Listeners by Port and collapses each group of Listeners into a single Listener if the Listeners + in the group are compatible. Envoy Gateway considers Listeners to be compatible if all the following conditions are + met: + * Either each Listener within the group specifies the “HTTP” Protocol or each Listener within the group specifies + either the “HTTPS” or “TLS” Protocol. + * Each Listener within the group specifies a unique "Hostname". + * As a special case, one Listener within a group may omit "Hostname", in which case this Listener matches when no + other Listener matches. + * Envoy Gateway does __not__ merge listeners across multiple Gateways. +* Envoy Gateway follows Gateway API [guidelines][gwapi_conflicts] to resolve any conflicts. + * A Gateway `listener` corresponds to an Envoy proxy [Listener][listener]. +* An [HTTPRoute][hroute] resource corresponds to an Envoy proxy [Route][route]. + * Each [backendRef][be_ref] corresponds to an Envoy proxy [Cluster][cluster]. +* The goal is to make Envoy Gateway components extensible in the future. See the [roadmap][] for additional details. + +The draft for this document is [here][draft_design]. + +[gw_api]: https://gateway-api.sigs.k8s.io +[gc]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gatewayclass +[gw]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#gateway +[hroute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#httproute +[troute]: https://gateway-api.sigs.k8s.io/concepts/api-overview/#tlsroute +[go_cp]: https://github.com/envoyproxy/go-control-plane +[grl]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting +[rls]: https://github.com/envoyproxy/ratelimit +[rlf]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#envoy-v3-api-msg-extensions-filters-http-ratelimit-v3-ratelimit +[crf]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#filters-optional +[gwapi_conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/#conflicts +[listener]: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listeners#config-listeners +[route]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-route +[be_ref]: https://gateway-api.sigs.k8s.io/v1alpha2/api-types/httproute/#backendrefs-optional +[cluster]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster +[draft_design]: https://docs.google.com/document/d/1riyTPPYuvNzIhBdrAX8dpfxTmcobWZDSYTTB5NeybuY/edit +[cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ +[be]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendObjectReference +[svc]: https://kubernetes.io/docs/concepts/services-networking/service/ +[ wcd ]: ../watching/ +[Issue #37]: https://github.com/envoyproxy/gateway/issues/37 +[roadmap]: ../../contributions/roadmap/ diff --git a/site/content/en/v0.6.0/design/tcp-udp-design.md b/site/content/en/v0.6.0/design/tcp-udp-design.md new file mode 100644 index 00000000000..f517e24feda --- /dev/null +++ b/site/content/en/v0.6.0/design/tcp-udp-design.md @@ -0,0 +1,49 @@ +--- +title: "TCP and UDP Proxy Design " +--- + +Even though most of the use cases for Envoy Gateway are at Layer-7, Envoy Gateway can also work at Layer-4 to proxy TCP +and UDP traffic. This document will explore the options we have when operating Envoy Gateway at Layer-4 and explain the +design decision. + +Envoy can work as a non-transparent proxy or a transparent proxy for both [TCP](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/ip_transparency#arch-overview-ip-transparency-original-src-listener) + and [UDP](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto#envoy-v3-api-msg-extensions-filters-udp-udp-proxy-v3-udpproxyconfig) +, so ideally, Envoy Gateway should also be able to work in these two modes: + +## Non-transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with its own IP address, and proxies the +TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses its own IP address as the sender IP address when +proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see Envoy's IP address and port. + +## Transparent Proxy Mode +For TCP, Envoy terminates the downstream connection, connects the upstream with the downstream IP address, and proxies +the TCP traffic from the downstream to the upstream. + +For UDP, Envoy receives UDP datagrams from the downstream, and uses the downstream IP address as the sender IP address +when proxying the UDP datagrams to the upstream. + +In this mode, the upstream will see the original downstream IP address and Envoy's mac address. + +Note: Even in transparent mode, the upstream can't see the port number of the downstream because Envoy doesn't forward +the port number. + +## The Implications of Transparent Proxy Mode + +### Escalated Privilege +Envoy needs to bind to the downstream IP when connecting to the upstream, which means Envoy requires escalated +CAP_NET_ADMIN privileges. This is often considered as a bad security practice and not allowed in some sensitive deployments. + +### Routing +The upstream can see the original source IP, but the original port number won't be passed, so the return +traffic from the upstream must be routed back to Envoy because only Envoy knows how to send the return traffic back +to the right port number of the downstream, which requires routing at the upstream side to be set up. +In a Kubernetes cluster, Envoy Gateway will have to carefully cooperate with CNI plugins to get the routing right. + +## The Design Decision (For Now) + +The implementation will only support proxying in non-transparent mode i.e. the backend will see the source IP and +port of the deployed Envoy instance instead of the client. diff --git a/site/content/en/v0.6.0/design/tracing.md b/site/content/en/v0.6.0/design/tracing.md new file mode 100644 index 00000000000..66f9454bc32 --- /dev/null +++ b/site/content/en/v0.6.0/design/tracing.md @@ -0,0 +1,161 @@ +--- +title: "Observability: Tracing" +--- + +## Overview + +Envoy supports extensible tracing to different sinks, Zipkin, OpenTelemetry etc. Overview of Envoy tracing can be found [here](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/observability/tracing). + +Envoy Gateway leverages [Gateway API](https://gateway-api.sigs.k8s.io/) for configuring managed Envoy proxies. Gateway API defines core, extended, and implementation-specific API [support levels](https://gateway-api.sigs.k8s.io/concepts/conformance/?h=extended#2-support-levels) for implementers such as Envoy Gateway to expose features. Since tracing is not covered by `Core` or `Extended` APIs, EG should provide an easy to config tracing per `EnvoyProxy`. + +Only OpenTelemetry sink can be configured currently, you can use [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) to export to other tracing backends. + +## Goals + +- Support send tracing to `OpenTelemetry` backend +- Support configurable sampling rate +- Support propagate tag from `Literal`, `Environment` and `Request Header` + +## Non-Goals + +- Support other tracing backend, e.g. `Zipkin`, `Jaeger` + +## Use-Cases + +- Configure accesslog for a `EnvoyProxy` to `File` + +### ProxyAccessLog API Type + +```golang mdox-exec="sed '1,7d' api/config/v1alpha1/tracing_types.go" +type ProxyTracing struct { + // SamplingRate controls the rate at which traffic will be + // selected for tracing if no prior sampling decision has been made. + // Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=100 + // +kubebuilder:default=100 + // +optional + SamplingRate *uint32 `json:"samplingRate,omitempty"` + // CustomTags defines the custom tags to add to each span. + // If provider is kubernetes, pod name and namespace are added by default. + CustomTags map[string]CustomTag `json:"customTags,omitempty"` + // Provider defines the tracing provider. + // Only OpenTelemetry is supported currently. + Provider TracingProvider `json:"provider"` +} + +type TracingProviderType string + +const ( + TracingProviderTypeOpenTelemetry TracingProviderType = "OpenTelemetry" +) + +type TracingProvider struct { + // Type defines the tracing provider type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type TracingProviderType `json:"type"` + // Host define the provider service hostname. + Host string `json:"host"` + // Port defines the port the provider service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +type CustomTagType string + +const ( + // CustomTagTypeLiteral adds hard-coded value to each span. + CustomTagTypeLiteral CustomTagType = "Literal" + // CustomTagTypeEnvironment adds value from environment variable to each span. + CustomTagTypeEnvironment CustomTagType = "Environment" + // CustomTagTypeRequestHeader adds value from request header to each span. + CustomTagTypeRequestHeader CustomTagType = "RequestHeader" +) + +type CustomTag struct { + // Type defines the type of custom tag. + // +kubebuilder:validation:Enum=Literal;Environment;RequestHeader + // +unionDiscriminator + // +kubebuilder:default=Literal + Type CustomTagType `json:"type"` + // Literal adds hard-coded value to each span. + // It's required when the type is "Literal". + Literal *LiteralCustomTag `json:"literal,omitempty"` + // Environment adds value from environment variable to each span. + // It's required when the type is "Environment". + Environment *EnvironmentCustomTag `json:"environment,omitempty"` + // RequestHeader adds value from request header to each span. + // It's required when the type is "RequestHeader". + RequestHeader *RequestHeaderCustomTag `json:"requestHeader,omitempty"` + + // TODO: add support for Metadata tags in the future. + // EG currently doesn't support metadata for route or cluster. +} + +// LiteralCustomTag adds hard-coded value to each span. +type LiteralCustomTag struct { + // Value defines the hard-coded value to add to each span. + Value string `json:"value"` +} + +// EnvironmentCustomTag adds value from environment variable to each span. +type EnvironmentCustomTag struct { + // Name defines the name of the environment variable which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the environment variable is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} + +// RequestHeaderCustomTag adds value from request header to each span. +type RequestHeaderCustomTag struct { + // Name defines the name of the request header which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the request header is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} +``` + +### Example + +1. The following is an example to config tracing. + +```yaml mdox-exec="sed '1,12d' examples/kubernetes/tracing/default.yaml" +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: tracing + namespace: envoy-gateway-system +spec: + telemetry: + tracing: + # sample 100% of requests + samplingRate: 100 + provider: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + customTags: + # This is an example of using a literal as a tag value + key1: + type: Literal + literal: + value: "val1" + # This is an example of using an environment variable as a tag value + env1: + type: Environment + environment: + name: ENV1 + defaultValue: "-" + # This is an example of using a header value as a tag value + header1: + type: RequestHeader + requestHeader: + name: X-Header-1 + defaultValue: "-" +``` diff --git a/site/content/en/v0.6.0/design/watching.md b/site/content/en/v0.6.0/design/watching.md new file mode 100644 index 00000000000..5eabad7b3f9 --- /dev/null +++ b/site/content/en/v0.6.0/design/watching.md @@ -0,0 +1,120 @@ +--- +title: "Watching Components Design" +weight: 3 +--- + +Envoy Gateway is made up of several components that communicate in-process. Some of them (namely Providers) watch +external resources, and "publish" what they see for other components to consume; others watch what another publishes and +act on it (such as the resource translator watches what the providers publish, and then publishes its own results that +are watched by another component). Some of these internally published results are consumed by multiple components. + +To facilitate this communication use the [watchable][] library. The `watchable.Map` type is very similar to the +standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub +pattern. + +## Pub + +Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace" +tuple. And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if +we were using native Go `map`s). For example, a struct that might be written to by the Kubernetes provider, and read by +the IR translator: + + ```go + type ResourceTable struct { + // gateway classes are cluster-scoped; no namespace + GatewayClasses watchable.Map[string, *gwapiv1.GatewayClass] + + // gateways are namespace-scoped, so use a k8s.io/apimachinery/pkg/types.NamespacedName as the map key. + Gateways watchable.Map[types.NamespacedName, *gwapiv1.Gateway] + + HTTPRoutes watchable.Map[types.NamespacedName, *gwapiv1.HTTPRoute] + } + ``` + +The Kubernetes provider updates the table by calling `table.Thing.Store(name, val)` and `table.Thing.Delete(name)`; +updating a map key with a value that is deep-equal (usually `reflect.DeepEqual`, but you can implement your own `.Equal` +method) the current value is a no-op; it won't trigger an event for subscribers. This is handy so that the publisher +doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just +`.Store` its data and `watchable` will do the right thing. + +## Sub + +Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or +`table.Thing.SubscribeSubset` if they only care about a few "Thing"s). So the translator goroutine might look like: + + ```go + func(ctx context.Context) error { + for snapshot := range k8sTable.HTTPRoutes.Subscribe(ctx) { + fullState := irInput{ + GatewayClasses: k8sTable.GatewayClasses.LoadAll(), + Gateways: k8sTable.Gateways.LoadAll(), + HTTPRoutes: snapshot.State, + } + translate(irInput) + } + } + ``` + +Or, to watch multiple maps in the same loop: + + ```go + func worker(ctx context.Context) error { + classCh := k8sTable.GatewayClasses.Subscribe(ctx) + gwCh := k8sTable.Gateways.Subscribe(ctx) + routeCh := k8sTable.HTTPRoutes.Subscribe(ctx) + for ctx.Err() == nil { + var arg irInput + select { + case snapshot := <-classCh: + arg.GatewayClasses = snapshot.State + case snapshot := <-gwCh: + arg.Gateways = snapshot.State + case snapshot := <-routeCh: + arg.Routes = snapshot.State + } + if arg.GateWayClasses == nil { + arg.GatewayClasses = k8sTable.GateWayClasses.LoadAll() + } + if arg.GateWays == nil { + arg.Gateways = k8sTable.GateWays.LoadAll() + } + if arg.HTTPRoutes == nil { + arg.HTTPRoutes = k8sTable.HTTPRoutes.LoadAll() + } + translate(irInput) + } + } + ``` + +From the updates it gets from `.Subscribe`, it can get a full view of the map being subscribed to via `snapshot.State`; +but it must read the other maps explicitly. Like `sync.Map`, `watchable.Map`s are thread-safe; while `.Subscribe` is a +handy way to know when to run, `.Load` and friends can be used without subscribing. + +There can be any number of subscribers. For that matter, there can be any number of publishers `.Store`ing things, but +it's probably wise to just have one publisher for each map. + +The channel returned from `.Subscribe` **is immediately readable** with a snapshot of the map as it existed when +`.Subscribe` was called; and becomes readable again whenever `.Store` or `.Delete` mutates the map. If multiple +mutations happen between reads (or if mutations happen between `.Subscribe` and the first read), they are coalesced in +to one snapshot to be read; the `snapshot.State` is the most-recent full state, and `snapshot.Updates` is a listing of +each of the mutations that cause this snapshot to be different than the last-read one. This way subscribers don't need +to worry about a backlog accumulating if they can't keep up with the rate of changes from the publisher. + +If the map contains anything before `.Subscribe` is called, that very first read won't include `snapshot.Updates` +entries for those pre-existing items; if you are working with `snapshot.Update` instead of `snapshot.State`, then you +must add special handling for your first read. We have a utility function `./internal/message.HandleSubscription` to +help with this. + +## Other Notes + +The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the +map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb +`chan`. + +A limitation of `watchable.Map` is that in order to ensure safety between goroutines, it does require that value types +be deep-copiable; either by having a `DeepCopy` method, being a `proto.Message`, or by containing no reference types and +so can be deep-copied by naive assignment. Fortunately, we're using `controller-gen` anyway, and `controller-gen` can +generate `DeepCopy` methods for us: just stick a `// +k8s:deepcopy-gen=true` on the types that you want it to generate +methods for. + +[watchable]: https://pkg.go.dev/github.com/telepresenceio/watchable diff --git a/site/content/en/v0.6.0/install/_index.md b/site/content/en/v0.6.0/install/_index.md new file mode 100644 index 00000000000..b4c6f79c6fd --- /dev/null +++ b/site/content/en/v0.6.0/install/_index.md @@ -0,0 +1,5 @@ +--- +title: "Installation" +description: This section includes installation related contents of Envoy Gateway. +weight: 70 +--- diff --git a/site/content/en/v0.6.0/install/api.md b/site/content/en/v0.6.0/install/api.md new file mode 100644 index 00000000000..253d528bdfb --- /dev/null +++ b/site/content/en/v0.6.0/install/api.md @@ -0,0 +1,57 @@ ++++ +title = "gateway-helm" ++++ + + +![Version: v0.0.0-latest](https://img.shields.io/badge/Version-v0.0.0--latest-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square) + +The Helm chart for Envoy Gateway + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| envoy-gateway-steering-committee | | | +| envoy-gateway-maintainers | | | + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| certgen.job.annotations | object | `{}` | | +| certgen.job.ttlSecondsAfterFinished | int | `0` | | +| certgen.rbac.annotations | object | `{}` | | +| certgen.rbac.labels | object | `{}` | | +| config.envoyGateway.gateway.controllerName | string | `"gateway.envoyproxy.io/gatewayclass-controller"` | | +| config.envoyGateway.logging.level.default | string | `"info"` | | +| config.envoyGateway.provider.type | string | `"Kubernetes"` | | +| createNamespace | bool | `false` | | +| deployment.envoyGateway.cert.expiryDays | int | `365` | | +| deployment.envoyGateway.image.repository | string | `"${ImageRepository}"` | | +| deployment.envoyGateway.image.tag | string | `"${ImageTag}"` | | +| deployment.envoyGateway.imagePullPolicy | string | `"Always"` | | +| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | +| deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | | +| deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | | +| deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | | +| deployment.pod.annotations | object | `{}` | | +| deployment.pod.labels | object | `{}` | | +| deployment.ports[0].name | string | `"grpc"` | | +| deployment.ports[0].port | int | `18000` | | +| deployment.ports[0].targetPort | int | `18000` | | +| deployment.ports[1].name | string | `"ratelimit"` | | +| deployment.ports[1].port | int | `18001` | | +| deployment.ports[1].targetPort | int | `18001` | | +| deployment.replicas | int | `1` | | +| envoyGatewayMetricsService.ports[0].name | string | `"http"` | | +| envoyGatewayMetricsService.ports[0].port | int | `19001` | | +| envoyGatewayMetricsService.ports[0].protocol | string | `"TCP"` | | +| envoyGatewayMetricsService.ports[0].targetPort | int | `19001` | | +| kubernetesClusterDomain | string | `"cluster.local"` | | + diff --git a/site/content/en/v0.6.0/install/install-egctl.md b/site/content/en/v0.6.0/install/install-egctl.md new file mode 100644 index 00000000000..18a593c445c --- /dev/null +++ b/site/content/en/v0.6.0/install/install-egctl.md @@ -0,0 +1,57 @@ +--- +title: "Install egctl" +weight: -80 +--- + +{{% alert title="What is egctl?" color="primary" %}} + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + +{{% /alert %}} + + +This guide shows how to install the egctl CLI. egctl can be installed either from source, or from pre-built binary releases. + +### From The Envoy Gateway Project + +The Envoy Gateway project provides two ways to fetch and install egctl. These are the official methods to get egctl releases. Installation through those methods can be found below the official methods. + +### From the Binary Releases + +Every [release](https://github.com/envoyproxy/gateway/releases) of egctl provides binary releases for a variety of OSes. These binary versions can be manually downloaded and installed. + +1. Download your [desired version](https://github.com/envoyproxy/gateway/releases) +2. Unpack it (tar -zxvf egctl_latest_linux_amd64.tar.gz) +3. Find the egctl binary in the unpacked directory, and move it to its desired destination (mv bin/linux/amd64/egctl /usr/local/bin/egctl) + +From there, you should be able to run: `egctl help`. + +### From Script + +`egctl` now has an installer script that will automatically grab the latest release version of egctl and install it locally. + +You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. + +```shell +curl -fsSL -o get-egctl.sh https://gateway.envoyproxy.io/get-egctl.sh + +chmod +x get-egctl.sh + +# get help info of the +bash get-egctl.sh --help + +# install the latest development version of egctl +bash VERSION=latest get-egctl.sh +``` + +Yes, you can just use the below command if you want to live on the edge. + +```shell +curl -fsSL https://gateway.envoyproxy.io/get-egctl.sh | VERSION=latest bash +``` + +{{% alert title="Next Steps" color="warning" %}} + +You can refer to [User Guides](/latest/user/egctl) to more details about egctl. + +{{% /alert %}} diff --git a/site/content/en/v0.6.0/install/install-helm.md b/site/content/en/v0.6.0/install/install-helm.md new file mode 100644 index 00000000000..3f3c57e1db9 --- /dev/null +++ b/site/content/en/v0.6.0/install/install-helm.md @@ -0,0 +1,144 @@ ++++ +title = "Install with Helm" +weight = -100 ++++ + +[Helm](https://helm.sh) is a package manager for Kubernetes that automates the release and management of software on Kubernetes. + +Envoy Gateway can be installed via a Helm chart with a few simple steps, depending on if you are deploying for the first time, upgrading Envoy Gateway from an existing installation, or migrating from Envoy Gateway. + +## Before you begin + +{{% alert title="Compatibility Matrix" color="warning" %}} +Refer to the [Version Compatibility Matrix](/blog/2022/10/01/versions/) to learn more. +{{% /alert %}} + +The Envoy Gateway Helm chart is hosted by DockerHub. + +It is published at `oci://docker.io/envoyproxy/gateway-helm`. + +{{% alert title="Note" color="primary" %}} +We use `v0.0.0-latest` as the latest development version. + +You can visit [Envoy Gateway Helm Chart](https://hub.docker.com/r/envoyproxy/gateway-helm/tags) for more releases. +{{% /alert %}} + +## Install with Helm + +Envoy Gateway is typically deployed to Kubernetes from the command line. If you don't have Kubernetes, you should use `kind` to create one. + +{{% alert title="Developer Guide" color="primary" %}} +Refer to the [Developer Guide](/latest/contributions/develop) to learn more. +{{% /alert %}} + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml + +## Helm chart customizations + +Some of the quick ways of using the helm install command for envoy gateway installation are below. + +### Increase the replicas + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set deployment.replicas=2 +``` + +### Change the kubernetesClusterDomain name + +If you have installed your cluster with different domain name you can use below command. + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace --set kubernetesClusterDomain= +``` + +**Note**: Above are some of the ways we can directly use for customization of our installation. But if you are looking for more complex changes [values.yaml](https://helm.sh/docs/chart_template_guide/values_files/) comes to rescue. + +### Using values.yaml file for complex installation + +```yaml +deployment: + envoyGateway: + resources: + limits: + cpu: 700m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + ports: + - name: grpc + port: 18005 + targetPort: 18000 + - name: ratelimit + port: 18006 + targetPort: 18001 + +config: + envoyGateway: + logging: + level: + default: debug +``` + +Here we have made three changes to our values.yaml file. Increase the resources limit for cpu to `700m`, changed the port for grpc to `18005` and for ratelimit to `18006` and also updated the logging level to `debug`. + +You can use the below command to install the envoy gateway using values.yaml file. + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace -f values.yaml +``` + +{{% alert title="Helm Chart Values" color="primary" %}} +If you want to know all the available fields inside the values.yaml file, please see the [Helm Chart Values](../api). +{{% /alert %}} + +## Open Ports + +These are the ports used by Envoy Gateway and the managed Envoy Proxy. + +### Envoy Gateway + +| Envoy Gateway | Address | Port | Configurable | +|:----------------------:|:---------:|:------:| :------: | +| Xds EnvoyProxy Server | 0.0.0.0 | 18000 | No | +| Xds RateLimit Server | 0.0.0.0 | 18001 | No | +| Admin Server | 127.0.0.1 | 19000 | Yes | +| Metrics Server | 0.0.0.0 | 19001 | No | +| Health Check | 127.0.0.1 | 8081 | No | + +### EnvoyProxy + +| Envoy Proxy | Address | Port | +|:---------------------------------:|:-----------:| :-----: | +| Admin Server | 127.0.0.1 | 19000 | +| Heath Check | 0.0.0.0 | 19001 | + +{{% alert title="Next Steps" color="warning" %}} +Envoy Gateway should now be successfully installed and running, but in order to experience more abilities of Envoy Gateway, you can refer to [User Guides](../../user). +{{% /alert %}} diff --git a/site/content/en/v0.6.0/install/install-yaml.md b/site/content/en/v0.6.0/install/install-yaml.md new file mode 100644 index 00000000000..4b13529f000 --- /dev/null +++ b/site/content/en/v0.6.0/install/install-yaml.md @@ -0,0 +1,39 @@ ++++ +title = "Install with Kubernetes YAML" +weight = -99 ++++ + +In this guide, we'll walk you through installing Envoy Gateway in your Kubernetes cluster. + +The manual install process does not allow for as much control over configuration +as the [Helm install method](../install-helm), so if you need more control over your Envoy Gateway +installation, it is recommended that you use helm. + +## Before you begin + +Envoy Gateway is designed to run in Kubernetes for production. The most essential requirements are: + +* Kubernetes 1.25 or later +* The `kubectl` command-line tool + +{{% alert title="Compatibility Matrix" color="warning" %}} +Refer to the [Version Compatibility Matrix](/blog/2022/10/01/versions/) to learn more. +{{% /alert %}} + +## Install with YAML + +Envoy Gateway is typically deployed to Kubernetes from the command line. If you don't have Kubernetes, you should use `kind` to create one. + +{{% alert title="Developer Guide" color="primary" %}} +Refer to the [Developer Guide](/latest/contributions/develop) to learn more. +{{% /alert %}} + +1. In your terminal, run the following command: + + ```shell + kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/install.yaml + ``` + +2. Next Steps + + Envoy Gateway should now be successfully installed and running, but in order to experience more abilities of Envoy Gateway, you can refer to [User Guides](/latest/user). diff --git a/site/content/en/v0.6.0/user/_index.md b/site/content/en/v0.6.0/user/_index.md new file mode 100644 index 00000000000..e413578a6ca --- /dev/null +++ b/site/content/en/v0.6.0/user/_index.md @@ -0,0 +1,5 @@ +--- +title: "User Guides" +weight: 2 +description: This section includes User Guides of Envoy Gateway. +--- diff --git a/site/content/en/v0.6.0/user/cors.md b/site/content/en/v0.6.0/user/cors.md new file mode 100644 index 00000000000..d8867ccb8d2 --- /dev/null +++ b/site/content/en/v0.6.0/user/cors.md @@ -0,0 +1,121 @@ +--- +title: "CORS" +--- + +This guide provides instructions for configuring [Cross-Origin Resource Sharing (CORS)][cors] on Envoy Gateway. +CORS defines a way for client web applications that are loaded in one domain to interact with resources in a different +domain. + +Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure CORS. +This instantiated resource can be linked to a [Gateway][Gateway], [HTTPRoute][HTTPRoute] or [GRPCRoute][GRPCRoute] resource. + +## Prerequisites + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Configuration + +The below example defines a SecurityPolicy that allows CORS requests from `www.foo.com`. + +```shell +cat < /dev/null +``` + +You should see the below response, indicating that the request from `http://www.foo.com` is allowed: + +```shell +< access-control-allow-origin: http://www.foo.com +< access-control-allow-methods: GET, POST +< access-control-allow-headers: x-header-1, x-header-2 +< access-control-max-age: 86400 +< access-control-expose-headers: x-header-3, x-header-4 +``` + +If you try to send a request from `http://www.bar.com`, you should see the below response: + +```shell +curl -H "Origin: http://www.bar.com" \ + -H "Host: www.example.com" \ + -H "Access-Control-Request-Method: GET" \ + -X OPTIONS -v -s \ + http://$GATEWAY_HOST \ + 1> /dev/null +``` + +You won't see any CORS headers in the response, indicating that the request from `http://www.bar.com` was not allowed. + +Note: CORS specification requires that the browsers to send a preflight request to the server to ask if it's allowed +to access the limited resource in another domains. The browsers are supposed to follow the response from the server to +determine whether to send the actual request or not. The CORS filter only response to the preflight requests according to +its configuration. It won't deny any requests. The browsers are responsible for enforcing the CORS policy. + + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the SecurityPolicy: + +```shell +kubectl delete securitypolicy/cors-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[SecurityPolicy]: ../../design/security-policy/ +[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/api-types/grpcroute diff --git a/site/content/en/v0.6.0/user/customize-envoyproxy.md b/site/content/en/v0.6.0/user/customize-envoyproxy.md new file mode 100644 index 00000000000..356a3d7b089 --- /dev/null +++ b/site/content/en/v0.6.0/user/customize-envoyproxy.md @@ -0,0 +1,322 @@ +--- +title: "Customize EnvoyProxy" +--- + +Envoy Gateway provides an [EnvoyProxy][] CRD that can be linked to the ParametersRef +in GatewayClass, allowing cluster admins to customize the managed EnvoyProxy Deployment and +Service. To learn more about GatewayClass and ParametersRef, please refer to [Gateway API documentation][]. + +## Installation + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Add GatewayClass ParametersRef + +First, you need to add ParametersRef in GatewayClass, and refer to EnvoyProxy Config: + +```shell +cat < Envoy Gateway has provided two initial `env` `ENVOY_GATEWAY_NAMESPACE` and `ENVOY_POD_NAME` for envoyproxy container. + +After applying the config, you can get the envoyproxy deployment, and see resources has been changed. + +## Customize EnvoyProxy Deployment Volumes or VolumeMounts + +You can customize the EnvoyProxy Deployment Volumes or VolumeMounts via EnvoyProxy Config like: + +```shell +cat < GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8888 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:19:42 GMT +< content-length: 521 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.marketing.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.157" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "c637977c-458a-48ae-92b3-f8c429849322" + ] + }, + "namespace": "marketing", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-bcs8f" +* Connection #0 to host localhost left intact +``` + +* Lets deploy Envoy Gateway in the `product` namespace and also watch resources only in this namespace. + +``` +helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller --set config.envoyGateway.provider.kubernetes.watch.namespaces={product} eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.6.0 -n product --create-namespace +``` + +Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. + +```shell +cat < GET /get HTTP/1.1 +> Host: www.product.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Thu, 20 Apr 2023 19:20:17 GMT +< content-length: 517 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "www.product.example.com", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.86.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Internal": [ + "true" + ], + "X-Forwarded-For": [ + "10.1.0.156" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39196453-2250-4331-b756-54003b2853c2" + ] + }, + "namespace": "product", + "ingress": "", + "service": "", + "pod": "backend-74888f465f-64fjs" +* Connection #0 to host localhost left intact +``` + +With the below command you can ensure that you are not able to access the marketing team's backend exposed using the `www.marketing.example.com` hostname +and the product team's data plane. + +```shell +curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get +``` + +``` +* Trying 127.0.0.1:8889... +* Connected to localhost (127.0.0.1) port 8889 (#0) +> GET /get HTTP/1.1 +> Host: www.marketing.example.com +> User-Agent: curl/7.86.0 +> Accept: */* +> +Handling connection for 8889 +* Mark bundle as not supporting multiuse +< HTTP/1.1 404 Not Found +< date: Thu, 20 Apr 2023 19:22:13 GMT +< server: envoy +< content-length: 0 +< +* Connection #0 to host localhost left intact +``` diff --git a/site/content/en/v0.6.0/user/egctl.md b/site/content/en/v0.6.0/user/egctl.md new file mode 100644 index 00000000000..937c783cc02 --- /dev/null +++ b/site/content/en/v0.6.0/user/egctl.md @@ -0,0 +1,742 @@ +--- +title: "Use egctl" +--- + +`egctl` is a command line tool to provide additional functionality for Envoy Gateway users. + + + +## egctl experimental translate + +This subcommand allows users to translate from an input configuration type to an output configuration type. + +In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS +resources. + +```shell +cat <// + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/local_reply_config" + value: + mappers: + - filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + runtime_key: key_b + status_code: 406 + body: + inline_string: "could not find what you are looking for" +EOF +``` + +* Lets edit the HTTPRoute resource from the Quickstart to only match on paths with value `/get` + +``` +kubectl patch httproute backend --type=json --patch '[{ + "op": "add", + "path": "/spec/rules/0/matches/0/path/value", + "value": "/get", +}]' +``` + +* Lets test it out by specifying a path apart from `/get` + +``` +$ curl --header "Host: www.example.com" http://localhost:8888/find +Handling connection for 8888 +could not find what you are looking for +``` + +## Debugging + +### Runtime + +* The `Status` subresource should have information about the status of the resource. Make sure +`Accepted=True` and `Programmed=True` conditions are set to ensure that the policy has been +applied to Envoy Proxy. + +``` +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"gateway.envoyproxy.io/v1alpha1","kind":"EnvoyPatchPolicy","metadata":{"annotations":{},"name":"custom-response-patch-policy","namespace":"default"},"spec":{"jsonPatches":[{"name":"default/eg/http","operation":{"op":"add","path":"/default_filter_chain/filters/0/typed_config/local_reply_config","value":{"mappers":[{"body":{"inline_string":"could not find what you are looking for"},"filter":{"status_code_filter":{"comparison":{"op":"EQ","value":{"default_value":404}}}}}]}},"type":"type.googleapis.com/envoy.config.listener.v3.Listener"}],"priority":0,"targetRef":{"group":"gateway.networking.k8s.io","kind":"Gateway","name":"eg","namespace":"default"},"type":"JSONPatch"}} + creationTimestamp: "2023-07-31T21:47:53Z" + generation: 1 + name: custom-response-patch-policy + namespace: default + resourceVersion: "10265" + uid: a35bda6e-a0cc-46d7-a63a-cee765174bc3 +spec: + jsonPatches: + - name: default/eg/http + operation: + op: add + path: /default_filter_chain/filters/0/typed_config/local_reply_config + value: + mappers: + - body: + inline_string: could not find what you are looking for + filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + type: type.googleapis.com/envoy.config.listener.v3.Listener + priority: 0 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch +status: + conditions: + - lastTransitionTime: "2023-07-31T21:48:19Z" + message: EnvoyPatchPolicy has been accepted. + observedGeneration: 1 + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2023-07-31T21:48:19Z" + message: successfully applied patches. + reason: Programmed + status: "True" + type: Programmed +``` + +### Offline + +* You can use [egctl x translate][] to validate the translated xds output. + +## Caveats + +This API will always be an unstable API and the same outcome cannot be garunteed +across versions for these reasons +* The Envoy Proxy API might deprecate and remove API fields +* Envoy Gateway might alter the xDS translation creating a different xDS output +such as changing the `name` field of resources. + +[EnvoyPatchPolicy]: ../../api/extension_types#envoypatchpolicy +[EnvoyGateway]: ../../api/extension_types#envoygateway +[JSON Patch]: https://datatracker.ietf.org/doc/html/rfc6902 +[xDS]: https://www.envoyproxy.io/docs/envoy/v0.6.0/intro/arch_overview/operations/dynamic_configuration +[Local Reply Modification]: https://www.envoyproxy.io/docs/envoy/v0.6.0/configuration/http/http_conn_man/local_reply +[egctl x translate]: ../egctl#egctl-experimental-translate diff --git a/site/content/en/v0.6.0/user/gateway-address.md b/site/content/en/v0.6.0/user/gateway-address.md new file mode 100644 index 00000000000..8f2458327a5 --- /dev/null +++ b/site/content/en/v0.6.0/user/gateway-address.md @@ -0,0 +1,69 @@ +--- +title: "Gateway Address" +--- + +The Gateway API provides an optional [Addresses][] field through which Envoy Gateway can set addresses for Envoy Proxy Service. The currently supported addresses are: + +- [External IPs](#External-IPs) + +## Installation + +Install Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.6.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## External IPs + +Using the addresses in `Gateway.Spec.Addresses` as the [External IPs][] of Envoy Proxy Service, this will __require__ the address to be of type `IPAddress`. + +Install the GatewayClass, Gateway from quickstart: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.6.0/quickstart.yaml -n default +``` + +Set the address of the Gateway, the address settings here are for reference only: + +```shell +kubectl patch gateway eg --type=json --patch '[{ + "op": "add", + "path": "/spec/addresses", + "value": [{ + "type": "IPAddress", + "value": "1.2.3.4" + }] +}]' +``` + +Verify the Gateway status: + +```shell +kubectl get gateway + +NAME CLASS ADDRESS PROGRAMMED AGE +eg eg 1.2.3.4 True 14m +``` + +Verify the Envoy Proxy Service status: + +```shell +kubectl get service -n envoy-gateway-system + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +envoy-default-eg-64656661 LoadBalancer 10.96.236.219 1.2.3.4 80:31017/TCP 15m +envoy-gateway ClusterIP 10.96.192.76 18000/TCP 15m +envoy-gateway-metrics-service ClusterIP 10.96.124.73 8443/TCP 15m +``` + +__Note:__ If the `Gateway.Spec.Addresses` is explicitly set, it will be the only addresses that populates the Gateway status. + +[Addresses]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.GatewayAddress +[External IPs]: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips diff --git a/site/content/en/v0.6.0/user/gateway-api-metrics.md b/site/content/en/v0.6.0/user/gateway-api-metrics.md new file mode 100644 index 00000000000..fef51bde69f --- /dev/null +++ b/site/content/en/v0.6.0/user/gateway-api-metrics.md @@ -0,0 +1,54 @@ +--- +title: "Gateway API Metrics" +--- + +Resource metrics for Gateway API objects are available using the [Gateway API State Metrics](https://github.com/Kuadrant/gateway-api-state-metrics) project. +The project also provides example dashboard for visualising the metrics using Grafana, and example alerts using Prometheus & Alertmanager. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +Run the following commands to install the metrics stack, with the Gateway API State Metrics configuration, on your kubernetes cluster: + +```shell +kubectl apply --server-side -f https://raw.githubusercontent.com/Kuadrant/gateway-api-state-metrics/main/config/examples/kube-prometheus/bundle_crd.yaml +kubectl apply -f https://raw.githubusercontent.com/Kuadrant/gateway-api-state-metrics/main/config/examples/kube-prometheus/bundle.yaml +``` + +## Metrics and Alerts + +To access the Prometheus UI, wait for the statefulset to be ready, then use the port-forward command: + +```shell +# This first command may fail if the statefulset has not been created yet. +# In that case, try again until you get a message like 'Waiting for 2 pods to be ready...' +# or 'statefulset rolling update complete 2 pods...' +kubectl -n monitoring rollout status --watch --timeout=5m statefulset/prometheus-k8s +kubectl -n monitoring port-forward service/prometheus-k8s 9090:9090 > /dev/null & +``` + +Navigate to [http://localhost:9090](http://localhost:9090). +Metrics can be queried from the 'Graph' tab e.g. `gatewayapi_gateway_created` +See the [Gateway API State Metrics README](https://github.com/Kuadrant/gateway-api-state-metrics/tree/main#metrics) for the full list of Gateway API metrics available. + +Alerts can be seen in the 'Alerts' tab. +Gateway API specific alerts will be grouped under the 'gateway-api.rules' heading. + +***Note:*** Alerts are defined in a PrometheusRules custom resource in the 'monitoring' namespace. You can modify the alert rules by updating this resource. + +## Dashboards + +To view the dashboards in Grafana, wait for the deployment to be ready, then use the port-forward command: + +```shell +kubectl -n monitoring wait --timeout=5m deployment/grafana --for=condition=Available +kubectl -n monitoring port-forward service/grafana 3000:3000 > /dev/null & +``` + +Navigate to [http://localhost:3000](http://localhost:3000) and sign in with admin/admin. +The Gateway API State dashboards will be available in the 'Default' folder and tagged with 'gateway-api'. +See the [Gateway API State Metrics README](https://github.com/Kuadrant/gateway-api-state-metrics/tree/main#dashboards) for further information on available dashboards. + +***Note:*** Dashboards are loaded from configmaps. You can modify the dashboards in the Grafana UI, however you will need to export them from the UI and update the json in the configmaps to persist changes. diff --git a/site/content/en/v0.6.0/user/gatewayapi-support.md b/site/content/en/v0.6.0/user/gatewayapi-support.md new file mode 100644 index 00000000000..064bb5b2b1b --- /dev/null +++ b/site/content/en/v0.6.0/user/gatewayapi-support.md @@ -0,0 +1,119 @@ +--- +title: "Gateway API Support" +--- + +As mentioned in the [system design][] document, Envoy Gateway's managed data plane is configured dynamically through +Kubernetes resources, primarily [Gateway API][] objects. Envoy Gateway supports configuration using the following Gateway API resources. + +## GatewayClass + +A [GatewayClass][] represents a "class" of gateways, i.e. which Gateways should be managed by Envoy Gateway. +Envoy Gateway supports managing __a single__ GatewayClass resource that matches its configured `controllerName` and +follows Gateway API guidelines for [resolving conflicts][] when multiple GatewayClasses exist with a matching +`controllerName`. + +__Note:__ If specifying GatewayClass [parameters reference][], it must refer to an [EnvoyProxy][] resource. + +## Gateway + +When a [Gateway][] resource is created that references the managed GatewayClass, Envoy Gateway will create and manage a +new Envoy Proxy deployment. Gateway API resources that reference this Gateway will configure this managed Envoy Proxy +deployment. + +## HTTPRoute + +An [HTTPRoute][] configures routing of HTTP traffic through one or more Gateways. The following HTTPRoute filters are +supported by Envoy Gateway: + +- `requestHeaderModifier`: [RequestHeaderModifiers][http-filter] + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers][http-filter] + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors][http-filter] + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. +- `requestRedirect`: [RequestRedirects][http-filter] + configure policied for how requests that match the HTTPRoute should be modified and then redirected. +- `urlRewrite`: [UrlRewrites][http-filter] + allow for modification of the request's hostname and path before it is proxied to its destination. +- `extensionRef`: [ExtensionRefs][] are used by Envoy Gateway to implement extended filters. Currently, Envoy Gateway + supports rate limiting and request authentication filters. For more information about these filters, refer to the + [rate limiting][] and [request authentication][] documentation. + +__Notes:__ +- The only [BackendRef][] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other destinations such + as arbitrary URLs is not possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TCPRoute + +A [TCPRoute][] configures routing of raw TCP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a TCP port number. + +__Note:__ A TCPRoute only supports proxying in non-transparent mode, i.e. the backend will see the source IP and port of +the Envoy Proxy instance instead of the client. + +## UDPRoute + +A [UDPRoute][] configures routing of raw UDP traffic through one or more Gateways. Traffic can be forwarded to the +desired BackendRefs based on a UDP port number. + +__Note:__ Similar to TCPRoutes, UDPRoutes only support proxying in non-transparent mode i.e. the backend will see the +source IP and port of the Envoy Proxy instance instead of the client. + +## GRPCRoute + +A [GRPCRoute][] configures routing of [gRPC][] requests through one or more Gateways. They offer request matching by +hostname, gRPC service, gRPC method, or HTTP/2 Header. Envoy Gateway supports the following filters on GRPCRoutes to +provide additional traffic processing: + +- `requestHeaderModifier`: [RequestHeaderModifiers][grpc-filter] + can be used to modify or add request headers before the request is proxied to its destination. +- `responseHeaderModifier`: [ResponseHeaderModifiers][grpc-filter] + can be used to modify or add response headers before the response is sent back to the client. +- `requestMirror`: [RequestMirrors][grpc-filter] + configure destinations where the requests should also be mirrored to. Responses to mirrored requests will be ignored. + +__Notes:__ +- The only [BackendRef][grpc-filter] kind supported by Envoy Gateway is a [Service][]. Routing traffic to other + destinations such as arbitrary URLs is not currently possible. +- The `filters` field within [HTTPBackendRef][] is not supported. + +## TLSRoute + +A [TLSRoute][] configures routing of TCP traffic through one or more Gateways. However, unlike TCPRoutes, TLSRoutes +can match against TLS-specific metadata. + +## ReferenceGrant + +A [ReferenceGrant][] is used to allow a resource to reference another resource in a different namespace. Normally an +HTTPRoute created in namespace `foo` is not allowed to reference a Service in namespace `bar`. A ReferenceGrant permits +these types of cross-namespace references. Envoy Gateway supports the following ReferenceGrant use-cases: + +- Allowing an HTTPRoute, GRPCRoute, TLSRoute, UDPRoute, or TCPRoute to reference a Service in a different namespace. +- Allowing an HTTPRoute's `requestMirror` filter to include a BackendRef that references a Service in a different + namespace. +- Allowing a Gateway's [SecretObjectReference][] to reference a secret in a different namespace. + +[system design]: ../../design/system-design/ +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[GatewayClass]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.GatewayClass +[parameters reference]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParametersReference +[Gateway]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRoute +[Service]: https://kubernetes.io/docs/concepts/services-networking/service/ +[BackendRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendRef +[HTTPBackendRef]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPBackendRef +[TCPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute +[UDPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute +[gRPC]: https://grpc.io/ +[TLSRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute +[ReferenceGrant]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.ReferenceGrant +[SecretObjectReference]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.SecretObjectReference +[rate limiting]: ../rate-limit/ +[request authentication]: ../jwt-authentication/ +[EnvoyProxy]: ../../api/extension_types#envoyproxy +[resolving conflicts]: https://gateway-api.sigs.k8s.io/concepts/guidelines/?h=conflict#conflicts +[ExtensionRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilterType +[grpc-filter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteFilter +[http-filter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter diff --git a/site/content/en/v0.6.0/user/grafana-integration.md b/site/content/en/v0.6.0/user/grafana-integration.md new file mode 100644 index 00000000000..d8acf06f669 --- /dev/null +++ b/site/content/en/v0.6.0/user/grafana-integration.md @@ -0,0 +1,65 @@ +--- +title: "Visualising metrics using Grafana" +--- + +Envoy Gateway provides support for exposing Envoy Proxy metrics to a Prometheus instance. +This guide shows you how to visualise the metrics exposed to prometheus using grafana. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +Follow the steps from the [Proxy Observability](../proxy-observability#Metrics) to enable prometheus metrics. + +[Prometheus](https://prometheus.io) is used to scrape metrics from the Envoy Proxy instances. Install Prometheus: + +```shell +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +helm upgrade --install prometheus prometheus-community/prometheus -n monitoring --create-namespace +``` + +[Grafana](https://grafana.com/grafana/) is used to visualise the metrics exposed by the envoy proxy instances. +Install Grafana: + +```shell +helm repo add grafana https://grafana.github.io/helm-charts +helm repo update +helm upgrade --install grafana grafana/grafana -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/grafana/helm-values.yaml -n monitoring --create-namespace +``` + +Expose endpoints: + +```shell +GRAFANA_IP=$(kubectl get svc grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +## Connecting Grafana with Prometheus datasource + +To visualise metrics from Prometheus, we have to connect Grafana with Prometheus. If you installed Grafana from the command +from prerequisites sections, the prometheus datasource should be already configured. + +You can also add the data source manually by following the instructions from [Grafana Docs](https://grafana.com/docs/grafana/v0.6.0/datasources/prometheus/configure-prometheus-data-source/). + +## Accessing Grafana + +You can access the Grafana instance by visiting `http://{GRAFANA_IP}`, derived in prerequisites. + +To log in to Grafana, use the credentials `admin:admin`. + +Envoy Gateway has examples of dashboard for you to get started: + +### [Envoy Global](https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/grafana/dashboards/envoy-global.json) + +![Envoy Global](/img/envoy-global-dashboard.png) + +### [Envoy Clusters](https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/grafana/dashboards/envoy-clusters.json) + +![Envoy Clusters](/img/envoy-clusters-dashboard.png) + +### [Envoy Pod Resources](https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/grafana/dashboards/envoy-pod-resource.json) + +![Envoy Pod Resources](/img/envoy-pod-resources-dashboard.png) + +You can load the above dashboards in your Grafana to get started. Please refer to Grafana docs for [importing dashboards](https://grafana.com/docs/grafana/v0.6.0/dashboards/manage-dashboards/#import-a-dashboard). diff --git a/site/content/en/v0.6.0/user/grpc-routing.md b/site/content/en/v0.6.0/user/grpc-routing.md new file mode 100644 index 00000000000..e365386036e --- /dev/null +++ b/site/content/en/v0.6.0/user/grpc-routing.md @@ -0,0 +1,204 @@ +--- +title: "GRPC Routing" +--- + +The [GRPCRoute][] resource allows users to configure gRPC routing by matching HTTP/2 traffic and forwarding it to backend gRPC servers. +To learn more about gRPC routing, refer to the [Gateway API documentation][]. + +## Prerequisites + +Install Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.6.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +## Installation + +Install the gRPC routing example resources: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/grpc-routing.yaml +``` + +The manifest installs a [GatewayClass][], [Gateway][], a Deployment, a Service, and a GRPCRoute resource. +The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. + +__Note:__ Envoy Gateway is configured by default to manage a GatewayClass with +`controllerName: gateway.envoyproxy.io/gatewayclass-controller`. + +## Verification + +Check the status of the GatewayClass: + +```shell +kubectl get gc --selector=example=grpc-routing +``` + +The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass. + +A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is +provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this +Gateway. Check the status of the Gateway: + +```shell +kubectl get gateways --selector=example=grpc-routing +``` + +The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also +provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend +services. + +Check the status of the GRPCRoute: + +```shell +kubectl get grpcroutes --selector=example=grpc-routing -o yaml +``` + +The status for the GRPCRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway. +The `example-route` matches any traffic for "grpc-example.com" and forwards it to the "yages" Service. + +## Testing the Configuration + +Before testing GRPC routing to the `yages` backend, get the Gateway's address. + +```shell +export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}') +``` + +Test GRPC routing to the `yages` backend using the [grpcurl][] command. + +```shell +grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +{ + "text": "pong" +} +``` + +Envoy Gateway also supports [gRPC-Web][] requests for this configuration. The below `curl` command can be used to send a grpc-Web request with over HTTP/2. You should receive the same response seen in the previous command. + +The data in the body `AAAAAAA=` is a base64 encoded representation of an empty message (data length 0) that the Ping RPC accepts. + +```shell +curl --http2-prior-knowledge -s ${GATEWAY_HOST}:80/yages.Echo/Ping -H 'Host: grpc-example.com' -H 'Content-Type: application/grpc-web-text' -H 'Accept: application/grpc-web-text' -XPOST -d'AAAAAAA=' | base64 -d +``` + +## GRPCRoute Match +The `matches` field can be used to restrict the route to a specific set of requests based on GRPC's service and/or method names. +It supports two match types: `Exact` and `RegularExpression`. + +### Exact + +`Exact` match is the default match type. + +The following example shows how to match a request based on the service and method names for `grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo`, +as well as a match for all services with a method name `Ping` which matches `yages.Echo/Ping` in our deployment. + +```shell +cat < HTTPS + +Listeners expose the TLS setting on a per domain or subdomain basis. TLS settings of a listener are applied to all domains that satisfy the hostname criteria. + +Create a root certificate and private key to sign certificates: + +```shell +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/CN=example.com' -keyout CA.key -out CA.crt +openssl req -out example.com.csr -newkey rsa:2048 -nodes -keyout tls.key -subj "/CN=example.com" +``` + +Generate a self-signed wildcard certificate for `example.com` with `*.example.com` extension + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something", + "foo" + ], +... +``` + +## Setting Request Headers + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + "headers": { + "Accept": [ + "*/*" + ], + "Set-Header": [ + "foo" + ], +... +``` + +## Removing Request Headers + +Headers can be removed from a request by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the request does not have the header configured by the filter, then it +will be added, but unlike [adding request headers](#adding-request-headers) which will append the value of the header if +the request already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "Add-Header": [ + "something" + ], +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +Check the logs of the pods and you will see that the original deployment and the new deployment each got a request: + +```shell +$ kubectl logs deploy/backend && kubectl logs deploy/backend-2 +... +Starting server, listening on port 3000 (http) +Echoing back request made to /get to client (10.42.0.10:41566) +Starting server, listening on port 3000 (http) +Echoing back request made to /get to client (10.42.0.10:45096) +``` + +## Multiple BackendRefs + +When an `HTTPRoute` has multiple `backendRefs` and an `HTTPRequestMirrorFilter`, traffic splitting will still behave the same as it normally would for the main `backendRefs` while the `backendRef` of the `HTTPRequestMirrorFilter` will continue receiving mirrored copies of the incoming requests. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: X-Foo: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< x-foo: value1 +< add-header: foo +< +... + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "X-Foo: value1" + ] +... +``` + +## Setting Response Headers + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: set-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< set-header: foo +< + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "set-header": value1" + ] +... +``` + +## Removing Response Headers + +Headers can be removed from a response by simply supplying a list of header names. + +Setting headers is similar to adding headers. If the response does not have the header configured by the filter, then it +will be added, but unlike [adding response headers](#adding-response-headers) which will append the value of the header +if the response already contains it, setting a header will cause the value to be replaced by the value configured in the +filter. + +```shell +cat < GET /get HTTP/1.1 +> Host: headers.example +> User-Agent: curl/7.81.0 +> Accept: */* +> X-Echo-Set-Header: remove-header: value1 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< + + "headers": { + "Accept": [ + "*/*" + ], + "X-Echo-Set-Header": [ + "remove-header": value1" + ] +... +``` + +## Combining Filters + +Headers can be added/set/removed in a single filter on the same HTTPRoute and they will all perform as expected + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-79665566f5-s589f" +... +``` + +## Multiple backendRefs + +If multiple backendRefs are configured, then traffic will be split between the backendRefs equally unless a weight is +configured. + +First, create a second instance of the example app from the quickstart: + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> add-header: something +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< content-length: 474 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +... + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-75bcd4c969-lsxpz" +... +``` + +## Weighted backendRefs + +If multiple backendRefs are configured and an un-even traffic split between the backends is desired, then the `weight` +field can be used to control the weight of requests to each backend. If weight is not configured for a backendRef it is +assumed to be `1`. + +The [weight field in a backendRef][backendRefs] controls the distribution of the traffic split. The proportion of +requests to a single backendRef is calculated by dividing its `weight` by the sum of all backendRef weights in the +HTTPRoute. The weight is not a percentage and the sum of all weights does not need to add up to 100. + +The HTTPRoute below will configure the gateway to send 80% of the traffic to the backend service, and 20% to the +backend-2 service. + +```shell +cat < GET /get HTTP/1.1 +> Host: backends.example +> User-Agent: curl/7.81.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< server: envoy +< content-length: 0 +< +``` + +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/ +[backendRefs]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.BackendRef diff --git a/site/content/en/v0.6.0/user/http-urlrewrite.md b/site/content/en/v0.6.0/user/http-urlrewrite.md new file mode 100644 index 00000000000..945a24a7a44 --- /dev/null +++ b/site/content/en/v0.6.0/user/http-urlrewrite.md @@ -0,0 +1,297 @@ +--- +title: "HTTP URL Rewrite" +--- + +[HTTPURLRewriteFilter][] defines a filter that modifies a request during forwarding. At most one of these filters may be +used on a Route rule. This MUST NOT be used on the same Route rule as a HTTPRequestRedirect filter. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +## Rewrite URL Prefix Path + +You can configure to rewrite the prefix in the url like below. In this example, any curls to +`http://${GATEWAY_HOST}/get/xxx` will be rewritten to `http://${GATEWAY_HOST}/replace/xxx`. + +```shell +cat < GET /get/origin/path HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> + +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:03:28 GMT +< content-length: 503 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/replace/origin/path", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "fd84b842-9937-4fb5-83c7-61470d854b90" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path`, but the actual path is `/replace/origin/path`. + +## Rewrite URL Full Path + +You can configure to rewrite the fullpath in the url like below. In this example, any request sent to +`http://${GATEWAY_HOST}/get/origin/path/xxxx` will be rewritten to +`http://${GATEWAY_HOST}/force/replace/fullpath`. + +```shell +cat < GET /get/origin/path/extra HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:09:31 GMT +< content-length: 512 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/force/replace/fullpath", + "host": "path.rewrite.example", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Envoy-Original-Path": [ + "/get/origin/path/extra" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "8ab774d6-9ffa-4faa-abbb-f45b0db00895" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Envoy-Original-Path` is `/get/origin/path/extra`, but the actual path is +`/force/replace/fullpath`. + +## Rewrite Host Name + +You can configure to rewrite the hostname like below. In this example, any requests sent to +`http://${GATEWAY_HOST}/get` with `--header "Host: path.rewrite.example"` will rewrite host into `envoygateway.io`. + +```shell +cat < GET /get HTTP/1.1 +> Host: path.rewrite.example +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< x-content-type-options: nosniff +< date: Wed, 21 Dec 2022 11:15:15 GMT +< content-length: 481 +< x-envoy-upstream-service-time: 0 +< server: envoy +< +{ + "path": "/get", + "host": "envoygateway.io", + "method": "GET", + "proto": "HTTP/1.1", + "headers": { + "Accept": [ + "*/*" + ], + "User-Agent": [ + "curl/7.85.0" + ], + "X-Envoy-Expected-Rq-Timeout-Ms": [ + "15000" + ], + "X-Forwarded-Host": [ + "path.rewrite.example" + ], + "X-Forwarded-Proto": [ + "http" + ], + "X-Request-Id": [ + "39aa447c-97b9-45a3-a675-9fb266ab1af0" + ] + }, + "namespace": "default", + "ingress": "", + "service": "", + "pod": "backend-6fdd4b9bd8-8vlc5" +... +``` + +You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`. + +[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPURLRewriteFilter diff --git a/site/content/en/v0.6.0/user/jwt-authentication.md b/site/content/en/v0.6.0/user/jwt-authentication.md new file mode 100644 index 00000000000..e11358c22fa --- /dev/null +++ b/site/content/en/v0.6.0/user/jwt-authentication.md @@ -0,0 +1,170 @@ +--- +title: "JWT Authentication" +--- + +This guide provides instructions for configuring [JSON Web Token (JWT)][jwt] authentication. JWT authentication checks +if an incoming request has a valid JWT before routing the request to a backend service. Currently, Envoy Gateway only +supports validating a JWT from an HTTP header, e.g. `Authorization: Bearer `. + +Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure JWT authentication. +This instantiated resource can be linked to a [Gateway][Gateway], [HTTPRoute][HTTPRoute] or [GRPCRoute][GRPCRoute] resource. + +## Prerequisites + +Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gateway and the example manifest. +For GRPC - follow the steps from the [GRPC Routing](../grpc-routing/) example. +Before proceeding, you should be able to query the example backend using HTTP or GRPC. + +## Configuration + +Allow requests with a valid JWT by creating an [SecurityPolicy][SecurityPolicy] and attaching it to the example +HTTPRoute or GRPCRoute. + +### HTTPRoute + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/jwt/jwt.yaml +``` + +Two HTTPRoute has been created, one for `/foo` and another for `/bar`. A SecurityPolicy has been created and targeted +HTTPRoute foo to authenticate requests for `/foo`. The HTTPRoute bar is not targeted by the SecurityPolicy and will allow +unauthenticated requests to `/bar`. + +Verify the HTTPRoute configuration and status: + +```shell +kubectl get httproute/foo -o yaml +kubectl get httproute/bar -o yaml +``` + +The SecurityPolicy is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the SecurityPolicy configuration: + +```shell +kubectl get securitypolicy/jwt-example -o yaml +``` + +### GRPCRoute + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/jwt/grpc-jwt.yaml +``` + +A SecurityPolicy has been created and targeted GRPCRoute yages to authenticate all requests for `yages` service.. + +Verify the GRPCRoute configuration and status: + +```shell +kubectl get grpcroute/yages -o yaml +``` + +The SecurityPolicy is configured for JWT authentication and uses a single [JSON Web Key Set (JWKS)][jwks] +provider for authenticating the JWT. + +Verify the SecurityPolicy configuration: + +```shell +kubectl get securitypolicy/jwt-example -o yaml +``` + +## Testing + +Ensure the `GATEWAY_HOST` environment variable from the [Quickstart](../quickstart) guide is set. If not, follow the +Quickstart instructions to set the variable. + +```shell +echo $GATEWAY_HOST +``` + +### HTTPRoute + +Verify that requests to `/foo` are denied without a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `401` HTTP response code should be returned. + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `/foo` with a valid JWT is allowed: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://$GATEWAY_HOST/foo +``` + +A `200` HTTP response code should be returned. + +Verify that requests to `/bar` are allowed __without__ a JWT: + +```shell +curl -sS -o /dev/null -H "Host: www.example.com" -w "%{http_code}\n" http://$GATEWAY_HOST/bar +``` + +### GRPCRoute + +Verify that requests to `yages`service are denied without a JWT: + +```shell +grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +Error invoking method "yages.Echo/Ping": rpc error: code = Unauthenticated desc = failed to query for service descriptor "yages.Echo": Jwt is missing +``` + +Get the JWT used for testing request authentication: + +```shell +TOKEN=$(curl https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/test.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - +``` + +__Note:__ The above command decodes and returns the token's payload. You can replace `f2` with `f1` to view the token's +header. + +Verify that a request to `yages` service with a valid JWT is allowed: + +```shell +grpcurl -plaintext -H "authorization: Bearer $TOKEN" -authority=grpc-example.com ${GATEWAY_HOST}:80 yages.Echo/Ping +``` + +You should see the below response + +```shell +{ + "text": "pong" +} +``` + +## Clean-Up + +Follow the steps from the [Quickstart](../quickstart) guide to uninstall Envoy Gateway and the example manifest. + +Delete the SecurityPolicy: + +```shell +kubectl delete securitypolicy/jwt-example +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[SecurityPolicy]: ../../design/security-policy +[jwt]: https://tools.ietf.org/html/rfc7519 +[jwks]: https://tools.ietf.org/html/rfc7517 +[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway +[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute +[GRPCRoute]: https://gateway-api.sigs.k8s.io/api-types/grpcroute diff --git a/site/content/en/v0.6.0/user/multicluster-service.md b/site/content/en/v0.6.0/user/multicluster-service.md new file mode 100644 index 00000000000..ca7580e0f8c --- /dev/null +++ b/site/content/en/v0.6.0/user/multicluster-service.md @@ -0,0 +1,86 @@ +--- +title: "Multicluster Service Routing" +--- + +The Multicluster Service API ServiceImport object can be used as part of the GatewayAPI backendRef for configuring routes. For more information about multicluster service API follow [sig documentation](https://multicluster.sigs.k8s.io/concepts/multicluster-services-api/). + +We will use [Submariner project](https://github.com/submariner-io/submariner) for setting up the multicluster environment for exporting the service to be routed from peer clusters. + +## Setting KIND clusters and installing Submariner. + +- We will be using KIND clusters to demonstrate this example. + +```shell +git clone https://github.com/submariner-io/submariner-operator +cd submariner-operator +make clusters +``` + +Note: remain in submariner-operator directory for the rest of the steps in this section + +- Install subctl: + +```shell +curl -Ls https://get.submariner.io | VERSION=v0.14.6 bash +``` + +- Set up multicluster service API and submariner for cross cluster traffic using ServiceImport + +```shell +subctl deploy-broker --kubeconfig output/kubeconfigs/kind-config-cluster1 --globalnet +subctl join --kubeconfig output/kubeconfigs/kind-config-cluster1 broker-info.subm --clusterid cluster1 --natt=false +subctl join --kubeconfig output/kubeconfigs/kind-config-cluster2 broker-info.subm --clusterid cluster2 --natt=false +``` + +Once the above steps are done and all the pods are up in both the clusters. We are ready for installing envoy gateway. + +## Install EnvoyGateway + +Install the Gateway API CRDs and Envoy Gateway in cluster1: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.6.0 -n envoy-gateway-system --create-namespace --kubeconfig output/kubeconfigs/kind-config-cluster1 +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available --kubeconfig output/kubeconfigs/kind-config-cluster1 +``` + +## Install Application + +Install the backend application in cluster2 and export it through subctl command. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/application.yaml --kubeconfig output/kubeconfigs/kind-config-cluster2 +subctl export service backend --namespace default --kubeconfig output/kubeconfigs/kind-config-cluster2 +``` + +## Create Gateway API Objects + +Create the Gateway API objects GatewayClass, Gateway and HTTPRoute in cluster1 to set up the routing. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/multicluster-service.yaml --kubeconfig output/kubeconfigs/kind-config-cluster1 +``` + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` diff --git a/site/content/en/v0.6.0/user/proxy-observability.md b/site/content/en/v0.6.0/user/proxy-observability.md new file mode 100644 index 00000000000..8601a0de7a8 --- /dev/null +++ b/site/content/en/v0.6.0/user/proxy-observability.md @@ -0,0 +1,134 @@ +--- +title: "Proxy Observability" +--- + +Envoy Gateway provides observability for the ControlPlane and the underlying EnvoyProxy instances. +This guide show you how to config proxy observability, includes metrics, logs, and traces. + +## Prerequisites + +Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +[FluentBit](https://fluentbit.io/) is used to collect logs from the EnvoyProxy instances and forward them to Loki. Install FluentBit: + +```shell +helm repo add fluent https://fluent.github.io/helm-charts +helm repo update +helm upgrade --install fluent-bit fluent/fluent-bit -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/fluent-bit/helm-values.yaml -n monitoring --create-namespace --version 0.30.4 +``` + +[Loki](https://grafana.com/oss/loki/) is used to store logs. Install Loki: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/loki/loki.yaml -n monitoring +``` + +[Tempo](https://grafana.com/oss/tempo/) is used to store traces. Install Tempo: + +```shell +helm repo add grafana https://grafana.github.io/helm-charts +helm repo update +helm upgrade --install tempo grafana/tempo -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/tempo/helm-values.yaml -n monitoring --create-namespace --version 1.3.1 +``` + +[OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) offers a vendor-agnostic implementation of how to receive, process and export telemetry data. +Install OTel-Collector: + +```shell +helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts +helm repo update +helm upgrade --install otel-collector open-telemetry/opentelemetry-collector -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/otel-collector/helm-values.yaml -n monitoring --create-namespace --version 0.60.0 +``` + +Expose endpoints: + +```shell +LOKI_IP=$(kubectl get svc loki -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +TEMPO_IP=$(kubectl get svc tempo -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +## Metrics + +By default, Envoy Gateway expose metrics with prometheus endpoint. + +Verify metrics: + +```shell +export ENVOY_POD_NAME=$(kubectl get pod -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +kubectl port-forward pod/$ENVOY_POD_NAME -n envoy-gateway-system 19001:19001 + +# check metrics +curl localhost:19001/stats/prometheus | grep "default/backend/rule/0/match/0-www" +``` + +You can disable metrics by setting the `telemetry.metrics.prometheus.disable` to `true` in the `EnvoyProxy` CRD. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/metric/disable-prometheus.yaml +``` + +Envoy Gateway can send metrics to OpenTelemetry Sink. +Send metrics to OTel-Collector: + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/metric/otel-sink.yaml +``` + +Verify OTel-Collector metrics: + +```shell +export OTEL_POD_NAME=$(kubectl get pod -n monitoring --selector=app.kubernetes.io/name=opentelemetry-collector -o jsonpath='{.items[0].metadata.name}') +kubectl port-forward pod/$OTEL_POD_NAME -n monitoring 19001:19001 + +# check metrics +curl localhost:19001/metrics | grep "default/backend/rule/0/match/0-www" +``` + +## Logs + +By default, Envoy Gateway send logs to stdout in [default text format](https://www.envoyproxy.io/docs/envoy/v0.6.0/configuration/observability/access_log/usage.html#default-format-string). +Verify logs from loki: + +```shell +curl -s "http://$LOKI_IP:3100/loki/api/v1/query_range" --data-urlencode "query={job=\"fluentbit\"}" | jq '.data.result[0].values' +``` + +If you want to disable it, set the `telemetry.accesslog.disable` to `true` in the `EnvoyProxy` CRD. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/accesslog/disable-accesslog.yaml +``` + +Envoy Gateway can send logs to OpenTelemetry Sink. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/accesslog/otel-accesslog.yaml +``` + +Verify logs from loki: + +```shell +curl -s "http://$LOKI_IP:3100/loki/api/v1/query_range" --data-urlencode "query={exporter=\"OTLP\"}" | jq '.data.result[0].values' +``` + +## Traces + +By default, Envoy Gateway doesn't send traces to OpenTelemetry Sink. +You can enable traces by setting the `telemetry.tracing` in the `EnvoyProxy` CRD. + +***Note:*** Envoy Gateway use 100% sample rate, which means all requests will be traced. This may cause performance issues. + +```shell +kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.6.0/examples/kubernetes/tracing/default.yaml +``` + +Verify traces from tempo: + +```shell +curl -s "http://$TEMPO_IP:3100/api/search" --data-urlencode "q={ component=envoy }" | jq .traces +``` + +```shell +curl -s "http://$TEMPO_IP:3100/api/traces/" | jq +``` diff --git a/site/content/en/v0.6.0/user/quickstart.md b/site/content/en/v0.6.0/user/quickstart.md new file mode 100644 index 00000000000..2e47dd6f26b --- /dev/null +++ b/site/content/en/v0.6.0/user/quickstart.md @@ -0,0 +1,103 @@ +--- +title: "Quickstart" +weight: 1 +--- + +This guide will help you get started with Envoy Gateway in a few simple steps. + +## Prerequisites + +A Kubernetes cluster. + +__Note:__ Refer to the [Compatibility Matrix](/blog/2022/10/01/versions/) for supported Kubernetes versions. + +__Note:__ In case your Kubernetes cluster, does not have a LoadBalancer implementation, we recommend installing one +so the `Gateway` resource has an Address associated with it. We recommend using [MetalLB](https://metallb.universe.tf/installation/). + +## Installation + +Install the Gateway API CRDs and Envoy Gateway: + +```shell +helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.6.0 -n envoy-gateway-system --create-namespace +``` + +Wait for Envoy Gateway to become available: + +```shell +kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available +``` + +Install the GatewayClass, Gateway, HTTPRoute and example app: + +```shell +kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.6.0/quickstart.yaml -n default +``` + +**Note**: [`quickstart.yaml`] defines that Envoy Gateway will listen for +traffic on port 80 on its globally-routable IP address, to make it easy to use +browsers to test Envoy Gateway. When Envoy Gateway sees that its Listener is +using a privileged port (<1024), it will map this internally to an +unprivileged port, so that Envoy Gateway doesn't need additional privileges. +It's important to be aware of this mapping, since you may need to take it into +consideration when debugging. + +[`quickstart.yaml`]: https://github.com/envoyproxy/gateway/releases/download/v0.6.0/quickstart.yaml + +## Testing the Configuration + +Get the name of the Envoy service created the by the example Gateway: + +```shell +export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}') +``` + +Port forward to the Envoy service: + +```shell +kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 & +``` + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://localhost:8888/get +``` + +### External LoadBalancer Support + +You can also test the same functionality by sending traffic to the External IP. To get the external IP of the +Envoy service, run: + +```shell +export GATEWAY_HOST=$(kubectl get svc/${ENVOY_SERVICE} -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +In certain environments, the load balancer may be exposed using a hostname, instead of an IP address. If so, replace +`ip` in the above command with `hostname`. + +Curl the example app through Envoy proxy: + +```shell +curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get +``` + +## Clean-Up + +Use the steps in this section to uninstall everything from the quickstart guide. + +Delete the GatewayClass, Gateway, HTTPRoute and Example App: + +```shell +kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.6.0/quickstart.yaml --ignore-not-found=true +``` + +Delete the Gateway API CRDs and Envoy Gateway: + +```shell +helm uninstall eg -n envoy-gateway-system +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. diff --git a/site/content/en/v0.6.0/user/rate-limit.md b/site/content/en/v0.6.0/user/rate-limit.md new file mode 100644 index 00000000000..8f5867413ce --- /dev/null +++ b/site/content/en/v0.6.0/user/rate-limit.md @@ -0,0 +1,826 @@ +--- +title: "Rate Limit" +--- + +Rate limit is a feature that allows the user to limit the number of incoming requests to a predefined value based on attributes within the traffic flow. + +Here are some reasons why you may want to implement Rate limits + +* To prevent malicious activity such as DDoS attacks. +* To prevent applications and its resources (such as a database) from getting overloaded. +* To create API limits based on user entitlements. + +Envoy Gateway supports [Global rate limiting][], where the rate limit is common across all the instances of Envoy proxies where its applied +i.e. if the data plane has 2 replicas of Envoy running, and the rate limit is 10 requests/second, this limit is common and will be hit +if 5 requests pass through the first replica and 5 requests pass through the second replica within the same second. + +Envoy Gateway introduces a new CRD called [BackendTrafficPolicy][] that allows the user to describe their rate limit intent. This instantiated resource +can be linked to a [Gateway][], [HTTPRoute][] or [GRPCRoute][] resource. + +## Prerequisites + +### Install Envoy Gateway + +* Follow the steps from the [Quickstart Guide](../quickstart) to install Envoy Gateway and the HTTPRoute example manifest. +Before proceeding, you should be able to query the example backend using HTTP. + +### Install Redis + +* The global rate limit feature is based on [Envoy Ratelimit][] which requires a Redis instance as its caching layer. +Lets install a Redis deployment in the `redis-system` namespce. + +```shell +cat <> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> @49.51.177.138 -p 5300 foo.bar.com +; (1 server found) +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58125 +;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3 +;; WARNING: recursion requested but not available + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +; COOKIE: 24fb86eba96ebf62 (echoed) +;; QUESTION SECTION: +;foo.bar.com. IN A + +;; ADDITIONAL SECTION: +foo.bar.com. 0 IN A 10.244.0.19 +_udp.foo.bar.com. 0 IN SRV 0 0 42376 . + +;; Query time: 1 msec +;; SERVER: 49.51.177.138#5300(49.51.177.138) (UDP) +;; WHEN: Fri Jan 13 10:20:34 UTC 2023 +;; MSG SIZE rcvd: 114 +``` + +## Clean-Up + +Follow the steps from the [Quickstart Guide](../quickstart) to uninstall Envoy Gateway. + +Delete the CoreDNS example manifest and the UDPRoute: + +```shell +kubectl delete deploy/coredns +kubectl delete service/coredns +kubectl delete cm/coredns +kubectl delete udproute/coredns +``` + +## Next Steps + +Checkout the [Developer Guide](../../contributions/develop/) to get involved in the project. + +[UDPRoute]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.UDPRoute +[UDP proxy documentation]: https://www.envoyproxy.io/docs/envoy/v0.6.0/configuration/listeners/udp_filters/udp_proxy diff --git a/site/docker-compose.yaml b/site/docker-compose.yaml new file mode 100644 index 00000000000..e8f211a610e --- /dev/null +++ b/site/docker-compose.yaml @@ -0,0 +1,13 @@ +version: "3.3" + +services: + + site: + image: docsy/docsy-example + build: + context: . + command: server + ports: + - "1313:1313" + volumes: + - .:/src diff --git a/site/go.mod b/site/go.mod new file mode 100644 index 00000000000..ec0974f5614 --- /dev/null +++ b/site/go.mod @@ -0,0 +1,9 @@ +module github.com/google/docsy-example + +go 1.12 + +require ( + github.com/FortAwesome/Font-Awesome v0.0.0-20230327165841-0698449d50f2 // indirect + github.com/google/docsy v0.7.1 // indirect + github.com/twbs/bootstrap v5.2.3+incompatible // indirect +) diff --git a/site/go.sum b/site/go.sum new file mode 100644 index 00000000000..e1d4ad4df70 --- /dev/null +++ b/site/go.sum @@ -0,0 +1,22 @@ +github.com/FortAwesome/Font-Awesome v0.0.0-20220831210243-d3a7818c253f h1:bvkUptSRPZBr3Kxuk+bnWCEmQ5MtEJX5fjezyV0bC3g= +github.com/FortAwesome/Font-Awesome v0.0.0-20220831210243-d3a7818c253f/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= +github.com/FortAwesome/Font-Awesome v0.0.0-20221115183454-96cafbd73ec4 h1:xfr9SidRCMEh4A8fdkLhFPcHAVbrdv3Ua0Jp/nSmhhQ= +github.com/FortAwesome/Font-Awesome v0.0.0-20221115183454-96cafbd73ec4/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= +github.com/FortAwesome/Font-Awesome v0.0.0-20230327165841-0698449d50f2 h1:Uv1z5EqCfmiK4IHUwT0m3h/u/WCk+kpRfxvAZhpC7Gc= +github.com/FortAwesome/Font-Awesome v0.0.0-20230327165841-0698449d50f2/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= +github.com/google/docsy v0.5.1 h1:D/ZdFKiE29xM/gwPwQzmkyXhcbQGkReRS6aGrF7lnYk= +github.com/google/docsy v0.5.1/go.mod h1:maoUAQU5H/d+FrZIB4xg1EVWAx7RyFMGSDJyWghm31E= +github.com/google/docsy v0.6.0 h1:43bVF18t2JihAamelQjjGzx1vO2ljCilVrBgetCA8oI= +github.com/google/docsy v0.6.0/go.mod h1:VKKLqD8PQ7AglJc98yBorATfW7GrNVsn0kGXVYF6G+M= +github.com/google/docsy v0.7.0 h1:JaeZ0/KufX/BJ3SyATb/fmZa1DFI7o5d9KU+i6+lLJY= +github.com/google/docsy v0.7.0/go.mod h1:5WhIFchr5BfH6agjcInhpLRz7U7map0bcmKSpcrg6BE= +github.com/google/docsy v0.7.1 h1:DUriA7Nr3lJjNi9Ulev1SfiG1sUYmvyDeU4nTp7uDxY= +github.com/google/docsy v0.7.1/go.mod h1:JCmE+c+izhE0Rvzv3y+AzHhz1KdwlA9Oj5YBMklJcfc= +github.com/google/docsy/dependencies v0.5.1/go.mod h1:EDGc2znMbGUw0RW5kWwy2oGgLt0iVXBmoq4UOqstuNE= +github.com/google/docsy/dependencies v0.6.0/go.mod h1:EDGc2znMbGUw0RW5kWwy2oGgLt0iVXBmoq4UOqstuNE= +github.com/google/docsy/dependencies v0.7.0/go.mod h1:gihhs5gmgeO+wuoay4FwOzob+jYJVyQbNaQOh788lD4= +github.com/google/docsy/dependencies v0.7.1/go.mod h1:gihhs5gmgeO+wuoay4FwOzob+jYJVyQbNaQOh788lD4= +github.com/twbs/bootstrap v4.6.2+incompatible h1:TDa+R51BTiy1wEHSYjmqDb8LxNl/zaEjAOpRE9Hwh/o= +github.com/twbs/bootstrap v4.6.2+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= +github.com/twbs/bootstrap v5.2.3+incompatible h1:lOmsJx587qfF7/gE7Vv4FxEofegyJlEACeVV+Mt7cgc= +github.com/twbs/bootstrap v5.2.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= diff --git a/site/hugo.toml b/site/hugo.toml new file mode 100644 index 00000000000..42b20f01c29 --- /dev/null +++ b/site/hugo.toml @@ -0,0 +1,251 @@ +baseURL = "/" +title = "Envoy Gateway" + +# Language settings +contentDir = "content/en" +defaultContentLanguage = "en" +defaultContentLanguageInSubdir = false +# Useful when translating. +enableMissingTranslationPlaceholders = true + +enableRobotsTXT = true + +# Will give values to .Lastmod etc. +enableGitInfo = true + +# Comment out to enable taxonomies in Docsy +# disableKinds = ["taxonomy", "taxonomyTerm"] + +# You can add your own taxonomies +[taxonomies] +tag = "tags" +category = "categories" + +[params.taxonomy] +# set taxonomyCloud = [] to hide taxonomy clouds +taxonomyCloud = ["tags", "categories"] + +# If used, must have same length as taxonomyCloud +taxonomyCloudTitle = ["Tag Cloud", "Categories"] + +# set taxonomyPageHeader = [] to hide taxonomies on the page headers +taxonomyPageHeader = ["tags", "categories"] + +# Highlighting config +pygmentsCodeFences = true +pygmentsUseClasses = false +# Use the new Chroma Go highlighter in Hugo. +pygmentsUseClassic = false +#pygmentsOptions = "linenos=table" +# See https://help.farbox.com/pygments.html +pygmentsStyle = "tango" + +# Configure how URLs look like per section. +[permalinks] +blog = "/:section/:year/:month/:day/:slug/" + +# Image processing configuration. +[imaging] +resampleFilter = "CatmullRom" +quality = 75 +anchor = "smart" + +[services] +[services.googleAnalytics] +# Comment out the next line to disable GA tracking. Also disables the feature described in [params.ui.feedback]. +id = "UA-00000000-0" + +# Language configuration + +[languages] +[languages.en] +languageName ="English" +# Weight used for sorting. +weight = 1 +[languages.en.params] +title = "Envoy Gateway" +description = "Manages Envoy Proxy as a Standalone or Kubernetes-based Application Gateway" + +[markup] + [markup.goldmark] + [markup.goldmark.parser.attribute] + block = true + [markup.goldmark.renderer] + unsafe = true + [markup.highlight] + # See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html + style = "tango" + # Uncomment if you want your chosen highlight style used for code blocks without a specified language + # guessSyntax = "true" + +# Everything below this are Site Params + +# Comment out if you don't want the "print entire section" link enabled. +[outputs] +section = ["HTML", "print", "RSS"] + +[params] +copyright = "The Envoy Gateway Authors" + + +# First one is picked as the Twitter card image if not set on page. +# images = ["images/project-illustration.png"] + +# Menu title if your navbar has a versions selector to access old versions of your site. +# This menu appears only if you have at least one [params.versions] set. +version_menu = "Versions" + +# Flag used in the "version-banner" partial to decide whether to display a +# banner on every page indicating that this is an archived version of the docs. +# Set this flag to "true" if you want to display the banner. +archived_version = false + +# The version number for the version of the docs represented in this doc set. +# Used in the "version-banner" partial to display a version number for the +# current doc set. +version = "latest" + +# A link to latest version of the docs. Used in the "version-banner" partial to +# point people to the main doc site. +url_latest_version = "https://gateway.envoyproxy.io" + +# Repository configuration (URLs for in-page links to opening issues and suggesting changes) +github_repo = "https://github.com/envoyproxy/gateway" +# An optional link to a related project repo. For example, the sibling repository where your product code lives. +github_project_repo = "https://github.com/envoyproxy/gateway" + +# Specify a value here if your content directory is not in your repo's root directory +github_subdir = "/site/" + +# Uncomment this if your GitHub repo does not have "main" as the default branch, +# or specify a new value if you want to reference another branch in your GitHub links +github_branch= "main" + +# Google Custom Search Engine ID. Remove or comment out to disable search. +# gcs_engine_id = "d72aa9b2712488cc3" + +# Enable Algolia DocSearch +algolia_docsearch = false + +# Enable Lunr.js offline search +offlineSearch = true + +# Enable syntax highlighting and copy buttons on code blocks with Prism +prism_syntax_highlighting = false + +# User interface configuration +[params.ui] +# Set to true to disable breadcrumb navigation. +breadcrumb_disable = false +# Set to true to disable the About link in the site footer +footer_about_disable = false +# Set to false if you don't want to display a logo (/assets/icons/logo.svg) in the top navbar +navbar_logo = true +# Set to true if you don't want the top navbar to be translucent when over a `block/cover`, like on the homepage. +navbar_translucent_over_cover_disable = false +# Enable to show the side bar menu in its compact state. +sidebar_menu_compact = true +# Set to true to hide the sidebar search box (the top nav search box will still be displayed if search is enabled) +sidebar_search_disable = false +# Show expand/collapse icon for sidebar sections +sidebar_menu_foldable = true + +# Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events. +# This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set. +# If you want this feature, but occasionally need to remove the "Feedback" section from a single page, +# add "hide_feedback: true" to the page's front matter. +[params.ui.feedback] +enable = true +# The responses that the user sees after clicking "yes" (the page was helpful) or "no" (the page was not helpful). +yes = 'Glad to hear it! Please tell us how we can improve.' +no = 'Sorry to hear that. Please tell us how we can improve.' + +# Adds a reading time to the top of each doc. +# If you want this feature, but occasionally need to remove the Reading time from a single page, +# add "hide_readingtime: true" to the page's front matter +[params.ui.readingtime] +enable = true + +# hugo module configuration + +[module] + # uncomment line below for temporary local development of module + # replacements = "github.com/envoyproxy/gateway -> ../../docsy" + [module.hugoVersion] + extended = true + min = "0.110.0" + [[module.imports]] + path = "github.com/google/docsy" + disable = false + [[module.imports]] + path = "github.com/google/docsy/dependencies" + disable = false + +[params.links] +# End user relevant links. These will show up on left side of footer and in the community page if you have one. +[[params.links.user]] + name = "User mailing list" + url = "mailto:envoy-users@googlegroups.com" + icon = "fa fa-envelope" + desc = "Discussion and help from your fellow users" +[[params.links.user]] + name ="Twitter" + url = "https://twitter.com/EnvoyProxy" + icon = "fab fa-twitter" + desc = "Follow us on Twitter to get the latest news!" +# Developer relevant links. These will show up on right side of footer and in the community page if you have one. +[[params.links.developer]] + name = "GitHub" + url = "https://github.com/envoyproxy/gateway" + icon = "fab fa-github" + desc = "Development takes place here!" +[[params.links.developer]] + name = "Slack" + url = "https://envoyproxy.slack.com/archives/C03E6NHLESV" + icon = "fab fa-slack" + desc = "Chat with other project developers" + +[menu] + [[menu.main]] + name = "Blog" + weight = -99 + pre = "" + url = "/blog" + [[menu.main]] + name = "About" + weight = -100 + pre = "" + url = "/about" + [[menu.main]] + name = "Announcements" + weight = -101 + pre = "" + url = "/announcements" + [[menu.main]] + name = "Documentation" + weight = -102 + pre = "" + url = "/v0.6.0" + +[[params.versions]] + version = "latest" + url = "/latest" + +[[params.versions]] + version = "v0.2.0" + url = "/v0.2.0" + +[[params.versions]] + version = "v0.3.0" + url = "/v0.3.0" + +[[params.versions]] + version = "v0.4.0" + url = "/v0.4.0" + +[[params.versions]] + version = "v0.5.0" + url = "/v0.5.0" +[[params.versions]] + version = "v0.6.0" + url = "/v0.6.0" diff --git a/site/layouts/404.html b/site/layouts/404.html new file mode 100644 index 00000000000..d32828c8b2f --- /dev/null +++ b/site/layouts/404.html @@ -0,0 +1,6 @@ +{{ define "main" -}} +
+

Not found

+

Oops! This page doesn't exist. Try going back to the home page.

+
+{{- end }} diff --git a/site/netlify.toml b/site/netlify.toml new file mode 100644 index 00000000000..60839e24812 --- /dev/null +++ b/site/netlify.toml @@ -0,0 +1,12 @@ +# Hugo build configuration for Netlify +# (https://gohugo.io/hosting-and-deployment/hosting-on-netlify/#configure-hugo-version-in-netlify) + +[build] +command = "npm run build:preview" +publish = "public" + +[build.environment] +GO_VERSION = "1.20.5" + +[context.production] +command = "npm run build:production" diff --git a/site/package.json b/site/package.json new file mode 100644 index 00000000000..85947b7888d --- /dev/null +++ b/site/package.json @@ -0,0 +1,38 @@ +{ + "name": "docsy-example-site", + "version": "0.7.1", + "description": "Example site that uses Docsy theme for technical documentation.", + "repository": "github:google/docsy-example", + "homepage": "https://example.docsy.dev", + "author": "Docsy Authors", + "license": "Apache-2.0", + "bugs": "https://github.com/google/docsy-example/issues", + "spelling": "cSpell:ignore HTMLTEST precheck postbuild -", + "scripts": { + "_build": "npm run _hugo-dev", + "_check:links": "echo IMPLEMENTATION PENDING for check-links; echo", + "_hugo": "hugo --cleanDestinationDir", + "_hugo-dev": "npm run _hugo -- -e dev -DFE", + "_serve": "npm run _hugo-dev -- --minify serve", + "build:preview": "npm run _hugo-dev -- --minify --baseURL \"${DEPLOY_PRIME_URL:-/}\"", + "build:production": "npm run _hugo -- --minify", + "build": "npm run _build", + "check:links:all": "HTMLTEST_ARGS= npm run _check:links", + "check:links": "npm run _check:links", + "clean": "rm -Rf public/* resources", + "make:public": "git init -b main public", + "precheck:links:all": "npm run build", + "precheck:links": "npm run build", + "postbuild:preview": "npm run _check:links", + "postbuild:production": "npm run _check:links", + "serve": "npm run _serve", + "test": "npm run check:links", + "update:pkg:dep": "npm install --save-dev autoprefixer@latest postcss-cli@latest", + "update:pkg:hugo": "npm install --save-dev --save-exact hugo-extended@latest" + }, + "devDependencies": { + "autoprefixer": "^10.4.14", + "hugo-extended": "0.117.0", + "postcss-cli": "^10.1.0" + } +} diff --git a/site/static/favicons/android-chrome-192x192.png b/site/static/favicons/android-chrome-192x192.png new file mode 100644 index 00000000000..2b522eb9f24 Binary files /dev/null and b/site/static/favicons/android-chrome-192x192.png differ diff --git a/site/static/favicons/android-chrome-512x512.png b/site/static/favicons/android-chrome-512x512.png new file mode 100644 index 00000000000..c4290018349 Binary files /dev/null and b/site/static/favicons/android-chrome-512x512.png differ diff --git a/site/static/favicons/apple-touch-icon.png b/site/static/favicons/apple-touch-icon.png new file mode 100644 index 00000000000..68a47526e14 Binary files /dev/null and b/site/static/favicons/apple-touch-icon.png differ diff --git a/site/static/favicons/favicon-16x16.png b/site/static/favicons/favicon-16x16.png new file mode 100644 index 00000000000..620b17acd61 Binary files /dev/null and b/site/static/favicons/favicon-16x16.png differ diff --git a/site/static/favicons/favicon-32x32.png b/site/static/favicons/favicon-32x32.png new file mode 100644 index 00000000000..e4be2e69213 Binary files /dev/null and b/site/static/favicons/favicon-32x32.png differ diff --git a/site/static/favicons/favicon.ico b/site/static/favicons/favicon.ico new file mode 100644 index 00000000000..d10fb6a2872 Binary files /dev/null and b/site/static/favicons/favicon.ico differ diff --git a/site/static/favicons/site.webmanifest b/site/static/favicons/site.webmanifest new file mode 100644 index 00000000000..45dc8a20658 --- /dev/null +++ b/site/static/favicons/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/docs/latest/images/architecture.png b/site/static/img/architecture.png similarity index 100% rename from docs/latest/images/architecture.png rename to site/static/img/architecture.png diff --git a/site/static/img/cncf.svg b/site/static/img/cncf.svg new file mode 100644 index 00000000000..9ecac9cf68a --- /dev/null +++ b/site/static/img/cncf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/static/img/envoy-clusters-dashboard.png b/site/static/img/envoy-clusters-dashboard.png new file mode 100644 index 00000000000..92d225598ec Binary files /dev/null and b/site/static/img/envoy-clusters-dashboard.png differ diff --git a/site/static/img/envoy-global-dashboard.png b/site/static/img/envoy-global-dashboard.png new file mode 100644 index 00000000000..645372ab4eb Binary files /dev/null and b/site/static/img/envoy-global-dashboard.png differ diff --git a/site/static/img/envoy-pod-resources-dashboard.png b/site/static/img/envoy-pod-resources-dashboard.png new file mode 100644 index 00000000000..1236ceb0f37 Binary files /dev/null and b/site/static/img/envoy-pod-resources-dashboard.png differ diff --git a/docs/latest/images/extension-example.png b/site/static/img/extension-example.png similarity index 100% rename from docs/latest/images/extension-example.png rename to site/static/img/extension-example.png diff --git a/site/static/img/traffic.png b/site/static/img/traffic.png new file mode 100644 index 00000000000..99970a63e46 Binary files /dev/null and b/site/static/img/traffic.png differ diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go new file mode 100644 index 00000000000..9ca669caea5 --- /dev/null +++ b/test/cel-validation/backendtrafficpolicy_test.go @@ -0,0 +1,341 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestBackendTrafficPolicyTarget(t *testing.T) { + ctx := context.Background() + baseBTP := egv1a1.BackendTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "btp", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.BackendTrafficPolicySpec{}, + } + + sectionName := gwapiv1a2.SectionName("foo") + + cases := []struct { + desc string + mutate func(btp *egv1a1.BackendTrafficPolicy) + mutateStatus func(btp *egv1a1.BackendTrafficPolicy) + wantErrors []string + }{ + { + desc: "valid gateway targetRef", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "valid httproute targetRef", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("HTTPRoute"), + Name: gwapiv1a2.ObjectName("httpbin-route"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "sectionName disabled until supported", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + SectionName: §ionName, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy does not yet support the sectionName field", + }, + }, + { + desc: "consistentHash field not nil when type is consistentHash", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.ConsistentHashLoadBalancerType, + ConsistentHash: &egv1a1.ConsistentHash{ + Type: "SourceIP", + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "consistentHash field nil when type is consistentHash", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.ConsistentHashLoadBalancerType, + }, + } + }, + wantErrors: []string{ + "spec.loadBalancer: Invalid value: \"object\": If LoadBalancer type is consistentHash, consistentHash field needs to be set", + }, + }, + { + desc: "leastRequest with ConsistentHash nil", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.LeastRequestLoadBalancerType, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "leastRequest with SlowStar is set", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.LeastRequestLoadBalancerType, + SlowStart: &egv1a1.SlowStart{ + Window: &metav1.Duration{ + Duration: 10000000, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "roundrobin with SlowStart is set", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.RoundRobinLoadBalancerType, + SlowStart: &egv1a1.SlowStart{ + Window: &metav1.Duration{ + Duration: 10000000, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: " random with SlowStart is set", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.RandomLoadBalancerType, + SlowStart: &egv1a1.SlowStart{ + Window: &metav1.Duration{ + Duration: 10000000, + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.loadBalancer: Invalid value: \"object\": Currently SlowStart is only supported for RoundRobin and LeastRequest load balancers.", + }, + }, + { + desc: " consistenthash with SlowStart is set", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + LoadBalancer: &egv1a1.LoadBalancer{ + Type: egv1a1.ConsistentHashLoadBalancerType, + SlowStart: &egv1a1.SlowStart{ + Window: &metav1.Duration{ + Duration: 10000000, + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.loadBalancer: Invalid value: \"object\": Currently SlowStart is only supported for RoundRobin and LeastRequest load balancers.", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + btp := baseBTP.DeepCopy() + btp.Name = fmt.Sprintf("btp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(btp) + } + err := c.Create(ctx, btp) + + if tc.mutateStatus != nil { + tc.mutateStatus(btp) + err = c.Status().Update(ctx, btp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating BackendTrafficPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.wantErrors { + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating BackendTrafficPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} diff --git a/test/cel-validation/clienttrafficpolicy_test.go b/test/cel-validation/clienttrafficpolicy_test.go new file mode 100644 index 00000000000..b0449b78f51 --- /dev/null +++ b/test/cel-validation/clienttrafficpolicy_test.go @@ -0,0 +1,170 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestClientTrafficPolicyTarget(t *testing.T) { + ctx := context.Background() + baseCTP := egv1a1.ClientTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ctp", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.ClientTrafficPolicySpec{}, + } + + sectionName := gwapiv1a2.SectionName("foo") + + cases := []struct { + desc string + mutate func(ctp *egv1a1.ClientTrafficPolicy) + mutateStatus func(ctp *egv1a1.ClientTrafficPolicy) + wantErrors []string + }{ + { + desc: "valid targetRef", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "sectionName disabled until supported", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + }, + SectionName: §ionName, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy does not yet support the sectionName field", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + ctp := baseCTP.DeepCopy() + ctp.Name = fmt.Sprintf("ctp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(ctp) + } + err := c.Create(ctx, ctp) + + if tc.mutateStatus != nil { + tc.mutateStatus(ctp) + err = c.Status().Update(ctx, ctp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating ClientTrafficPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.wantErrors { + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating ClientTrafficPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go new file mode 100644 index 00000000000..40347665ea8 --- /dev/null +++ b/test/cel-validation/envoyproxy_test.go @@ -0,0 +1,534 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func TestEnvoyProxyProvider(t *testing.T) { + ctx := context.Background() + baseEnvoyProxy := egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "proxy", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.EnvoyProxySpec{}, + } + + cases := []struct { + desc string + mutate func(envoy *egv1a1.EnvoyProxy) + mutateStatus func(envoy *egv1a1.EnvoyProxy) + wantErrors []string + }{ + { + desc: "nil provider", + mutate: func(envoy *egv1a1.EnvoyProxy) {}, + wantErrors: []string{}, + }, + { + desc: "unsupported provider", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: "foo", + }, + } + }, + wantErrors: []string{"Unsupported value: \"foo\": supported values: \"Kubernetes\""}, + }, + { + desc: "invalid service type", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceType("foo")), + }, + }, + }, + } + }, + wantErrors: []string{"Unsupported value: \"foo\": supported values: \"ClusterIP\", \"LoadBalancer\", \"NodePort\""}, + }, + { + desc: "allocateLoadBalancerNodePorts-pass-case1", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeLoadBalancer), + AllocateLoadBalancerNodePorts: ptr.To(true), + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "allocateLoadBalancerNodePorts-pass-case2", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeClusterIP), + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "allocateLoadBalancerNodePorts-fail", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeClusterIP), + AllocateLoadBalancerNodePorts: ptr.To(true), + }, + }, + }, + } + }, + wantErrors: []string{"allocateLoadBalancerNodePorts can only be set for LoadBalancer type"}, + }, + { + desc: "ServiceTypeLoadBalancer-with-valid-IP", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("20.205.243.166"), // github ip for test only + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ServiceTypeLoadBalancer-with-empty-IP", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeLoadBalancer), + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ServiceTypeLoadBalancer-with-invalid-IP", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("a.b.c.d"), + }, + }, + }, + } + }, + wantErrors: []string{"loadBalancerIP must be a valid IPv4 address"}, + }, + { + desc: "ServiceTypeClusterIP-with-empty-IP", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeClusterIP), + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ServiceTypeClusterIP-with-LoadBalancerIP", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Type: ptr.To(egv1a1.ServiceTypeClusterIP), + LoadBalancerIP: ptr.To("20.205.243.166"), // github ip for test only + }, + }, + }, + } + }, + wantErrors: []string{"loadBalancerIP can only be set for LoadBalancer type"}, + }, + { + desc: "invalid-ProxyAccessLogFormat", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: "foo", + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"Unsupported value: \"foo\": supported values: \"Text\", \"JSON\""}, + }, + { + desc: "ProxyAccessLogFormat-with-TypeText-but-no-text", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeText, + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeFile, + File: &egv1a1.FileEnvoyProxyAccessLog{ + Path: "foo/bar", + }, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"If AccessLogFormat type is Text, text field needs to be set"}, + }, + { + desc: "ProxyAccessLogFormat-with-TypeJSON-but-no-json", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeJSON, + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeFile, + File: &egv1a1.FileEnvoyProxyAccessLog{ + Path: "foo/bar", + }, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"If AccessLogFormat type is JSON, json field needs to be set"}, + }, + { + desc: "ProxyAccessLogFormat-with-TypeJSON-but-got-text", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeJSON, + Text: ptr.To("[%START_TIME%]"), + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeFile, + File: &egv1a1.FileEnvoyProxyAccessLog{ + Path: "foo/bar", + }, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"If AccessLogFormat type is JSON, json field needs to be set"}, + }, + { + desc: "ProxyAccessLogSink-with-TypeFile-but-no-file", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeText, + Text: ptr.To("[%START_TIME%]"), + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeFile, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"If AccessLogSink type is File, file field needs to be set"}, + }, + { + desc: "ProxyAccessLogSink-with-TypeOpenTelemetry-but-no-openTelemetry", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeText, + Text: ptr.To("[%START_TIME%]"), + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeOpenTelemetry, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"If AccessLogSink type is OpenTelemetry, openTelemetry field needs to be set"}, + }, + { + desc: "ProxyAccessLog-settings-pass", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + AccessLog: &egv1a1.ProxyAccessLog{ + Settings: []egv1a1.ProxyAccessLogSetting{ + { + Format: egv1a1.ProxyAccessLogFormat{ + Type: egv1a1.ProxyAccessLogFormatTypeText, + Text: ptr.To("[%START_TIME%]"), + }, + Sinks: []egv1a1.ProxyAccessLogSink{ + { + Type: egv1a1.ProxyAccessLogSinkTypeFile, + File: &egv1a1.FileEnvoyProxyAccessLog{ + Path: "foo/bar", + }, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ProxyMetricSink-with-TypeOpenTelemetry-but-no-openTelemetry", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + Metrics: &egv1a1.ProxyMetrics{ + Sinks: []egv1a1.ProxyMetricSink{ + { + Type: egv1a1.MetricSinkTypeOpenTelemetry, + }, + }, + }, + }, + } + }, + wantErrors: []string{"If MetricSink type is OpenTelemetry, openTelemetry field needs to be set"}, + }, + { + desc: "ProxyMetrics-sinks-pass", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + Metrics: &egv1a1.ProxyMetrics{ + Sinks: []egv1a1.ProxyMetricSink{ + { + Type: egv1a1.MetricSinkTypeOpenTelemetry, + OpenTelemetry: &egv1a1.ProxyOpenTelemetrySink{ + Host: "0.0.0.0", + Port: 3217, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ProxyHpa-maxReplicas-is-required", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{}, + }, + }, + } + }, + wantErrors: []string{"spec.provider.kubernetes.envoyHpa.maxReplicas: Required value"}, + }, + { + desc: "ProxyHpa-minReplicas-less-than-0", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](-1), + MaxReplicas: ptr.To[int32](2), + }, + }, + }, + } + }, + wantErrors: []string{"minReplicas must be greater than 0"}, + }, + { + desc: "ProxyHpa-maxReplicas-less-than-0", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MaxReplicas: ptr.To[int32](-1), + }, + }, + }, + } + }, + wantErrors: []string{"maxReplicas must be greater than 0"}, + }, + { + desc: "ProxyHpa-maxReplicas-less-than-minReplicas", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](5), + MaxReplicas: ptr.To[int32](2), + }, + }, + }, + } + }, + wantErrors: []string{"maxReplicas cannot be less than or equal to minReplicas"}, + }, + { + desc: "ProxyHpa-valid", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](5), + MaxReplicas: ptr.To[int32](10), + }, + }, + }, + } + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + proxy := baseEnvoyProxy.DeepCopy() + proxy.Name = fmt.Sprintf("proxy-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(proxy) + } + err := c.Create(ctx, proxy) + + if tc.mutateStatus != nil { + tc.mutateStatus(proxy) + err = c.Status().Update(ctx, proxy) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating EnvoyProxy; got err=\n%v\n;want error=%v", err, tc.wantErrors != nil) + } + + var missingErrorStrings []string + for _, wantError := range tc.wantErrors { + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating EnvoyProxy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} diff --git a/test/cel-validation/main_test.go b/test/cel-validation/main_test.go new file mode 100644 index 00000000000..64915388439 --- /dev/null +++ b/test/cel-validation/main_test.go @@ -0,0 +1,66 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +var c client.Client + +func TestMain(m *testing.M) { + // Setup the test environment. + testEnv, restCfg, err := startEnv() + if err != nil { + panic(fmt.Sprintf("Failed to start testenv: %v", err)) + } + + _, cancel := context.WithCancel(ctrl.SetupSignalHandler()) + defer func() { + cancel() + if err := testEnv.Stop(); err != nil { + panic(fmt.Sprintf("Failed to stop testenv: %v", err)) + } + }() + + c, err = client.New(restCfg, client.Options{}) + if err != nil { + panic(fmt.Sprintf("Error initializing client: %v", err)) + } + _ = egv1a1.AddToScheme(c.Scheme()) + + os.Exit(m.Run()) +} + +func startEnv() (*envtest.Environment, *rest.Config, error) { + log.SetLogger(zap.New(zap.WriteTo(os.Stderr), zap.UseDevMode(true))) + egAPIs := filepath.Join("..", "..", "charts", "gateway-helm", "crds", "generated") + + env := &envtest.Environment{ + CRDDirectoryPaths: []string{egAPIs}, + } + cfg, err := env.Start() + if err != nil { + return env, nil, err + } + return env, cfg, nil +} diff --git a/test/cel-validation/securitypolicy_test.go b/test/cel-validation/securitypolicy_test.go new file mode 100644 index 00000000000..7b634f2f1e5 --- /dev/null +++ b/test/cel-validation/securitypolicy_test.go @@ -0,0 +1,171 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestSecurityPolicyTarget(t *testing.T) { + ctx := context.Background() + baseSP := egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sp", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.SecurityPolicySpec{}, + } + + sectionName := gwapiv1a2.SectionName("foo") + + cases := []struct { + desc string + mutate func(sp *egv1a1.SecurityPolicy) + mutateStatus func(sp *egv1a1.SecurityPolicy) + wantErrors []string + }{ + { + desc: "valid targetRef", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "sectionName disabled until supported", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + SectionName: §ionName, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy does not yet support the sectionName field", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + sp := baseSP.DeepCopy() + sp.Name = fmt.Sprintf("sp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(sp) + } + err := c.Create(ctx, sp) + + if tc.mutateStatus != nil { + tc.mutateStatus(sp) + err = c.Status().Update(ctx, sp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating SecurityPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.wantErrors { + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating SecurityPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} diff --git a/test/config/gatewayclass.yaml b/test/config/gatewayclass.yaml index 5f43cb897e0..24985fa0874 100644 --- a/test/config/gatewayclass.yaml +++ b/test/config/gatewayclass.yaml @@ -1,16 +1,16 @@ kind: GatewayClass -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 metadata: name: envoy-gateway spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: - group: config.gateway.envoyproxy.io + group: gateway.envoyproxy.io kind: EnvoyProxy name: proxy-config namespace: envoy-gateway-system --- -apiVersion: config.gateway.envoyproxy.io/v1alpha1 +apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: proxy-config diff --git a/test/conformance/conformance_test.go b/test/conformance/conformance_test.go index 806a74be70a..149909e2ce4 100644 --- a/test/conformance/conformance_test.go +++ b/test/conformance/conformance_test.go @@ -16,6 +16,7 @@ import ( "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" + v1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1beta1" "sigs.k8s.io/gateway-api/conformance/tests" @@ -37,6 +38,7 @@ func TestGatewayAPIConformance(t *testing.T) { require.NoError(t, v1alpha2.AddToScheme(client.Scheme())) require.NoError(t, v1beta1.AddToScheme(client.Scheme())) + require.NoError(t, v1.AddToScheme(client.Scheme())) cSuite := suite.New(suite.Options{ Client: client, @@ -45,10 +47,10 @@ func TestGatewayAPIConformance(t *testing.T) { Clientset: clientset, CleanupBaseResources: *flags.CleanupBaseResources, SupportedFeatures: suite.AllFeatures, - ExemptFeatures: suite.MeshCoreFeatures, SkipTests: []string{ - tests.HTTPRouteRedirectPortAndScheme.ShortName, + tests.GatewayStaticAddresses.ShortName, }, + ExemptFeatures: suite.MeshCoreFeatures, }) cSuite.Setup(t) cSuite.Run(t, tests.ConformanceTests) diff --git a/test/conformance/experimental_conformance_test.go b/test/conformance/experimental_conformance_test.go new file mode 100644 index 00000000000..348f44a3a4d --- /dev/null +++ b/test/conformance/experimental_conformance_test.go @@ -0,0 +1,139 @@ +//go:build experimental +// +build experimental + +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package conformance + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/yaml" + + v1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + confv1a1 "sigs.k8s.io/gateway-api/conformance/apis/v1alpha1" + "sigs.k8s.io/gateway-api/conformance/tests" + "sigs.k8s.io/gateway-api/conformance/utils/flags" + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +var ( + cfg *rest.Config + k8sClientset *kubernetes.Clientset + mgrClient client.Client + implementation *confv1a1.Implementation + conformanceProfiles sets.Set[suite.ConformanceProfileName] +) + +func TestExperimentalConformance(t *testing.T) { + var err error + cfg, err = config.GetConfig() + if err != nil { + t.Fatalf("Error loading Kubernetes config: %v", err) + } + mgrClient, err = client.New(cfg, client.Options{}) + if err != nil { + t.Fatalf("Error initializing Kubernetes client: %v", err) + } + k8sClientset, err = kubernetes.NewForConfig(cfg) + if err != nil { + t.Fatalf("Error initializing Kubernetes REST client: %v", err) + } + + err = v1alpha2.AddToScheme(mgrClient.Scheme()) + assert.NoError(t, err) + err = v1beta1.AddToScheme(mgrClient.Scheme()) + assert.NoError(t, err) + err = v1.AddToScheme(mgrClient.Scheme()) + assert.NoError(t, err) + + // experimental conformance flags + conformanceProfiles = sets.New( + suite.HTTPConformanceProfileName, + suite.TLSConformanceProfileName, + ) + + // if some conformance profiles have been set, run the experimental conformance suite... + implementation, err = suite.ParseImplementation( + *flags.ImplementationOrganization, + *flags.ImplementationProject, + *flags.ImplementationURL, + *flags.ImplementationVersion, + *flags.ImplementationContact, + ) + if err != nil { + t.Fatalf("Error parsing implementation's details: %v", err) + } + + experimentalConformance(t) +} + +func experimentalConformance(t *testing.T) { + t.Logf("Running experimental conformance tests with %s GatewayClass\n cleanup: %t\n debug: %t\n enable all features: %t \n conformance profiles: [%v]", + *flags.GatewayClassName, *flags.CleanupBaseResources, *flags.ShowDebug, *flags.EnableAllSupportedFeatures, conformanceProfiles) + + cSuite, err := suite.NewExperimentalConformanceTestSuite( + suite.ExperimentalConformanceOptions{ + Options: suite.Options{ + Client: mgrClient, + RestConfig: cfg, + // This clientset is needed in addition to the client only because + // controller-runtime client doesn't support non CRUD sub-resources yet (https://github.com/kubernetes-sigs/controller-runtime/issues/452). + Clientset: k8sClientset, + GatewayClassName: *flags.GatewayClassName, + Debug: *flags.ShowDebug, + CleanupBaseResources: *flags.CleanupBaseResources, + SupportedFeatures: suite.AllFeatures, + SkipTests: []string{ + tests.GatewayStaticAddresses.ShortName, + }, + ExemptFeatures: suite.MeshCoreFeatures, + }, + Implementation: *implementation, + ConformanceProfiles: conformanceProfiles, + }) + if err != nil { + t.Fatalf("error creating experimental conformance test suite: %v", err) + } + + cSuite.Setup(t) + err = cSuite.Run(t, tests.ConformanceTests) + if err != nil { + t.Fatalf("error running conformance profile report: %v", err) + } + report, err := cSuite.Report() + if err != nil { + t.Fatalf("error generating conformance profile report: %v", err) + } + + err = experimentalConformanceReport(t.Logf, *report, *flags.ReportOutput) + assert.NoError(t, err) +} + +func experimentalConformanceReport(logf func(string, ...any), report confv1a1.ConformanceReport, output string) error { + rawReport, err := yaml.Marshal(report) + if err != nil { + return err + } + + if output != "" { + if err = os.WriteFile(output, rawReport, 0600); err != nil { + return err + } + } + logf("Conformance report:\n%s", string(rawReport)) + + return nil +} diff --git a/test/e2e/base/manifests.yaml b/test/e2e/base/manifests.yaml index b89746b8ffb..54450ce73bf 100644 --- a/test/e2e/base/manifests.yaml +++ b/test/e2e/base/manifests.yaml @@ -14,7 +14,7 @@ metadata: labels: gateway-conformance: infra --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: same-namespace @@ -29,7 +29,7 @@ spec: namespaces: from: Same --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: all-namespaces @@ -44,7 +44,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: backend-namespaces @@ -62,6 +62,27 @@ spec: matchLabels: gateway-conformance: backend --- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: my-tcp-gateway + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: foo + protocol: TCP + port: 8080 + allowedRoutes: + kinds: + - kind: TCPRoute + - name: bar + protocol: TCP + port: 8090 + allowedRoutes: + kinds: + - kind: TCPRoute +--- apiVersion: v1 kind: Service metadata: @@ -414,3 +435,89 @@ spec: resources: requests: cpu: 10m +--- +apiVersion: v1 +kind: Namespace +metadata: + name: gateway-conformance-udp + labels: + gateway-conformance: udp +--- +apiVersion: v1 +kind: Service +metadata: + name: coredns + namespace: gateway-conformance-udp + labels: + app: udp +spec: + ports: + - name: udp-dns + port: 53 + protocol: UDP + targetPort: 53 + selector: + app: udp +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coredns + namespace: gateway-conformance-udp + labels: + app: udp +spec: + selector: + matchLabels: + app: udp + template: + metadata: + labels: + app: udp + spec: + containers: + - args: + - -conf + - /root/Corefile + image: coredns/coredns + name: coredns + volumeMounts: + - mountPath: /root + name: conf + volumes: + - configMap: + defaultMode: 420 + name: coredns + name: conf +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: coredns + namespace: gateway-conformance-udp +data: + Corefile: | + .:53 { + forward . 8.8.8.8 9.9.9.9 + log + errors + } + + foo.bar.com:53 { + whoami + } +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: udp-gateway + namespace: gateway-conformance-udp +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: coredns + protocol: UDP + port: 5300 + allowedRoutes: + kinds: + - kind: UDPRoute diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 7ba69f3f252..1c6d5b22f92 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -15,8 +15,8 @@ import ( "github.com/stretchr/testify/require" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/conformance/utils/flags" "sigs.k8s.io/gateway-api/conformance/utils/suite" @@ -33,8 +33,8 @@ func TestE2E(t *testing.T) { client, err := client.New(cfg, client.Options{}) require.NoError(t, err) - require.NoError(t, v1alpha2.AddToScheme(client.Scheme())) - require.NoError(t, v1beta1.AddToScheme(client.Scheme())) + require.NoError(t, gwapiv1a2.AddToScheme(client.Scheme())) + require.NoError(t, gwapiv1.AddToScheme(client.Scheme())) require.NoError(t, egv1a1.AddToScheme(client.Scheme())) t.Logf("Running E2E tests with %s GatewayClass\n cleanup: %t\n debug: %t\n supported features: [%v]\n exempt features: [%v]", diff --git a/test/e2e/testdata/accesslog-file.yaml b/test/e2e/testdata/accesslog-file.yaml index 18d164fc608..a6ab4998c7c 100644 --- a/test/e2e/testdata/accesslog-file.yaml +++ b/test/e2e/testdata/accesslog-file.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: accesslog-file diff --git a/test/e2e/testdata/accesslog-otel.yaml b/test/e2e/testdata/accesslog-otel.yaml index 8f9e4924080..a4a58cc3b37 100644 --- a/test/e2e/testdata/accesslog-otel.yaml +++ b/test/e2e/testdata/accesslog-otel.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: accesslog-otel diff --git a/test/e2e/testdata/basic-auth.yaml b/test/e2e/testdata/basic-auth.yaml new file mode 100644 index 00000000000..036bb788eaa --- /dev/null +++ b/test/e2e/testdata/basic-auth.yaml @@ -0,0 +1,80 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: gateway-conformance-infra + name: basic-auth-users-secret-1 +data: + .htpasswd: "dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=" +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: gateway-conformance-infra + name: basic-auth-users-secret-2 +data: + .htpasswd: "dXNlcjM6e1NIQX1QcitqQWR4WkdXOFlXVHhGNVJrb2VpTXBkWWs9CnVzZXI0OntTSEF9SC9LemNFcnQ0RTdzdFI1UXltbU8vVkNoTjVzPQ==" +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-with-basic-auth-1 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: Exact + value: /basic-auth-1 + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-with-basic-auth-2 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: Exact + value: /basic-auth-2 + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: basic-auth-1 + namespace: gateway-conformance-infra +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-with-basic-auth-1 + namespace: gateway-conformance-infra + basicAuth: + users: + name: "basic-auth-users-secret-1" +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: basic-auth-2 + namespace: gateway-conformance-infra +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-with-basic-auth-2 + namespace: gateway-conformance-infra + basicAuth: + users: + name: "basic-auth-users-secret-2" diff --git a/test/e2e/testdata/cors.yaml b/test/e2e/testdata/cors.yaml new file mode 100644 index 00000000000..1967fc96288 --- /dev/null +++ b/test/e2e/testdata/cors.yaml @@ -0,0 +1,49 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: cors-example + namespace: gateway-conformance-infra +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-with-cors + namespace: gateway-conformance-infra + cors: + allowOrigins: + - type: Exact + value: "https://www.foo.com" + - type: Exact + value: "https://www.bar.com" + - type: RegularExpression + value: "https://[a-zA-Z0-9]+.foobar.com" + allowMethods: + - GET + - POST + - PUT + - PATCH + - DELETE + - OPTIONS + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-with-cors + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /cors + backendRefs: + - name: infra-backend-v1 + port: 8080 diff --git a/test/e2e/testdata/envoy-patch-policy.yaml b/test/e2e/testdata/envoy-patch-policy.yaml index fa63ead7c80..e8f56215278 100644 --- a/test/e2e/testdata/envoy-patch-policy.yaml +++ b/test/e2e/testdata/envoy-patch-policy.yaml @@ -1,5 +1,5 @@ --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-envoy-patch-policy diff --git a/test/e2e/testdata/metric.yaml b/test/e2e/testdata/metric.yaml index 9417bd0c670..2d2c26311dc 100644 --- a/test/e2e/testdata/metric.yaml +++ b/test/e2e/testdata/metric.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: metric-prometheus diff --git a/test/e2e/testdata/prometheus.yaml b/test/e2e/testdata/prometheus.yaml new file mode 100644 index 00000000000..d33db1d2dea --- /dev/null +++ b/test/e2e/testdata/prometheus.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: envoy-gateway-metrics-lb + namespace: envoy-gateway-system + labels: + control-plane: envoy-gateway +spec: + selector: + control-plane: envoy-gateway + app.kubernetes.io/instance: eg + ports: + - name: http-metrics + port: 19001 + protocol: TCP + targetPort: 19001 + type: LoadBalancer diff --git a/test/e2e/testdata/ratelimit-based-jwt-claims.yaml b/test/e2e/testdata/ratelimit-based-jwt-claims.yaml index 5787c3c21ca..d16e9c1ebdb 100644 --- a/test/e2e/testdata/ratelimit-based-jwt-claims.yaml +++ b/test/e2e/testdata/ratelimit-based-jwt-claims.yaml @@ -1,36 +1,47 @@ apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: AuthenticationFilter +kind: SecurityPolicy metadata: name: jwt-example namespace: gateway-conformance-infra spec: - type: JWT - jwtProviders: - - name: example - remoteJWKS: - uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/authn/jwks.json - claimToHeaders: - - claim: name - header: x-claim-name + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-ratelimit-based-jwt-claims + namespace: gateway-conformance-infra + jwt: + providers: + - name: example + remoteJWKS: + uri: https://raw.githubusercontent.com/envoyproxy/gateway/main/examples/kubernetes/jwt/jwks.json + claimToHeaders: + - claim: name + header: x-claim-name --- apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter +kind: BackendTrafficPolicy metadata: name: ratelimit-specific-user namespace: gateway-conformance-infra spec: - type: Global - global: - rules: - - clientSelectors: - - headers: - - name: x-claim-name - value: John Doe - limit: - requests: 3 - unit: Hour + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-ratelimit-based-jwt-claims + namespace: gateway-conformance-infra + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-claim-name + value: John Doe + limit: + requests: 3 + unit: Hour --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-ratelimit-based-jwt-claims @@ -42,21 +53,20 @@ spec: - backendRefs: - name: infra-backend-v1 port: 8080 - filters: - - extensionRef: - group: gateway.envoyproxy.io - kind: AuthenticationFilter - name: jwt-example - type: ExtensionRef - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-specific-user matches: - path: type: PathPrefix value: /foo +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-no-ratelimit + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: - backendRefs: - name: infra-backend-v1 port: 8080 diff --git a/test/e2e/testdata/ratelimit-block-all-ips.yaml b/test/e2e/testdata/ratelimit-block-all-ips.yaml index 67fc95ab3df..9741ad17a8a 100644 --- a/test/e2e/testdata/ratelimit-block-all-ips.yaml +++ b/test/e2e/testdata/ratelimit-block-all-ips.yaml @@ -1,21 +1,27 @@ apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: RateLimitFilter +kind: BackendTrafficPolicy metadata: name: ratelimit-all-ips namespace: gateway-conformance-infra spec: - type: Global - global: - rules: - - clientSelectors: - - sourceCIDR: - value: 0.0.0.0/0 - type: distinct - limit: - requests: 3 - unit: Hour + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-ratelimit + namespace: gateway-conformance-infra + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - sourceCIDR: + value: 0.0.0.0/0 + type: distinct + limit: + requests: 3 + unit: Hour --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-ratelimit @@ -28,12 +34,6 @@ spec: - path: type: PathPrefix value: / - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: RateLimitFilter - name: ratelimit-all-ips backendRefs: - name: infra-backend-v1 port: 8080 diff --git a/test/e2e/testdata/tcp-route.yaml b/test/e2e/testdata/tcp-route.yaml new file mode 100644 index 00000000000..75e7d23c134 --- /dev/null +++ b/test/e2e/testdata/tcp-route.yaml @@ -0,0 +1,27 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TCPRoute +metadata: + name: tcp-app-1 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: my-tcp-gateway + sectionName: foo + rules: + - backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TCPRoute +metadata: + name: tcp-app-2 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: my-tcp-gateway + sectionName: bar + rules: + - backendRefs: + - name: infra-backend-v2 + port: 8080 diff --git a/test/e2e/testdata/tracing-otel.yaml b/test/e2e/testdata/tracing-otel.yaml index b02e4943a12..f5b896a91c8 100644 --- a/test/e2e/testdata/tracing-otel.yaml +++ b/test/e2e/testdata/tracing-otel.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: tracing-otel diff --git a/test/e2e/testdata/udproute.yaml b/test/e2e/testdata/udproute.yaml new file mode 100644 index 00000000000..e5ea8d1244f --- /dev/null +++ b/test/e2e/testdata/udproute.yaml @@ -0,0 +1,13 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: UDPRoute +metadata: + name: udp-coredns + namespace: gateway-conformance-udp +spec: + parentRefs: + - name: udp-gateway + sectionName: coredns + rules: + - backendRefs: + - name: coredns + port: 53 diff --git a/test/e2e/tests/basic-auth.go b/test/e2e/tests/basic-auth.go new file mode 100644 index 00000000000..a5a584d226b --- /dev/null +++ b/test/e2e/tests/basic-auth.go @@ -0,0 +1,185 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e +// +build e2e + +package tests + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func init() { + ConformanceTests = append(ConformanceTests, BasicAuthTest) +} + +var BasicAuthTest = suite.ConformanceTest{ + ShortName: "BasicAuth", + Description: "Resource with BasicAuth enabled", + Manifests: []string{"testdata/basic-auth.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("valid username password", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-basic-auth-1", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "basic-auth-1", Namespace: ns}) + // TODO: We should wait for the `programmed` condition to be true before sending traffic. + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/basic-auth-1", + Headers: map[string]string{ + "Authorization": "Basic dXNlcjE6dGVzdDE=", // user1:test1 + }, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + + t.Run("without Authorization header", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-basic-auth-1", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "basic-auth-1", Namespace: ns}) + // TODO: We should wait for the `programmed` condition to be true before sending traffic. + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/basic-auth-1", + }, + Response: http.Response{ + StatusCode: 401, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + + t.Run("invalid username password", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-basic-auth-1", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "basic-auth-1", Namespace: ns}) + // TODO: We should wait for the `programmed` condition to be true before sending traffic. + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/basic-auth-1", + Headers: map[string]string{ + "Authorization": "Basic dXNlcjE6dGVzdDI=", // user1:test2 + }, + }, + Response: http.Response{ + StatusCode: 401, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + + t.Run("per route configuration second route", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-basic-auth-2", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "basic-auth-2", Namespace: ns}) + // TODO: We should wait for the `programmed` condition to be true before sending traffic. + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/basic-auth-2", + Headers: map[string]string{ + "Authorization": "Basic dXNlcjQ6dGVzdDQ=", // user4:test4 + }, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + }, +} + +// SecurityPolicyMustBeAccepted waits for the specified SecurityPolicy to be accepted. +func SecurityPolicyMustBeAccepted( + t *testing.T, + client client.Client, + securityPolicyName types.NamespacedName) { + t.Helper() + + waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) { + securityPolicy := &egv1a1.SecurityPolicy{} + err := client.Get(ctx, securityPolicyName, securityPolicy) + if err != nil { + return false, fmt.Errorf("error fetching SecurityPolicy: %w", err) + } + + for _, condition := range securityPolicy.Status.Conditions { + if condition.Type == string(gwv1a2.PolicyConditionAccepted) && condition.Status == metav1.ConditionTrue { + return true, nil + } + } + t.Logf("SecurityPolicy not yet accepted: %v", securityPolicy) + return false, nil + }) + require.NoErrorf(t, waitErr, "error waiting for HTTPRoute to have parents matching expectations") +} diff --git a/test/e2e/tests/controlplane.go b/test/e2e/tests/controlplane.go new file mode 100644 index 00000000000..7149c2546b6 --- /dev/null +++ b/test/e2e/tests/controlplane.go @@ -0,0 +1,76 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e +// +build e2e + +package tests + +import ( + "context" + "testing" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, ControlPlaneMetricTest) +} + +var ControlPlaneMetricTest = suite.ConformanceTest{ + ShortName: "ControlPlane", + Description: "Make sure control plane prometheus endpoint is working", + Manifests: []string{"testdata/prometheus.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("Prometheus", func(t *testing.T) { + nn := types.NamespacedName{Name: "envoy-gateway-metrics-lb", Namespace: "envoy-gateway-system"} + if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Minute, true, + func(_ context.Context) (done bool, err error) { + svc := corev1.Service{} + if err := suite.Client.Get(context.Background(), nn, &svc); err != nil { + return false, nil + } + + host := "" + switch svc.Spec.Type { + case corev1.ServiceTypeLoadBalancer: + for _, ing := range svc.Status.LoadBalancer.Ingress { + if ing.IP != "" { + host = ing.IP + break + } + } + default: + // do nothing + } + + if host == "" { + return false, nil + } + + return true, nil + }); err != nil { + t.Errorf("failed to get service %s : %v", nn.String(), err) + } + + // too much flakes in the test if timeout is 1 minute + // this should not take so long, but we give it a long timeout to be safe, and poll every second + if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, 2*time.Minute, true, + func(_ context.Context) (done bool, err error) { + if err := ScrapeMetrics(t, suite.Client, nn, 19001, "/metrics"); err != nil { + t.Logf("failed to get metric: %v", err) + return false, nil + } + return true, nil + }); err != nil { + t.Errorf("failed to scrape metrics: %v", err) + } + }) + }, +} diff --git a/test/e2e/tests/cors.go b/test/e2e/tests/cors.go new file mode 100644 index 00000000000..4b4e54bcc8f --- /dev/null +++ b/test/e2e/tests/cors.go @@ -0,0 +1,128 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e +// +build e2e + +package tests + +import ( + "testing" + + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, CorsTest) +} + +var CorsTest = suite.ConformanceTest{ + ShortName: "Cors", + Description: "Resource with CORS enabled", + Manifests: []string{"testdata/cors.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("should enable cors with Allow Origin Exact", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-cors", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/cors", + Headers: map[string]string{ + "Origin": "https://www.foo.com", + }, + }, + Response: http.Response{ + StatusCode: 200, + Headers: map[string]string{ + "access-control-allow-origin": "https://www.foo.com", + "access-control-expose-headers": "x-header-3, x-header-4", + }, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + + t.Run("should enable cors with Allow Origin Regex", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-cors", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/cors", + Method: "OPTIONS", + Headers: map[string]string{ + "Origin": "https://anydomain.foobar.com", + }, + }, + Response: http.Response{ + StatusCode: 200, + Headers: map[string]string{ + "access-control-allow-origin": "https://anydomain.foobar.com", + "access-control-expose-headers": "x-header-3, x-header-4", + }, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + + t.Run("should not contain cors headers when Origin not registered", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-with-cors", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/cors", + Headers: map[string]string{ + "Origin": "https://unknown.foo.com", + }, + }, + Response: http.Response{ + AbsentHeaders: []string{"access-control-allow-origin", "access-control-expose-headers"}, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + }) + }, +} diff --git a/test/e2e/tests/metric.go b/test/e2e/tests/metric.go index 0f312dc07b5..2c9a8c296c3 100644 --- a/test/e2e/tests/metric.go +++ b/test/e2e/tests/metric.go @@ -59,7 +59,7 @@ var MetricTest = suite.ConformanceTest{ if err := ScrapeMetrics(t, suite.Client, types.NamespacedName{ Namespace: "envoy-gateway-system", Name: "same-namespace-gw-metrics", - }, "/stats/prometheus"); err != nil { + }, 19001, "/stats/prometheus"); err != nil { t.Logf("failed to get metric: %v", err) return false, nil } @@ -93,7 +93,7 @@ var MetricTest = suite.ConformanceTest{ if err := ScrapeMetrics(t, suite.Client, types.NamespacedName{ Namespace: "monitoring", Name: "otel-collecot-prometheus", - }, "/metrics"); err != nil { + }, 19001, "/metrics"); err != nil { t.Logf("failed to get metric: %v", err) return false, nil } @@ -105,19 +105,25 @@ var MetricTest = suite.ConformanceTest{ }, } -func ScrapeMetrics(t *testing.T, c client.Client, nn types.NamespacedName, path string) error { +func ScrapeMetrics(t *testing.T, c client.Client, nn types.NamespacedName, port int32, path string) error { svc := corev1.Service{} if err := c.Get(context.Background(), nn, &svc); err != nil { return err } host := "" - for _, ing := range svc.Status.LoadBalancer.Ingress { - if ing.IP != "" { - host = ing.IP - break + switch svc.Spec.Type { + case corev1.ServiceTypeLoadBalancer: + for _, ing := range svc.Status.LoadBalancer.Ingress { + if ing.IP != "" { + host = ing.IP + break + } } + default: + host = fmt.Sprintf("%s.%s.svc", nn.Name, nn.Namespace) } - url := fmt.Sprintf("http://%s:19001%s", host, path) + + url := fmt.Sprintf("http://%s:%d%s", host, port, path) t.Logf("try to request: %s", url) httpClient := http.Client{ diff --git a/test/e2e/tests/tcp-route.go b/test/e2e/tests/tcp-route.go new file mode 100644 index 00000000000..0aa8b609e79 --- /dev/null +++ b/test/e2e/tests/tcp-route.go @@ -0,0 +1,187 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +// This file contains code derived from upstream gateway-api, it will be moved to upstream. + +//go:build e2e +// +build e2e + +package tests + +import ( + "context" + "fmt" + "net" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/gateway-api/conformance/utils/config" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func init() { + ConformanceTests = append(ConformanceTests, TCPRouteTest) +} + +var TCPRouteTest = suite.ConformanceTest{ + ShortName: "TCPRoute", + Description: "Testing TCP Route", + Manifests: []string{"testdata/tcp-route.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("tcp-route-1", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "tcp-app-1", Namespace: ns} + gwNN := types.NamespacedName{Name: "my-tcp-gateway", Namespace: ns} + gwAddr := GatewayAndTCPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, NewGatewayRef(gwNN), routeNN) + OkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/", + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + // Send a request to an valid path and expect a successful response + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp) + }) + t.Run("tcp-route-2", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "tcp-app-2", Namespace: ns} + gwNN := types.NamespacedName{Name: "my-tcp-gateway", Namespace: ns} + gwAddr := GatewayAndTCPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, NewGatewayRef(gwNN), routeNN) + OkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/", + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + // Send a request to an valid path and expect a successful response + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, OkResp) + }) + + }, +} + +func GatewayAndTCPRoutesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, controllerName string, gw GatewayRef, routeNNs ...types.NamespacedName) string { + t.Helper() + + tcpRoute := &v1alpha2.TCPRoute{} + err := c.Get(context.Background(), routeNNs[0], tcpRoute) + if err != nil { + t.Logf("error fetching TCPRoute: %v", err) + } + + gwAddr, err := WaitForGatewayAddress(t, c, timeoutConfig, gw.NamespacedName, string(*tcpRoute.Spec.ParentRefs[0].SectionName)) + require.NoErrorf(t, err, "timed out waiting for Gateway address to be assigned") + + ns := gatewayv1.Namespace(gw.Namespace) + kind := gatewayv1.Kind("Gateway") + + for _, routeNN := range routeNNs { + namespaceRequired := true + if routeNN.Namespace == gw.Namespace { + namespaceRequired = false + } + + var parents []gatewayv1.RouteParentStatus + for _, listener := range gw.listenerNames { + parents = append(parents, gatewayv1.RouteParentStatus{ + ParentRef: gatewayv1.ParentReference{ + Group: (*gatewayv1.Group)(&gatewayv1.GroupVersion.Group), + Kind: &kind, + Name: gatewayv1.ObjectName(gw.Name), + Namespace: &ns, + SectionName: listener, + }, + ControllerName: gatewayv1.GatewayController(controllerName), + Conditions: []metav1.Condition{{ + Type: string(gatewayv1.RouteConditionAccepted), + Status: metav1.ConditionTrue, + Reason: string(gatewayv1.RouteReasonAccepted), + }}, + }) + } + TCPRouteMustHaveParents(t, c, timeoutConfig, routeNN, parents, namespaceRequired) + } + + return gwAddr + +} + +// WaitForGatewayAddress waits until at least one IP Address has been set in the +// status of the specified Gateway. +func WaitForGatewayAddress(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, gwName types.NamespacedName, sectionName string) (string, error) { + t.Helper() + + var ipAddr, port string + waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.GatewayMustHaveAddress, true, func(ctx context.Context) (bool, error) { + gw := &gatewayv1.Gateway{} + err := client.Get(ctx, gwName, gw) + if err != nil { + t.Logf("error fetching Gateway: %v", err) + return false, fmt.Errorf("error fetching Gateway: %w", err) + } + + if err := kubernetes.ConditionsHaveLatestObservedGeneration(gw, gw.Status.Conditions); err != nil { + t.Log("Gateway", err) + return false, nil + } + if sectionName != "" { + for i, val := range gw.Spec.Listeners { + if val.Name == gatewayv1.SectionName(sectionName) { + port = strconv.FormatInt(int64(gw.Spec.Listeners[i].Port), 10) + } + } + } else { + port = strconv.FormatInt(int64(gw.Spec.Listeners[0].Port), 10) + } + + // TODO: Support more than IPAddress + for _, address := range gw.Status.Addresses { + if address.Type != nil && *address.Type == gatewayv1.IPAddressType { + ipAddr = address.Value + return true, nil + } + } + + return false, nil + }) + require.NoErrorf(t, waitErr, "error waiting for Gateway to have at least one IP address in status") + return net.JoinHostPort(ipAddr, port), waitErr +} + +func TCPRouteMustHaveParents(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeName types.NamespacedName, parents []gatewayv1.RouteParentStatus, namespaceRequired bool) { + t.Helper() + + var actual []gatewayv1.RouteParentStatus + waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.RouteMustHaveParents, true, func(ctx context.Context) (bool, error) { + route := &v1alpha2.TCPRoute{} + err := client.Get(ctx, routeName, route) + if err != nil { + return false, fmt.Errorf("error fetching HTTPRoute: %w", err) + } + + actual = route.Status.Parents + return parentsForRouteMatch(t, routeName, parents, actual, namespaceRequired), nil + }) + require.NoErrorf(t, waitErr, "error waiting for TCPRoute to have parents matching expectations") +} diff --git a/test/e2e/tests/udproute.go b/test/e2e/tests/udproute.go new file mode 100644 index 00000000000..3961727fb56 --- /dev/null +++ b/test/e2e/tests/udproute.go @@ -0,0 +1,236 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +// This file contains code derived from upstream gateway-api, it will be moved to upstream. + +//go:build e2e +// +build e2e + +package tests + +import ( + "context" + "fmt" + "reflect" + "testing" + "time" + + "github.com/miekg/dns" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/gateway-api/conformance/utils/config" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, UDPRouteTest) +} + +var UDPRouteTest = suite.ConformanceTest{ + ShortName: "UDPRoute", + Description: "Make sure UDPRoute is working", + Manifests: []string{"testdata/udproute.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("Simple UDP request matching UDPRoute should reach coredns backend", func(t *testing.T) { + namespace := "gateway-conformance-udp" + domain := "foo.bar.com." + routeNN := types.NamespacedName{Name: "udp-coredns", Namespace: namespace} + gwNN := types.NamespacedName{Name: "udp-gateway", Namespace: namespace} + gwAddr := GatewayAndUDPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, NewGatewayRef(gwNN), routeNN) + + msg := new(dns.Msg) + msg.SetQuestion(domain, dns.TypeA) + + if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Minute, true, + func(_ context.Context) (done bool, err error) { + t.Logf("performing DNS query %s on %s", domain, gwAddr) + _, err = dns.Exchange(msg, gwAddr) + if err != nil { + t.Logf("failed to perform a UDP query: %v", err) + return false, nil + } + return true, nil + }); err != nil { + t.Errorf("failed to perform DNS query: %v", err) + } + }) + }, +} + +// GatewayRef is a tiny type for specifying a UDP Route ParentRef without +// relying on a specific api version. +type GatewayRef struct { + types.NamespacedName + listenerNames []*gatewayv1.SectionName +} + +// NewGatewayRef creates a GatewayRef resource. ListenerNames are optional. +func NewGatewayRef(nn types.NamespacedName, listenerNames ...string) GatewayRef { + var listeners []*gatewayv1.SectionName + + if len(listenerNames) == 0 { + listenerNames = append(listenerNames, "") + } + + for _, listener := range listenerNames { + sectionName := gatewayv1.SectionName(listener) + listeners = append(listeners, §ionName) + } + return GatewayRef{ + NamespacedName: nn, + listenerNames: listeners, + } +} + +// GatewayAndUDPRoutesMustBeAccepted waits until the specified Gateway has an IP +// address assigned to it and the UDPRoute has a ParentRef referring to the +// Gateway. The test will fail if these conditions are not met before the +// timeouts. +func GatewayAndUDPRoutesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, controllerName string, gw GatewayRef, routeNNs ...types.NamespacedName) string { + t.Helper() + + gwAddr, err := kubernetes.WaitForGatewayAddress(t, c, timeoutConfig, gw.NamespacedName) + require.NoErrorf(t, err, "timed out waiting for Gateway address to be assigned") + + ns := gatewayv1.Namespace(gw.Namespace) + kind := gatewayv1.Kind("Gateway") + + for _, routeNN := range routeNNs { + namespaceRequired := true + if routeNN.Namespace == gw.Namespace { + namespaceRequired = false + } + + var parents []gatewayv1.RouteParentStatus + for _, listener := range gw.listenerNames { + parents = append(parents, gatewayv1.RouteParentStatus{ + ParentRef: gatewayv1.ParentReference{ + Group: (*gatewayv1.Group)(&gatewayv1.GroupVersion.Group), + Kind: &kind, + Name: gatewayv1.ObjectName(gw.Name), + Namespace: &ns, + SectionName: listener, + }, + ControllerName: gatewayv1.GatewayController(controllerName), + Conditions: []metav1.Condition{ + { + Type: string(gatewayv1.RouteConditionAccepted), + Status: metav1.ConditionTrue, + Reason: string(gatewayv1.RouteReasonAccepted), + }, + }, + }) + } + UDPRouteMustHaveParents(t, c, timeoutConfig, routeNN, parents, namespaceRequired) + } + + return gwAddr +} + +func UDPRouteMustHaveParents(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeName types.NamespacedName, parents []gatewayv1.RouteParentStatus, namespaceRequired bool) { + t.Helper() + + var actual []gatewayv1.RouteParentStatus + waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.RouteMustHaveParents, true, func(ctx context.Context) (bool, error) { + route := &v1alpha2.UDPRoute{} + err := client.Get(ctx, routeName, route) + if err != nil { + return false, fmt.Errorf("error fetching UDPRoute: %w", err) + } + + actual = route.Status.Parents + return parentsForRouteMatch(t, routeName, parents, actual, namespaceRequired), nil + }) + require.NoErrorf(t, waitErr, "error waiting for UDPRoute to have parents matching expectations") +} + +func parentsForRouteMatch(t *testing.T, routeName types.NamespacedName, expected, actual []gatewayv1.RouteParentStatus, namespaceRequired bool) bool { + t.Helper() + + if len(expected) != len(actual) { + t.Logf("Route %s/%s expected %d Parents got %d", routeName.Namespace, routeName.Name, len(expected), len(actual)) + return false + } + + for i, expectedParent := range expected { + actualParent := actual[i] + if actualParent.ControllerName != expectedParent.ControllerName { + t.Logf("Route %s/%s ControllerName doesn't match", routeName.Namespace, routeName.Name) + return false + } + if !reflect.DeepEqual(actualParent.ParentRef.Group, expectedParent.ParentRef.Group) { + t.Logf("Route %s/%s expected ParentReference.Group to be %v, got %v", routeName.Namespace, routeName.Name, expectedParent.ParentRef.Group, actualParent.ParentRef.Group) + return false + } + if !reflect.DeepEqual(actualParent.ParentRef.Kind, expectedParent.ParentRef.Kind) { + t.Logf("Route %s/%s expected ParentReference.Kind to be %v, got %v", routeName.Namespace, routeName.Name, expectedParent.ParentRef.Kind, actualParent.ParentRef.Kind) + return false + } + if actualParent.ParentRef.Name != expectedParent.ParentRef.Name { + t.Logf("Route %s/%s ParentReference.Name doesn't match", routeName.Namespace, routeName.Name) + return false + } + if !reflect.DeepEqual(actualParent.ParentRef.Namespace, expectedParent.ParentRef.Namespace) { + if namespaceRequired || actualParent.ParentRef.Namespace != nil { + t.Logf("Route %s/%s expected ParentReference.Namespace to be %v, got %v", routeName.Namespace, routeName.Name, expectedParent.ParentRef.Namespace, actualParent.ParentRef.Namespace) + return false + } + } + if !conditionsMatch(t, expectedParent.Conditions, actualParent.Conditions) { + return false + } + } + + t.Logf("Route %s/%s Parents matched expectations", routeName.Namespace, routeName.Name) + return true +} + +func conditionsMatch(t *testing.T, expected, actual []metav1.Condition) bool { + t.Helper() + + if len(actual) < len(expected) { + t.Logf("Expected more conditions to be present") + return false + } + for _, condition := range expected { + if !findConditionInList(t, actual, condition.Type, string(condition.Status), condition.Reason) { + return false + } + } + + t.Logf("Conditions matched expectations") + return true +} + +// findConditionInList finds a condition in a list of Conditions, checking +// the Name, Value, and Reason. If an empty reason is passed, any Reason will match. +// If an empty status is passed, any Status will match. +func findConditionInList(t *testing.T, conditions []metav1.Condition, condName, expectedStatus, expectedReason string) bool { + t.Helper() + + for _, cond := range conditions { + if cond.Type == condName { + // an empty Status string means "Match any status". + if expectedStatus == "" || cond.Status == metav1.ConditionStatus(expectedStatus) { + // an empty Reason string means "Match any reason". + if expectedReason == "" || cond.Reason == expectedReason { + return true + } + t.Logf("%s condition Reason set to %s, expected %s", condName, cond.Reason, expectedReason) + } + + t.Logf("%s condition set to Status %s with Reason %v, expected Status %s", condName, cond.Status, cond.Reason, expectedStatus) + } + } + + t.Logf("%s was not in conditions list [%v]", condName, conditions) + return false +} diff --git a/tools/crd-ref-docs/config.yaml b/tools/crd-ref-docs/config.yaml index ef9d10c0b9f..cd95e3d8d4a 100644 --- a/tools/crd-ref-docs/config.yaml +++ b/tools/crd-ref-docs/config.yaml @@ -1,7 +1,7 @@ processor: # RE2 regular expressions describing types that should be excluded from the generated documentation. ignoreTypes: - - "(EnvoyProxy|AuthenticationFilter|RateLimitFilter)List$" + - "(EnvoyProxy)List$" # RE2 regular expressions describing type fields that should be excluded from the generated documentation. ignoreFields: - "status$" @@ -12,5 +12,5 @@ render: kubernetesVersion: 1.26 knownTypes: - name: SecretObjectReference - package: sigs.k8s.io/gateway-api/apis/v1beta1 - link: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference + package: sigs.k8s.io/gateway-api/apis/v1 + link: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference diff --git a/tools/github-actions/setup-deps/action.yaml b/tools/github-actions/setup-deps/action.yaml index 22e3e557b8a..31974e29d12 100644 --- a/tools/github-actions/setup-deps/action.yaml +++ b/tools/github-actions/setup-deps/action.yaml @@ -6,5 +6,5 @@ runs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.20.x + go-version: 1.21.x cache: true diff --git a/tools/hack/create-cluster.sh b/tools/hack/create-cluster.sh index b4e20e66876..a78adaffd57 100755 --- a/tools/hack/create-cluster.sh +++ b/tools/hack/create-cluster.sh @@ -4,8 +4,8 @@ set -euo pipefail # Setup default values CLUSTER_NAME=${CLUSTER_NAME:-"envoy-gateway"} -METALLB_VERSION=${METALLB_VERSION:-"v0.13.7"} -KIND_NODE_TAG=${KIND_NODE_TAG:-"v1.26.0"} +METALLB_VERSION=${METALLB_VERSION:-"v0.13.10"} +KIND_NODE_TAG=${KIND_NODE_TAG:-"v1.28.0"} ## Create kind cluster. if [[ -z "${KIND_NODE_TAG}" ]]; then diff --git a/tools/hack/docs-headings.sh b/tools/hack/docs-headings.sh index 7311c46092e..4f61928065b 100755 --- a/tools/hack/docs-headings.sh +++ b/tools/hack/docs-headings.sh @@ -1,26 +1,21 @@ #!/usr/bin/env bash -set -o errexit -set -o nounset -set -o pipefail +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi -# Wrap sed to deal with GNU and BSD sed flags. -run::sed() { - if sed --version &1 | grep -q GNU; then - # GNU sed - sed -i "$@" - else - # assume BSD sed - sed -i '' "$@" - fi -} +input_file=$1 + +temp_file=$(mktemp) -files=(docs/latest/api/config_types.md docs/latest/api/extension_types.md) +sed -n ' +/^# / { + s/^# \(.*\)/+++\ntitle = "\1"\n+++\n/ + p + d +} +p +' "$input_file" > "$temp_file" -# Required since Sphinx mst does not link to h4 headings. -for file in "${files[@]}" ; do - run::sed \ - "-es|####|##|" \ - "$file" - echo "updated markdown headings for $file" -done +mv "$temp_file" "$input_file" diff --git a/tools/linter/codespell/.codespell.ignorewords b/tools/linter/codespell/.codespell.ignorewords index 254c60d129e..e773f85868e 100644 --- a/tools/linter/codespell/.codespell.ignorewords +++ b/tools/linter/codespell/.codespell.ignorewords @@ -1,4 +1,4 @@ keypair keypairs als -requestor \ No newline at end of file +requestor diff --git a/tools/linter/golangci-lint/.golangci.yml b/tools/linter/golangci-lint/.golangci.yml index ac65207238c..56f8ee1278e 100644 --- a/tools/linter/golangci-lint/.golangci.yml +++ b/tools/linter/golangci-lint/.golangci.yml @@ -28,6 +28,8 @@ linters-settings: desc: "use sigs.k8s.io/yaml instead" - pkg: gopkg.in/yaml.v3 desc: "use sigs.k8s.io/yaml instead" + - pkg: k8s.io/utils/pointer + desc: "use k8s.io/utils/ptr instead" gci: sections: # Captures all standard packages if they do not match another section. diff --git a/tools/make/docs.mk b/tools/make/docs.mk index 2496cc8c3d9..9a78e3032e3 100644 --- a/tools/make/docs.mk +++ b/tools/make/docs.mk @@ -1,20 +1,14 @@ -DOCS_OUTPUT_DIR := docs/html +DOCS_OUTPUT_DIR := site/public RELEASE_VERSIONS ?= $(foreach v,$(wildcard ${ROOT_DIR}/docs/*),$(notdir ${v})) ##@ Docs .PHONY: docs -docs: docs.clean $(tools/sphinx-build) docs-api helm-readme-gen ## Generate Envoy Gateway Docs Sources +docs: docs.clean helm-readme-gen docs-api docs-api-headings ## Generate Envoy Gateway Docs Sources @$(LOG_TARGET) - mkdir -p $(DOCS_OUTPUT_DIR) - cp docs/index.html $(DOCS_OUTPUT_DIR)/index.html - cp tools/hack/get-egctl.sh $(DOCS_OUTPUT_DIR)/get-egctl.sh - @for VERSION in $(RELEASE_VERSIONS); do \ - env BUILD_VERSION=$$VERSION \ - ENVOY_PROXY_VERSION=$(shell go run ./cmd/envoy-gateway versions -o json | jq -r ".envoyProxyVersion") \ - GATEWAYAPI_VERSION=$(shell go run ./cmd/envoy-gateway versions -o json | jq -r ".gatewayAPIVersion") \ - $(tools/sphinx-build) -j auto -b html docs/$$VERSION $(DOCS_OUTPUT_DIR)/$$VERSION; \ - done + cd $(ROOT_DIR)/site && npm install + cd $(ROOT_DIR)/site && npm run build:production + cp tools/hack/get-egctl.sh $(DOCS_OUTPUT_DIR) .PHONY: docs-release docs-release: docs-release-prepare docs-release-gen docs ## Generate Envoy Gateway Release Docs @@ -22,7 +16,7 @@ docs-release: docs-release-prepare docs-release-gen docs ## Generate Envoy Gate .PHONY: docs-serve docs-serve: ## Start Envoy Gateway Site Locally @$(LOG_TARGET) - python3 -m http.server -d $(DOCS_OUTPUT_DIR) + cd $(ROOT_DIR)/site && npm run serve .PHONY: clean clean: ## Remove all files that are created during builds. @@ -32,35 +26,35 @@ clean: docs.clean docs.clean: @$(LOG_TARGET) rm -rf $(DOCS_OUTPUT_DIR) + rm -rf site/node_modules + rm -rf site/resources + rm -f site/package-lock.json + rm -f site/.hugo_build.lock .PHONY: docs-api -docs-api: docs-api-gen docs-api-headings +docs-api: docs-api-gen helm-readme-gen docs-api-headings .PHONY: helm-readme-gen helm-readme-gen: $(tools/helm-docs) @$(LOG_TARGET) $(tools/helm-docs) charts/gateway-helm/ -f values.tmpl.yaml -o api.md - mv charts/gateway-helm/api.md docs/latest/helm/api.md + mv charts/gateway-helm/api.md site/content/en/latest/install/api.md .PHONY: docs-api-gen docs-api-gen: $(tools/crd-ref-docs) - $(tools/crd-ref-docs) \ - --source-path=api/config \ - --config=tools/crd-ref-docs/config.yaml \ - --output-path=docs/latest/api/config_types.md \ - --max-depth 10 \ - --renderer=markdown + @$(LOG_TARGET) $(tools/crd-ref-docs) \ --source-path=api/v1alpha1 \ --config=tools/crd-ref-docs/config.yaml \ - --output-path=docs/latest/api/extension_types.md \ + --output-path=site/content/en/latest/api/extension_types.md \ --max-depth 10 \ --renderer=markdown .PHONY: docs-api-headings # Required since sphinx mst does not link to h4 headings. docs-api-headings: @$(LOG_TARGET) - tools/hack/docs-headings.sh + tools/hack/docs-headings.sh site/content/en/latest/api/extension_types.md + tools/hack/docs-headings.sh site/content/en/latest/install/api.md .PHONY: docs-release-prepare docs-release-prepare: @@ -68,18 +62,20 @@ docs-release-prepare: mkdir -p $(OUTPUT_DIR) @$(call log, "Updated Release Version: $(TAG)") $(eval LAST_VERSION := $(shell cat VERSION)) - cat docs/index.html | sed "s;$(LAST_VERSION);$(TAG);g" > $(OUTPUT_DIR)/index.html - mv $(OUTPUT_DIR)/index.html docs/index.html echo $(TAG) > VERSION .PHONY: docs-release-gen docs-release-gen: @$(LOG_TARGET) - @$(call log, "Added Release Doc: docs/$(TAG)") - cp -r docs/latest docs/$(TAG) - @for DOC in $(shell ls docs/latest/user); do \ - cp docs/$(TAG)/user/$$DOC $(OUTPUT_DIR)/$$DOC ; \ + @$(call log, "Added Release Doc: site/content/en/$(TAG)") + cp -r site/content/en/latest site/content/en/$(TAG) + @for DOC in $(shell ls site/content/en/latest/user); do \ + cp site/content/en/$(TAG)/user/$$DOC $(OUTPUT_DIR)/$$DOC ; \ cat $(OUTPUT_DIR)/$$DOC | sed "s;v0.0.0-latest;$(TAG);g" | sed "s;latest;$(TAG);g" > $(OUTPUT_DIR)/$(TAG)-$$DOC ; \ - mv $(OUTPUT_DIR)/$(TAG)-$$DOC docs/$(TAG)/user/$$DOC ; \ - $(call log, "Updated: docs/$(TAG)/user/$$DOC") ; \ + mv $(OUTPUT_DIR)/$(TAG)-$$DOC site/content/en/$(TAG)/user/$$DOC ; \ + $(call log, "Updated: site/content/en/$(TAG)/user/$$DOC") ; \ done + + @echo '[[params.versions]]' >> site/hugo.toml + @echo ' version = "$(TAG)"' >> site/hugo.toml + @echo ' url = "/$(TAG)"' >> site/hugo.toml diff --git a/tools/make/golang.mk b/tools/make/golang.mk index 4cf7d4e3bbb..4cfd8c5ebc8 100644 --- a/tools/make/golang.mk +++ b/tools/make/golang.mk @@ -54,7 +54,8 @@ go.testdata.complete: ## Override test ouputdata .PHONY: go.test.coverage go.test.coverage: $(tools/setup-envtest) ## Run go unit and integration tests in GitHub Actions @$(LOG_TARGET) - KUBEBUILDER_ASSETS="$(shell $(tools/setup-envtest) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... --tags=integration -race -coverprofile=coverage.xml -covermode=atomic + KUBEBUILDER_ASSETS="$(shell $(tools/setup-envtest) use $(ENVTEST_K8S_VERSION) -p path)" \ + go test ./... --tags=integration,celvalidation -race -coverprofile=coverage.xml -covermode=atomic .PHONY: go.clean go.clean: ## Clean the building output files diff --git a/tools/make/helm.mk b/tools/make/helm.mk index d0b3ec2a863..86b943fe4e9 100644 --- a/tools/make/helm.mk +++ b/tools/make/helm.mk @@ -25,11 +25,6 @@ helm-install: helm-generate ## Install envoy gateway helm chart from OCI registr @$(LOG_TARGET) helm install eg ${OCI_REGISTRY}/${CHART_NAME} --version ${CHART_VERSION} -n envoy-gateway-system --create-namespace -helm-release: -helm-release: ## Package envoy gateway helm chart for release -helm-release: helm-generate - @$(LOG_TARGET) - helm package charts/${CHART_NAME} --app-version ${TAG} --version ${CHART_VERSION} --destination ${OUTPUT_DIR}/charts/ - helm-generate: ImageRepository=${IMAGE} ImageTag=${TAG} envsubst < charts/gateway-helm/values.tmpl.yaml > ./charts/gateway-helm/values.yaml + helm lint charts/gateway-helm diff --git a/tools/make/kube.mk b/tools/make/kube.mk index 98b2e1a6f81..354e4730edd 100644 --- a/tools/make/kube.mk +++ b/tools/make/kube.mk @@ -1,5 +1,5 @@ # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION ?= 1.24.1 +ENVTEST_K8S_VERSION ?= 1.27.1 # GATEWAY_API_VERSION refers to the version of Gateway API CRDs. # For more details, see https://gateway-api.sigs.k8s.io/guides/getting-started/#installing-gateway-api GATEWAY_API_VERSION ?= $(shell go list -m -f '{{.Version}}' sigs.k8s.io/gateway-api) @@ -9,7 +9,7 @@ GATEWAY_RELEASE_URL ?= https://github.com/kubernetes-sigs/gateway-api/releases/d WAIT_TIMEOUT ?= 15m FLUENT_BIT_CHART_VERSION ?= 0.30.4 -OTEL_COLLECTOR_CHART_VERSION ?= 0.60.0 +OTEL_COLLECTOR_CHART_VERSION ?= 0.73.1 TEMPO_CHART_VERSION ?= 1.3.1 # Set Kubernetes Resources Directory Path @@ -28,11 +28,9 @@ YEAR := $(shell date +%Y) CONTROLLERGEN_OBJECT_FLAGS := object:headerFile="$(ROOT_DIR)/tools/boilerplate/boilerplate.generatego.txt",year=$(YEAR) .PHONY: manifests -manifests: $(tools/controller-gen) generate-gwapi-manifests ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +manifests: $(tools/controller-gen) generate-gwapi-manifests ## Generate WebhookConfiguration and CustomResourceDefinition objects. @$(LOG_TARGET) - $(tools/controller-gen) rbac:roleName=envoy-gateway-role crd webhook paths="./..." output:crd:artifacts:config=charts/gateway-helm/crds/generated output:rbac:artifacts:config=charts/gateway-helm/templates/generated/rbac output:webhook:artifacts:config=charts/gateway-helm/templates/generated/webhook - cat charts/gateway-helm/templates/generated/rbac/role.yaml | sed "s;envoy-gateway-role;{{ include \"eg.fullname\" . }}-envoy-gateway-role;g" > charts/gateway-helm/templates/generated/rbac/roles.yaml - rm charts/gateway-helm/templates/generated/rbac/role.yaml + $(tools/controller-gen) crd paths="./..." output:crd:artifacts:config=charts/gateway-helm/crds/generated .PHONY: generate-gwapi-manifests generate-gwapi-manifests: generate-gwapi-manifests: ## Generate GWAPI manifests and make it consistent with the go mod version. @@ -50,7 +48,7 @@ kube-generate: $(tools/controller-gen) ## Generate code containing DeepCopy, Dee .PHONY: kube-test kube-test: manifests generate $(tools/setup-envtest) ## Run Kubernetes provider tests. @$(LOG_TARGET) - KUBEBUILDER_ASSETS="$(shell $(tools/setup-envtest) use $(ENVTEST_K8S_VERSION) -p path)" go test --tags=integration ./... -coverprofile cover.out + KUBEBUILDER_ASSETS="$(shell $(tools/setup-envtest) use $(ENVTEST_K8S_VERSION) -p path)" go test --tags=integration,celvalidation ./... -coverprofile cover.out ##@ Kubernetes Deployment @@ -98,6 +96,9 @@ kube-demo-undeploy: ## Uninstall the Kubernetes resources installed from the `ma .PHONY: conformance conformance: create-cluster kube-install-image kube-deploy run-conformance delete-cluster ## Create a kind cluster, deploy EG into it, run Gateway API conformance, and clean up. +.PHONY: experimental-conformance ## Create a kind cluster, deploy EG into it, run Gateway API experimental conformance, and clean up. +experimental-conformance: create-cluster kube-install-image kube-deploy run-experimental-conformance delete-cluster ## Create a kind cluster, deploy EG into it, run Gateway API conformance, and clean up. + .PHONY: e2e e2e: create-cluster kube-install-image kube-deploy install-ratelimit run-e2e delete-cluster @@ -113,10 +114,8 @@ install-ratelimit: .PHONY: run-e2e run-e2e: prepare-e2e @$(LOG_TARGET) - kubectl wait --timeout=5m -n gateway-system deployment/gateway-api-admission-server --for=condition=Available kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-ratelimit --for=condition=Available kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available - kubectl wait --timeout=5m -n gateway-system job/gateway-api-admission --for=condition=Complete kubectl apply -f test/config/gatewayclass.yaml go test -v -tags e2e ./test/e2e --gateway-class=envoy-gateway --debug=true @@ -169,12 +168,19 @@ kube-install-image: image.build $(tools/kind) ## Install the EG image to a kind .PHONY: run-conformance run-conformance: ## Run Gateway API conformance. @$(LOG_TARGET) - kubectl wait --timeout=$(WAIT_TIMEOUT) -n gateway-system deployment/gateway-api-admission-server --for=condition=Available kubectl wait --timeout=$(WAIT_TIMEOUT) -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available - kubectl wait --timeout=$(WAIT_TIMEOUT) -n gateway-system job/gateway-api-admission --for=condition=Complete kubectl apply -f test/config/gatewayclass.yaml go test -v -tags conformance ./test/conformance --gateway-class=envoy-gateway --debug=true +CONFORMANCE_REPORT_PATH ?= + +.PHONY: run-experimental-conformance +run-experimental-conformance: ## Run Experimental Gateway API conformance. + @$(LOG_TARGET) + kubectl wait --timeout=$(WAIT_TIMEOUT) -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available + kubectl apply -f test/config/gatewayclass.yaml + go test -v -tags experimental ./test/conformance -run TestExperimentalConformance --gateway-class=envoy-gateway --debug=true --organization=envoyproxy --project=envoy-gateway --url=https://github.com/envoyproxy/gateway --version=latest --report-output="$(CONFORMANCE_REPORT_PATH)" --contact=https://github.com/envoyproxy/gateway/blob/main/GOVERNANCE.md + .PHONY: delete-cluster delete-cluster: $(tools/kind) ## Delete kind cluster. @$(LOG_TARGET) diff --git a/tools/src/buf/go.mod b/tools/src/buf/go.mod index ffeab992f54..758489607ba 100644 --- a/tools/src/buf/go.mod +++ b/tools/src/buf/go.mod @@ -2,60 +2,74 @@ module local go 1.19 -require github.com/bufbuild/buf v1.11.0 +require github.com/bufbuild/buf v1.28.1 require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/bufbuild/connect-go v1.4.0 // indirect - github.com/bufbuild/protocompile v0.1.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/docker/cli v20.10.21+incompatible // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker v20.10.21+incompatible // indirect - github.com/docker/docker-credential-helpers v0.7.0 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20231106192134-1baebb0a1518.2 // indirect + buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.31.0-20231111212044-1119bf4b707e.2 // indirect + connectrpc.com/connect v1.12.0 // indirect + connectrpc.com/otelconnect v0.6.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/bufbuild/protocompile v0.6.1-0.20231108163138-146b831231f7 // indirect + github.com/bufbuild/protovalidate-go v0.4.1 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/cli v24.0.7+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect - github.com/go-chi/chi/v5 v5.0.8 // indirect - github.com/gofrs/flock v0.8.1 // indirect - github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/go-chi/chi/v5 v5.0.10 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-containerregistry v0.12.1 // indirect - github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect + github.com/google/cel-go v0.18.2 // indirect + github.com/google/go-containerregistry v0.16.1 // indirect + github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 // indirect - github.com/klauspost/compress v1.15.13 // indirect - github.com/klauspost/pgzip v1.2.5 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/jdx/go-netrc v1.0.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/profile v1.7.0 // indirect - github.com/rs/cors v1.8.2 // indirect + github.com/rs/cors v1.10.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.11.2 // indirect - go.opentelemetry.io/otel/metric v0.34.0 // indirect - go.opentelemetry.io/otel/trace v1.11.2 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.4.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - golang.org/x/tools v0.4.0 // indirect - google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/tetratelabs/wazero v1.5.0 // indirect + github.com/vbatts/tar-split v0.11.5 // indirect + go.opentelemetry.io/otel v1.20.0 // indirect + go.opentelemetry.io/otel/metric v1.20.0 // indirect + go.opentelemetry.io/otel/sdk v1.20.0 // indirect + go.opentelemetry.io/otel/trace v1.20.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/protobuf v1.31.1-0.20231027082548-f4a6c1f6e5c1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tools/src/buf/go.sum b/tools/src/buf/go.sum index 6c447f2204b..96169e9a7ba 100644 --- a/tools/src/buf/go.sum +++ b/tools/src/buf/go.sum @@ -1,195 +1,171 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/bufbuild/buf v1.11.0 h1:jq/ckkxg87GaEgyHMqGGvZkVh8FeZuYLBF9DyOQK1RU= -github.com/bufbuild/buf v1.11.0/go.mod h1:IoaGkUhois4gv4EqTGT7H+4bbhZUvlYutECLgfoE2cA= -github.com/bufbuild/connect-go v1.4.0 h1:N94D0tGxuM2cSI7hM/aL8mtxL6+8rtHuFcIj9oGRp5s= -github.com/bufbuild/connect-go v1.4.0/go.mod h1:9iNvh/NOsfhNBUH5CtvXeVUskQO1xsrEviH7ZArwZ3I= -github.com/bufbuild/protocompile v0.1.0 h1:HjgJBI85hY/qmW5tw/66sNDZ7z0UDdVSi/5r40WHw4s= -github.com/bufbuild/protocompile v0.1.0/go.mod h1:ix/MMMdsT3fzxfw91dvbfzKW3fRRnuPCP47kpAm5m/4= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230914171853-63dfe56cc2c4.2/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20231106192134-1baebb0a1518.2 h1:iRWpWLm1nrsCHBVhibqPJQB3iIf3FRsAXioJVU8m6w0= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20231106192134-1baebb0a1518.2/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= +buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.31.0-20231111212044-1119bf4b707e.2 h1:tgXOEZtPifMR5kaQ6GB4t7pk0G3dai/OS3JFD9Zt+eE= +buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.31.0-20231111212044-1119bf4b707e.2/go.mod h1:3Ion4eJWjUDfJyrUXSgtB3zO5ZweZtyvNuEc+fyMBCk= +connectrpc.com/connect v1.12.0 h1:HwKdOY0lGhhoHdsza+hW55aqHEC64pYpObRNoAgn70g= +connectrpc.com/connect v1.12.0/go.mod h1:3AGaO6RRGMx5IKFfqbe3hvK1NqLosFNP2BxDYTPmNPo= +connectrpc.com/otelconnect v0.6.0 h1:VJAdQL9+sgdUw9+7+J+jq8pQo/h1S7tSFv2+vDcR7bU= +connectrpc.com/otelconnect v0.6.0/go.mod h1:jdcs0uiwXQVmSMgTJ2dAaWR5VbpNd7QKNkuoH7n86RA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/bufbuild/buf v1.28.1 h1:JG+PjhaVz4bprGV1u//ZBDSxWwFnjrzjse2pmm4zBlI= +github.com/bufbuild/buf v1.28.1/go.mod h1:X/HDGbWUM2QiR5XvvsBfElUPblETOf96zq5H7FOUiP4= +github.com/bufbuild/protocompile v0.6.1-0.20231108163138-146b831231f7 h1:1pUks8VaLdprN9wrxAgshb06b08IzdYp0B7JgoDeUfw= +github.com/bufbuild/protocompile v0.6.1-0.20231108163138-146b831231f7/go.mod h1:9N39DyRmxAF5+5AjqXQKV6hyWDI0EeoX4TRMix2ZnPE= +github.com/bufbuild/protovalidate-go v0.4.1 h1:ye/8S72WbEklCeltPkSEeT8Eu1A7P/gmMsmapkwqTFk= +github.com/bufbuild/protovalidate-go v0.4.1/go.mod h1:+p5FXfOjSEgLz5WBDTOMPMdQPXqALEERbJZU7huDCtA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= +github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= -github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= -github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= +github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= +github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= -github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= -github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= +github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= +github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/google/cel-go v0.18.2 h1:L0B6sNBSVmt0OyECi8v6VOS74KOc9W/tLiWKfZABvf4= +github.com/google/cel-go v0.18.2/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8= -github.com/google/go-containerregistry v0.12.1/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= +github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 h1:2uT3aivO7NVpUPGcQX7RbHijHMyWix/yCnIrCWc+5co= -github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84/go.mod h1:Zi/ZFkEqFHTm7qkjyNJjaWH4LQA9LQhGJyF0lTYGpxw= -github.com/jhump/protoreflect v1.14.0 h1:MBbQK392K3u8NTLbKOCIi3XdI+y+c6yt5oMq0X3xviw= +github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ= +github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0= -github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= -github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= -github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0= +github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= +github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= +github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= -go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= -go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= -go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= -go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= -go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= +go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= +go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA= +go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM= +go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM= +go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= +go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= +go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= +go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -197,57 +173,36 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk= -google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.1-0.20231027082548-f4a6c1f6e5c1 h1:fk72uXZyuZiTtW5tgd63jyVK6582lF61nRC/kGv6vCA= +google.golang.org/protobuf v1.31.1-0.20231027082548-f4a6c1f6e5c1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/tools/src/codespell/requirements.txt b/tools/src/codespell/requirements.txt index ddff454685c..e0a43e13840 100644 --- a/tools/src/codespell/requirements.txt +++ b/tools/src/codespell/requirements.txt @@ -1 +1 @@ -codespell==2.2.5 +codespell==2.2.6 diff --git a/tools/src/controller-gen/go.mod b/tools/src/controller-gen/go.mod index 9eaf4167a05..ebafb3d2757 100644 --- a/tools/src/controller-gen/go.mod +++ b/tools/src/controller-gen/go.mod @@ -1,37 +1,37 @@ module local -go 1.20 +go 1.21 -require sigs.k8s.io/controller-tools v0.10.0 +require sigs.k8s.io/controller-tools v0.13.0 require ( - github.com/fatih/color v1.12.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/gobuffalo/flect v0.2.5 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/gobuffalo/flect v1.0.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/spf13/cobra v1.4.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect + golang.org/x/tools v0.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.25.0 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/apimachinery v0.25.0 // indirect - k8s.io/klog/v2 v2.70.1 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + k8s.io/api v0.28.0 // indirect + k8s.io/apiextensions-apiserver v0.28.0 // indirect + k8s.io/apimachinery v0.28.0 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/tools/src/controller-gen/go.sum b/tools/src/controller-gen/go.sum index 68f327ed0b4..0160f5185bf 100644 --- a/tools/src/controller-gen/go.sum +++ b/tools/src/controller-gen/go.sum @@ -1,54 +1,58 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/gobuffalo/flect v0.2.5 h1:H6vvsv2an0lalEaCDRThvtBfmg44W/QHXBCYUXf/6S4= -github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= +github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -56,41 +60,40 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -100,21 +103,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= -k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -sigs.k8s.io/controller-tools v0.10.0 h1:0L5DTDTFB67jm9DkfrONgTGmfc/zYow0ZaHyppizU2U= -sigs.k8s.io/controller-tools v0.10.0/go.mod h1:uvr0EW6IsprfB0jpQq6evtKy+hHyHCXNfdWI5ONPx94= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +k8s.io/api v0.28.0 h1:3j3VPWmN9tTDI68NETBWlDiA9qOiGJ7sdKeufehBYsM= +k8s.io/api v0.28.0/go.mod h1:0l8NZJzB0i/etuWnIXcwfIv+xnDOhL3lLW919AWYDuY= +k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= +k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= +k8s.io/apimachinery v0.28.0 h1:ScHS2AG16UlYWk63r46oU3D5y54T53cVI5mMJwwqFNA= +k8s.io/apimachinery v0.28.0/go.mod h1:X0xh/chESs2hP9koe+SdIAcXWcQ+RM5hy0ZynB+yEvw= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-tools v0.13.0 h1:NfrvuZ4bxyolhDBt/rCZhDnx3M2hzlhgo5n3Iv2RykI= +sigs.k8s.io/controller-tools v0.13.0/go.mod h1:5vw3En2NazbejQGCeWKRrE7q4P+CW8/klfVqP8QZkgA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/tools/src/crd-ref-docs/go.mod b/tools/src/crd-ref-docs/go.mod index 6b561c71ab7..6a27606ad34 100644 --- a/tools/src/crd-ref-docs/go.mod +++ b/tools/src/crd-ref-docs/go.mod @@ -1,6 +1,6 @@ module local -go 1.20 +go 1.21 require github.com/elastic/crd-ref-docs v0.0.9 diff --git a/tools/src/golangci-lint/go.mod b/tools/src/golangci-lint/go.mod index 0bb159ca06c..dfd11cf4b25 100644 --- a/tools/src/golangci-lint/go.mod +++ b/tools/src/golangci-lint/go.mod @@ -1,39 +1,43 @@ module local -go 1.20 +go 1.21 -require github.com/golangci/golangci-lint v1.53.3 +require github.com/golangci/golangci-lint v1.55.2 require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect - github.com/4meepo/tagalign v1.2.2 // indirect - github.com/Abirdcfly/dupword v0.0.11 // indirect - github.com/Antonboom/errname v0.1.10 // indirect - github.com/Antonboom/nilnil v0.1.5 // indirect + github.com/4meepo/tagalign v1.3.3 // indirect + github.com/Abirdcfly/dupword v0.0.13 // indirect + github.com/Antonboom/errname v0.1.12 // indirect + github.com/Antonboom/nilnil v0.1.7 // indirect + github.com/Antonboom/testifylint v0.2.3 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.1.0 // indirect + github.com/alecthomas/go-check-sumtype v0.1.3 // indirect github.com/alexkohler/nakedret/v2 v2.0.2 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/ashanbrown/forbidigo v1.5.3 // indirect + github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bombsimon/wsl/v3 v3.4.0 // indirect - github.com/breml/bidichk v0.2.4 // indirect - github.com/breml/errchkjson v0.3.1 // indirect - github.com/butuzov/ireturn v0.2.0 // indirect + github.com/breml/bidichk v0.2.7 // indirect + github.com/breml/errchkjson v0.3.6 // indirect + github.com/butuzov/ireturn v0.2.2 // indirect github.com/butuzov/mirror v1.1.0 // indirect + github.com/catenacyber/perfsprint v0.2.0 // indirect + github.com/ccojocar/zxcvbn-go v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/charithe/durationcheck v0.0.10 // indirect - github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect + github.com/chavacava/garif v0.1.0 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect - github.com/daixiang0/gci v0.10.1 // indirect + github.com/daixiang0/gci v0.11.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.4.3 // indirect github.com/esimonov/ifshort v1.0.4 // indirect @@ -43,7 +47,8 @@ require ( github.com/firefart/nonamedreturns v1.0.4 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/go-critic/go-critic v0.8.1 // indirect + github.com/ghostiam/protogetter v0.2.3 // indirect + github.com/go-critic/go-critic v0.9.0 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect github.com/go-toolsmith/astequal v1.1.0 // indirect @@ -58,13 +63,13 @@ require ( github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect - github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect + github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect - github.com/golangci/misspell v0.4.0 // indirect - github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect + github.com/golangci/misspell v0.4.1 // indirect + github.com/golangci/revgrep v0.5.2 // indirect github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect @@ -76,7 +81,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jgautheron/goconst v1.5.1 // indirect + github.com/jgautheron/goconst v1.6.0 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect github.com/julz/importas v0.1.0 // indirect @@ -84,12 +89,13 @@ require ( github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.7 // indirect + github.com/kunwardeep/paralleltest v1.0.8 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect github.com/ldez/gomoddirectives v0.2.3 // indirect github.com/ldez/tagliatelle v0.5.0 // indirect github.com/leonklingele/grouper v1.1.1 // indirect github.com/lufeee/execinquery v1.2.1 // indirect + github.com/macabu/inamedparam v0.1.2 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect @@ -99,34 +105,33 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.3.2 // indirect + github.com/mgechev/revive v1.3.4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moricho/tparallel v0.3.1 // indirect github.com/nakabonne/nestif v0.3.1 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect github.com/nishanths/exhaustive v0.11.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.12.1 // indirect + github.com/nunnatsa/ginkgolinter v0.14.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.4.2 // indirect + github.com/polyfloyd/go-errorlint v1.4.5 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/quasilyte/go-ruleguard v0.3.19 // indirect + github.com/quasilyte/go-ruleguard v0.4.0 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect github.com/ryancurrah/gomodguard v1.3.0 // indirect - github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect + github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect - github.com/securego/gosec/v2 v2.16.0 // indirect + github.com/sashamelentyev/usestdlibvars v1.24.0 // indirect + github.com/securego/gosec/v2 v2.18.2 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect @@ -147,35 +152,36 @@ require ( github.com/subosito/gotenv v1.4.1 // indirect github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect - github.com/tetafro/godot v1.4.11 // indirect + github.com/tetafro/godot v1.4.15 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/timonwong/loggercheck v0.9.4 // indirect github.com/tomarrell/wrapcheck/v2 v2.8.1 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect - github.com/ultraware/funlen v0.0.3 // indirect + github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.0.5 // indirect - github.com/uudashr/gocognit v1.0.6 // indirect - github.com/xen0n/gosmopolitan v1.2.1 // indirect + github.com/uudashr/gocognit v1.1.2 // indirect + github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect - github.com/ykadowak/zerologlint v0.1.2 // indirect - gitlab.com/bosi/decorder v0.2.3 // indirect - go.tmz.dev/musttag v0.7.0 // indirect + github.com/ykadowak/zerologlint v0.1.3 // indirect + gitlab.com/bosi/decorder v0.4.1 // indirect + go-simpler.org/sloglint v0.1.2 // indirect + go.tmz.dev/musttag v0.7.2 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect - golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.9.3 // indirect + golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - honnef.co/go/tools v0.4.3 // indirect + honnef.co/go/tools v0.4.6 // indirect mvdan.cc/gofumpt v0.5.0 // indirect mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect diff --git a/tools/src/golangci-lint/go.sum b/tools/src/golangci-lint/go.sum index 63d3c3482b4..8c0d284fc64 100644 --- a/tools/src/golangci-lint/go.sum +++ b/tools/src/golangci-lint/go.sum @@ -40,26 +40,34 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/4meepo/tagalign v1.2.2 h1:kQeUTkFTaBRtd/7jm8OKJl9iHk0gAO+TDFPHGSna0aw= -github.com/4meepo/tagalign v1.2.2/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= -github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16wgU= -github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= -github.com/Antonboom/errname v0.1.10 h1:RZ7cYo/GuZqjr1nuJLNe8ZH+a+Jd9DaZzttWzak9Bls= -github.com/Antonboom/errname v0.1.10/go.mod h1:xLeiCIrvVNpUtsN0wxAh05bNIZpqE22/qDMnTBTttiA= -github.com/Antonboom/nilnil v0.1.5 h1:X2JAdEVcbPaOom2TUa1FxZ3uyuUlex0XMLGYMemu6l0= -github.com/Antonboom/nilnil v0.1.5/go.mod h1:I24toVuBKhfP5teihGWctrRiPbRKHwZIFOvc6v3HZXk= +github.com/4meepo/tagalign v1.3.3 h1:ZsOxcwGD/jP4U/aw7qeWu58i7dwYemfy5Y+IF1ACoNw= +github.com/4meepo/tagalign v1.3.3/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= +github.com/Abirdcfly/dupword v0.0.13 h1:SMS17YXypwP000fA7Lr+kfyBQyW14tTT+nRv9ASwUUo= +github.com/Abirdcfly/dupword v0.0.13/go.mod h1:Ut6Ue2KgF/kCOawpW4LnExT+xZLQviJPE4klBPMK/5Y= +github.com/Antonboom/errname v0.1.12 h1:oh9ak2zUtsLp5oaEd/erjB4GPu9w19NyoIskZClDcQY= +github.com/Antonboom/errname v0.1.12/go.mod h1:bK7todrzvlaZoQagP1orKzWXv59X/x0W0Io2XT1Ssro= +github.com/Antonboom/nilnil v0.1.7 h1:ofgL+BA7vlA1K2wNQOsHzLJ2Pw5B5DpWRLdDAVvvTow= +github.com/Antonboom/nilnil v0.1.7/go.mod h1:TP+ScQWVEq0eSIxqU8CbdT5DFWoHp0MbP+KMUO1BKYQ= +github.com/Antonboom/testifylint v0.2.3 h1:MFq9zyL+rIVpsvLX4vDPLojgN7qODzWsrnftNX2Qh60= +github.com/Antonboom/testifylint v0.2.3/go.mod h1:IYaXaOX9NbfAyO+Y04nfjGI8wDemC1rUyM/cYolz018= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 h1:3ZBs7LAezy8gh0uECsA6CGU43FF3zsx5f4eah5FxTMA= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0/go.mod h1:rZLTje5A9kFBe0pzhpe2TdhRniBF++PRHQuRpR8esVc= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/f1kSDVYY= github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= +github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= +github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/go-check-sumtype v0.1.3 h1:M+tqMxB68hcgccRXBMVCPI4UJ+QUfdSx0xdbypKCqA8= +github.com/alecthomas/go-check-sumtype v0.1.3/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -71,11 +79,12 @@ github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pO github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/ashanbrown/forbidigo v1.5.3 h1:jfg+fkm/snMx+V9FBwsl1d340BV/99kZGv5jN9hBoXk= -github.com/ashanbrown/forbidigo v1.5.3/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= +github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= +github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -86,22 +95,26 @@ github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bombsimon/wsl/v3 v3.4.0 h1:RkSxjT3tmlptwfgEgTgU+KYKLI35p/tviNXNXiL2aNU= github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo= -github.com/breml/bidichk v0.2.4 h1:i3yedFWWQ7YzjdZJHnPo9d/xURinSq3OM+gyM43K4/8= -github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s= -github.com/breml/errchkjson v0.3.1 h1:hlIeXuspTyt8Y/UmP5qy1JocGNR00KQHgfaNtRAjoxQ= -github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U= -github.com/butuzov/ireturn v0.2.0 h1:kCHi+YzC150GE98WFuZQu9yrTn6GEydO2AuPLbTgnO4= -github.com/butuzov/ireturn v0.2.0/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= +github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= +github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= +github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= +github.com/butuzov/ireturn v0.2.2 h1:jWI36dxXwVrI+RnXDwux2IZOewpmfv930OuIRfaBUJ0= +github.com/butuzov/ireturn v0.2.2/go.mod h1:RfGHUvvAuFFxoHKf4Z8Yxuh6OjlCw1KvR2zM1NFHeBk= github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= +github.com/catenacyber/perfsprint v0.2.0 h1:azOocHLscPjqXVJ7Mf14Zjlkn4uNua0+Hcg1wTR6vUo= +github.com/catenacyber/perfsprint v0.2.0/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/ccojocar/zxcvbn-go v1.0.1 h1:+sxrANSCj6CdadkcMnvde/GWU1vZiiXRbqYSCalV4/4= +github.com/ccojocar/zxcvbn-go v1.0.1/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= -github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0= -github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= +github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= +github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -112,8 +125,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= -github.com/daixiang0/gci v0.10.1 h1:eheNA3ljF6SxnPD/vE4lCBusVHmV3Rs3dkKvFrJ7MR0= -github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= +github.com/daixiang0/gci v0.11.2 h1:Oji+oPsp3bQ6bNNgX30NBAVT18P4uBH4sRZnlOlTj7Y= +github.com/daixiang0/gci v0.11.2/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -136,12 +149,15 @@ github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4 github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/go-critic/go-critic v0.8.1 h1:16omCF1gN3gTzt4j4J6fKI/HnRojhEp+Eks6EuKw3vw= -github.com/go-critic/go-critic v0.8.1/go.mod h1:kpzXl09SIJX1cr9TB/g/sAG+eFEl7ZS9f9cqvZtyNl0= +github.com/ghostiam/protogetter v0.2.3 h1:qdv2pzo3BpLqezwqfGDLZ+nHEYmc5bUpIdsMbBVwMjw= +github.com/ghostiam/protogetter v0.2.3/go.mod h1:KmNLOsy1v04hKbvZs8EfGI1fk39AgTdRDxWNYPfXVc4= +github.com/go-critic/go-critic v0.9.0 h1:Pmys9qvU3pSML/3GEQ2Xd9RZ/ip+aXHKILuxczKGV/U= +github.com/go-critic/go-critic v0.9.0/go.mod h1:5P8tdXL7m/6qnyG6oRAlYLORvoXH0WDypYgAEmagT40= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -152,8 +168,10 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= @@ -166,6 +184,7 @@ github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlN github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= +github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= @@ -212,18 +231,18 @@ github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9 github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs= -github.com/golangci/golangci-lint v1.53.3 h1:CUcRafczT4t1F+mvdkUm6KuOpxUZTl0yWN/rSU6sSMo= -github.com/golangci/golangci-lint v1.53.3/go.mod h1:W4Gg3ONq6p3Jl+0s/h9Gr0j7yEgHJWWZO2bHl2tBUXM= +github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= +github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= +github.com/golangci/golangci-lint v1.55.2 h1:yllEIsSJ7MtlDBwDJ9IMBkyEUz2fYE0b5B8IUgO1oP8= +github.com/golangci/golangci-lint v1.55.2/go.mod h1:H60CZ0fuqoTwlTvnbyjhpZPWp7KmsjwV2yupIMiMXbM= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0= -github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= +github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g= +github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI= +github.com/golangci/revgrep v0.5.2 h1:EndcWoRhcnfj2NHQ+28hyuXpLMF+dQmCN+YaeeIl4FU= +github.com/golangci/revgrep v0.5.2/go.mod h1:bjAMA+Sh/QUfTDcHzxfyHxr4xKvllVr/0sCv2e7jJHA= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -240,8 +259,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -257,6 +276,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -275,6 +295,7 @@ github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3 github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -292,8 +313,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= -github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.6.0 h1:gbMLWKRMkzAc6kYsQL6/TxaoBUg3Jm9LSF/Ih1ADWGA= +github.com/jgautheron/goconst v1.6.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= @@ -321,13 +342,15 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.7 h1:2uCk94js0+nVNQoHZNLBkAR1DQJrVzw6T0RMzJn55dQ= -github.com/kunwardeep/paralleltest v1.0.7/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kunwardeep/paralleltest v1.0.8 h1:Ul2KsqtzFxTlSU7IP0JusWlLiNqQaloB9vguyjbE558= +github.com/kunwardeep/paralleltest v1.0.8/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= @@ -338,6 +361,8 @@ github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= +github.com/macabu/inamedparam v0.1.2 h1:RR5cnayM6Q7cDhQol32DE2BGAPGMnffJ31LFE+UklaU= +github.com/macabu/inamedparam v0.1.2/go.mod h1:Xg25QvY7IBRl1KLPV9Rbml8JOMZtF/iAkNkmV7eQgjw= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= @@ -359,8 +384,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.3.2 h1:Wb8NQKBaALBJ3xrrj4zpwJwqwNA6nDpyJSEQWcCka6U= -github.com/mgechev/revive v1.3.2/go.mod h1:UCLtc7o5vg5aXCwdUTU1kEBQ1v+YXPAkYDIDXbrs5I0= +github.com/mgechev/revive v1.3.4 h1:k/tO3XTaWY4DEHal9tWBkkUMJYO/dLDVyMmAQxmIMDc= +github.com/mgechev/revive v1.3.4/go.mod h1:W+pZCMu9qj8Uhfs1iJMQsEFLRozUfvwFwqVvRbSNLVw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -376,21 +401,23 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.11.0 h1:T3I8nUGhl/Cwu5Z2hfc92l0e04D2GEW6e0l8pzda2l0= github.com/nishanths/exhaustive v0.11.0/go.mod h1:RqwDsZ1xY0dNdqHho2z6X+bgzizwbLYOWnZbbl2wLB4= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.12.1 h1:vwOqb5Nu05OikTXqhvLdHCGcx5uthIYIl0t79UVrERQ= -github.com/nunnatsa/ginkgolinter v0.12.1/go.mod h1:AK8Ab1PypVrcGUusuKD8RDcl2KgsIwvNaaxAlyHSzso= +github.com/nunnatsa/ginkgolinter v0.14.1 h1:khx0CqR5U4ghsscjJ+lZVthp3zjIFytRXPTaQ/TMiyA= +github.com/nunnatsa/ginkgolinter v0.14.1/go.mod h1:nY0pafUSst7v7F637e7fymaMlQqI9c0Wka2fGsDkzWg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= +github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= @@ -406,8 +433,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.4.2 h1:CU+O4181IxFDdPH6t/HT7IiDj1I7zxNi1RIUxYwn8d0= -github.com/polyfloyd/go-errorlint v1.4.2/go.mod h1:k6fU/+fQe38ednoZS51T7gSIGQW1y94d6TkSr35OzH8= +github.com/polyfloyd/go-errorlint v1.4.5 h1:70YWmMy4FgRHehGNOUask3HtSFSOLKgmDn7ryNe7LqI= +github.com/polyfloyd/go-errorlint v1.4.5/go.mod h1:sIZEbFoDOCnTYYZoVkjc4hTnM459tuWA9H/EkdXwsKk= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -430,8 +457,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/quasilyte/go-ruleguard v0.3.19 h1:tfMnabXle/HzOb5Xe9CUZYWXKfkS1KwRmZyPmD9nVcc= -github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw= +github.com/quasilyte/go-ruleguard v0.4.0 h1:DyM6r+TKL+xbKB4Nm7Afd1IQh9kEUKQs2pboWGKtvQo= +github.com/quasilyte/go-ruleguard v0.4.0/go.mod h1:Eu76Z/R8IXtViWUIHkE3p8gdH3/PKk1eh3YGfaEof10= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= @@ -440,19 +467,20 @@ github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4l github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.3.0 h1:q15RT/pd6UggBXVBuLps8BXRvl5GPBcwVA7BJHMLuTw= github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50= -github.com/ryanrolds/sqlclosecheck v0.4.0 h1:i8SX60Rppc1wRuyQjMciLqIzV3xnoHB7/tXbr6RGYNI= -github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ= +github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= +github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.23.0 h1:01h+/2Kd+NblNItNeux0veSL5cBF1jbEOPrEhDzGYq0= -github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= -github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyOs5U= -github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI= +github.com/sashamelentyev/usestdlibvars v1.24.0 h1:MKNzmXtGh5N0y74Z/CIaJh4GlB364l0K1RUT08WSWAc= +github.com/sashamelentyev/usestdlibvars v1.24.0/go.mod h1:9cYkq+gYJ+a5W2RPdhfaSCnTVUC1OQP/bSiiBhq3OZE= +github.com/securego/gosec/v2 v2.18.2 h1:DkDt3wCiOtAHf1XkiXZBhQ6m6mK/b9T/wD257R3/c+I= +github.com/securego/gosec/v2 v2.18.2/go.mod h1:xUuqSF6i0So56Y2wwohWAmB07EdBkUN6crbLlHwbyJs= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -493,7 +521,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -501,7 +528,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= @@ -514,8 +540,8 @@ github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= -github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= +github.com/tetafro/godot v1.4.15 h1:QzdIs+XB8q+U1WmQEWKHQbKmCw06QuQM7gLx/dky2RM= +github.com/tetafro/godot v1.4.15/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= @@ -524,20 +550,20 @@ github.com/tomarrell/wrapcheck/v2 v2.8.1 h1:HxSqDSN0sAt0yJYsrcYVoEeyM4aI9yAm3KQp github.com/tomarrell/wrapcheck/v2 v2.8.1/go.mod h1:/n2Q3NZ4XFT50ho6Hbxg+RV1uyo2Uow/Vdm9NQcl5SE= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= +github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= -github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y= -github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= -github.com/xen0n/gosmopolitan v1.2.1 h1:3pttnTuFumELBRSh+KQs1zcz4fN6Zy7aB0xlnQSn1Iw= -github.com/xen0n/gosmopolitan v1.2.1/go.mod h1:JsHq/Brs1o050OOdmzHeOr0N7OtlnKRAGAsElF8xBQA= +github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI= +github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= +github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= +github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= -github.com/ykadowak/zerologlint v0.1.2 h1:Um4P5RMmelfjQqQJKtE8ZW+dLZrXrENeIzWWKw800U4= -github.com/ykadowak/zerologlint v0.1.2/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= +github.com/ykadowak/zerologlint v0.1.3 h1:TLy1dTW3Nuc+YE3bYRPToG1Q9Ej78b5UUN6bjbGdxPE= +github.com/ykadowak/zerologlint v0.1.3/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -545,20 +571,24 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= -gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= -go-simpler.org/assert v0.5.0 h1:+5L/lajuQtzmbtEfh69sr5cRf2/xZzyJhFjoOz/PPqs= +gitlab.com/bosi/decorder v0.4.1 h1:VdsdfxhstabyhZovHafFw+9eJ6eU0d2CkFNJcZz/NU4= +gitlab.com/bosi/decorder v0.4.1/go.mod h1:jecSqWUew6Yle1pCr2eLWTensJMmsxHsBwt+PVbkAqA= +go-simpler.org/assert v0.6.0 h1:QxSrXa4oRuo/1eHMXSBFHKvJIpWABayzKldqZyugG7E= +go-simpler.org/assert v0.6.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= +go-simpler.org/sloglint v0.1.2 h1:IjdhF8NPxyn0Ckn2+fuIof7ntSnVUAqBFcQRrnG9AiM= +go-simpler.org/sloglint v0.1.2/go.mod h1:2LL+QImPfTslD5muNPydAEYmpXIj6o/WYcqnJjLi4o4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.tmz.dev/musttag v0.7.0 h1:QfytzjTWGXZmChoX0L++7uQN+yRCPfyFm+whsM+lfGc= -go.tmz.dev/musttag v0.7.0/go.mod h1:oTFPvgOkJmp5kYL02S8+jrH0eLrBIl57rzWeA26zDEM= +go.tmz.dev/musttag v0.7.2 h1:1J6S9ipDbalBSODNT5jCep8dhZyMr4ttnjQagmGYR5s= +go.tmz.dev/musttag v0.7.2/go.mod h1:m6q5NiiSKMnQYokefa2xGoyoXnrswCbJ0AWYzf4Zs28= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -587,8 +617,8 @@ golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQ golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 h1:J74nGeMgeFnYQJN59eFwh06jX/V8g0lB7LWpjSLxtgU= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCmFW9ekjA9Zx8vYORzOUH2/Nl5WPuLQ= +golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -619,8 +649,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -662,7 +692,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -686,8 +717,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -746,8 +777,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -765,8 +796,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -838,8 +869,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -941,6 +972,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -961,8 +993,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.4.3 h1:o/n5/K5gXqk8Gozvs2cnL0F2S1/g1vcGCAx2vETjITw= -honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= +honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8= +honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E= mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= diff --git a/tools/src/helm-docs/go.mod b/tools/src/helm-docs/go.mod index f59802e5218..682bddcdc09 100644 --- a/tools/src/helm-docs/go.mod +++ b/tools/src/helm-docs/go.mod @@ -1,38 +1,39 @@ module github.com/envoyproxy/gateway/tools/src/helm-docs -go 1.20 +go 1.21 -require github.com/norwoodj/helm-docs v1.11.0 +require github.com/norwoodj/helm-docs v1.11.3 require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/google/uuid v1.1.1 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.1 // indirect github.com/imdario/mergo v0.3.11 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect - github.com/magiconair/properties v1.8.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect - github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/shopspring/decimal v1.2.0 // indirect - github.com/sirupsen/logrus v1.2.0 // indirect - github.com/spf13/afero v1.1.2 // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/cobra v0.0.5 // indirect - github.com/spf13/jwalterweatherman v1.0.0 // indirect - github.com/spf13/pflag v1.0.3 // indirect - github.com/spf13/viper v1.4.0 // indirect - golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 // indirect - golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect - golang.org/x/text v0.3.0 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect + github.com/sirupsen/logrus v1.6.0 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/helm v2.14.3+incompatible // indirect ) diff --git a/tools/src/helm-docs/go.sum b/tools/src/helm-docs/go.sum index a2df6129e64..c014045239d 100644 --- a/tools/src/helm-docs/go.sum +++ b/tools/src/helm-docs/go.sum @@ -1,6 +1,603 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= @@ -8,181 +605,1184 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/norwoodj/helm-docs v1.11.0 h1:y6pakrWOPTfe1Mpt0UyR4KrDzIV3V7EDTu5NW4a2BS8= -github.com/norwoodj/helm-docs v1.11.0/go.mod h1:rLqec59NO7YF57Rq9VlubQHMp7wcRTJhzpkcgs4lOG4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/norwoodj/helm-docs v1.11.3 h1:8dwJUOkZY+PrnMeGfJh0HjjnQAHRPIgb2qabW4MSzXk= +github.com/norwoodj/helm-docs v1.11.3/go.mod h1:pSD2NcqopI4luhnVbdX6gwnwrWP8BTTaWTY7yPlm9xY= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s= +go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY= -golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/helm v2.14.3+incompatible h1:uzotTcZXa/b2SWVoUzM1xiCXVjI38TuxMujS/1s+3Gw= k8s.io/helm v2.14.3+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/tools/src/kind/go.mod b/tools/src/kind/go.mod index eaf6166439a..160b8d84b52 100644 --- a/tools/src/kind/go.mod +++ b/tools/src/kind/go.mod @@ -1,6 +1,6 @@ module github.com/envoyproxy/gateway/tools/src/kind -go 1.20 +go 1.21 require sigs.k8s.io/kind v0.20.0 diff --git a/tools/src/kustomize/go.mod b/tools/src/kustomize/go.mod deleted file mode 100644 index 91878b3174f..00000000000 --- a/tools/src/kustomize/go.mod +++ /dev/null @@ -1,95 +0,0 @@ -module local - -go 1.20 - -require sigs.k8s.io/kustomize/kustomize/v3 v3.10.0 - -require ( - cloud.google.com/go v0.38.0 // indirect - github.com/Azure/go-autorest/autorest v0.9.0 // indirect - github.com/Azure/go-autorest/autorest/adal v0.5.0 // indirect - github.com/Azure/go-autorest/autorest/date v0.1.0 // indirect - github.com/Azure/go-autorest/logger v0.1.0 // indirect - github.com/Azure/go-autorest/tracing v0.5.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect - github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 // indirect - github.com/evanphx/json-patch v4.9.0+incompatible // indirect - github.com/go-errors/errors v1.0.1 // indirect - github.com/go-openapi/analysis v0.19.5 // indirect - github.com/go-openapi/errors v0.19.2 // indirect - github.com/go-openapi/jsonpointer v0.19.3 // indirect - github.com/go-openapi/jsonreference v0.19.3 // indirect - github.com/go-openapi/loads v0.19.4 // indirect - github.com/go-openapi/runtime v0.19.4 // indirect - github.com/go-openapi/spec v0.19.5 // indirect - github.com/go-openapi/strfmt v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect - github.com/go-openapi/validate v0.19.8 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/gogo/protobuf v1.3.1 // indirect - github.com/golang/protobuf v1.3.2 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/googleapis/gnostic v0.1.0 // indirect - github.com/gophercloud/gophercloud v0.1.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.0 // indirect - github.com/hashicorp/go-multierror v1.1.0 // indirect - github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/go-version v1.1.0 // indirect - github.com/imdario/mergo v0.3.5 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/json-iterator/go v1.1.8 // indirect - github.com/mailru/easyjson v0.7.0 // indirect - github.com/mattn/go-runewidth v0.0.7 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect - github.com/olekukonko/tablewriter v0.0.4 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d // indirect - github.com/spf13/cobra v1.0.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.6.1 // indirect - github.com/ulikunitz/xz v0.5.8 // indirect - github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - github.com/yujunz/go-getter v1.5.1-lite.0.20201201013212-6d9c071adddf // indirect - go.mongodb.org/mongo-driver v1.1.2 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect - golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - golang.org/x/text v0.3.4 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - google.golang.org/appengine v1.5.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect - k8s.io/api v0.18.10 // indirect - k8s.io/apimachinery v0.18.10 // indirect - k8s.io/client-go v0.18.10 // indirect - k8s.io/klog v1.0.0 // indirect - k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 // indirect - k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 // indirect - sigs.k8s.io/kustomize/api v0.8.0 // indirect - sigs.k8s.io/kustomize/cmd/config v0.9.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.10.9 // indirect - sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect -) - -exclude ( - github.com/russross/blackfriday v2.0.0+incompatible - sigs.k8s.io/kustomize/api v0.2.0 - sigs.k8s.io/kustomize/cmd/config v0.2.0 -) diff --git a/tools/src/kustomize/go.sum b/tools/src/kustomize/go.sum deleted file mode 100644 index c8e33ba974a..00000000000 --- a/tools/src/kustomize/go.sum +++ /dev/null @@ -1,622 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= -github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= -github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= -github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4 h1:5I4CCSqoWzT+82bBkNIvmLc0UOsoKKQ4Fz+3VxOB7SY= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= -github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9wWIM= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.8 h1:YFzsdWIDfVuLvIOF+ZmKjVg1MbPJ1QgY9PihMwei1ys= -github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= -github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= -github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA= -github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= -github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= -github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= -github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= -github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM= -github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc= -github.com/yujunz/go-getter v1.5.1-lite.0.20201201013212-6d9c071adddf h1:gvEmqF83GB8R5XtrMseJb6A6R0OCtNAS8f4TmZg2dGc= -github.com/yujunz/go-getter v1.5.1-lite.0.20201201013212-6d9c071adddf/go.mod h1:bL0Pr07HEdsMZ1WBqZIxXj96r5LnFsY4LgPaPEGkw1k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= -k8s.io/api v0.18.10 h1:M0/vqfuBAIIS7jsOOcosT0niiotZGqw6/zHTFpyi8iQ= -k8s.io/api v0.18.10/go.mod h1:xWtwPX1v47j5RTncmlMFGCx8b0avh+nP8OgZZ9hjo3M= -k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.18.10 h1:Zupk3lPrUfhCF9puTpA8EvEfPsrhNZtrpOqdp66mKVs= -k8s.io/apimachinery v0.18.10/go.mod h1:PF5taHbXgTEJLU+xMypMmYTXTWPJ5LaW8bfsisxnEXk= -k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= -k8s.io/client-go v0.18.10 h1:fETWvjTtnE3/s+h0SYr2wvlKWFDF+NrhwAL/ddqVa2Q= -k8s.io/client-go v0.18.10/go.mod h1:XBkFAqPrzqfwmGkV5ac+mlgBpWcz5TkhLw2808q8C3c= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= -k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= -sigs.k8s.io/kustomize/api v0.6.5 h1:xaAWZamIhpt9Y5Kn/vuBcBhZH8/m0zwew1d4HepIgXg= -sigs.k8s.io/kustomize/api v0.6.5/go.mod h1:Z96Z48h3nOWgVAmd4JGABszi5znhEnz7xoWHy+Bl7L4= -sigs.k8s.io/kustomize/api v0.8.0 h1:6C3GlBzDSrHg3q29k4nwAJHKyLAyZ+Fsz6DwZuao54w= -sigs.k8s.io/kustomize/api v0.8.0/go.mod h1:Ih6Y6bOErR70EdapDtWitBzPG9HewyemRY6sFaQyugU= -sigs.k8s.io/kustomize/cmd/config v0.8.5 h1:e8uMA7LjAUss3cMefVoOY2tFr92brCTUAA6CmWgq57M= -sigs.k8s.io/kustomize/cmd/config v0.8.5/go.mod h1:PlZTWxL7Xi75mY64HBC9IVf2xwrEbzy3L7r7+19kON4= -sigs.k8s.io/kustomize/cmd/config v0.9.1 h1:vU5aKij0ej0DroCAaiu8eObj7Z0HxNtbGi7wBmYYwP0= -sigs.k8s.io/kustomize/cmd/config v0.9.1/go.mod h1:HAoxw2o0Kwfdre4+xAQpgD0ljMzUZvLnqcpItqnqFlk= -sigs.k8s.io/kustomize/kustomize/v3 v3.8.7 h1:9Yn76OTslYzNg4k2c7F279IgmpQW1C753SEJGh1tubw= -sigs.k8s.io/kustomize/kustomize/v3 v3.8.7/go.mod h1:EJBh54QBPZPSCJqfb9b+qGwzL9IE0Bp7hyM+wONC6Mw= -sigs.k8s.io/kustomize/kustomize/v3 v3.10.0 h1:2ceyieTUl0Y5pRKAypYg1UkDSGV23cgxkeoBnz8obPI= -sigs.k8s.io/kustomize/kustomize/v3 v3.10.0/go.mod h1:spVXMybMUUCzxzdS3TUxJU6s3kXPACyD1lK/cG+TOXI= -sigs.k8s.io/kustomize/kyaml v0.9.4 h1:DDuzZtjIzFqp2IPy4DTyCI69Cl3bDgcJODjI6sjF9NY= -sigs.k8s.io/kustomize/kyaml v0.9.4/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw= -sigs.k8s.io/kustomize/kyaml v0.10.9 h1:n3WNdvPPReRNDxW+XXd2JlyZ8EII721I21D1DBpBVBE= -sigs.k8s.io/kustomize/kyaml v0.10.9/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/tools/src/kustomize/pin.go b/tools/src/kustomize/pin.go deleted file mode 100644 index ab0f2f9c7fd..00000000000 --- a/tools/src/kustomize/pin.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -//go:build pin -// +build pin - -package ignore - -import _ "sigs.k8s.io/kustomize/kustomize/v3" diff --git a/tools/src/protoc-gen-go-grpc/go.mod b/tools/src/protoc-gen-go-grpc/go.mod index 7d3884774c6..079bab201a9 100644 --- a/tools/src/protoc-gen-go-grpc/go.mod +++ b/tools/src/protoc-gen-go-grpc/go.mod @@ -1,6 +1,6 @@ module github.com/envoyproxy/gateway/tools/src/protoc-gen-go-grpc -go 1.20 +go 1.21 require google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 diff --git a/tools/src/protoc-gen-go/go.mod b/tools/src/protoc-gen-go/go.mod index 112de585840..5cbba95aa00 100644 --- a/tools/src/protoc-gen-go/go.mod +++ b/tools/src/protoc-gen-go/go.mod @@ -1,5 +1,5 @@ module github.com/envoyproxy/gateway/tools/src/protoc-gen-go -go 1.20 +go 1.21 require google.golang.org/protobuf v1.30.0 diff --git a/tools/src/setup-envtest/go.mod b/tools/src/setup-envtest/go.mod index 99b4fd7a633..7c22047aa6d 100644 --- a/tools/src/setup-envtest/go.mod +++ b/tools/src/setup-envtest/go.mod @@ -1,6 +1,6 @@ module local -go 1.20 +go 1.21 require sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20220706173534-cd0058ad295c diff --git a/tools/src/sphinx-build/requirements.txt b/tools/src/sphinx-build/requirements.txt index b31354afaf0..c25f298978f 100644 --- a/tools/src/sphinx-build/requirements.txt +++ b/tools/src/sphinx-build/requirements.txt @@ -1,2 +1,2 @@ -Sphinx==7.1.1 +Sphinx==7.2.6 myst-parser==2.0.0 diff --git a/tools/src/yamllint/requirements.txt b/tools/src/yamllint/requirements.txt index daa80970c9a..5caab9d5754 100644 --- a/tools/src/yamllint/requirements.txt +++ b/tools/src/yamllint/requirements.txt @@ -1 +1 @@ -yamllint==1.32.0 +yamllint==1.33.0