├── .cargo └── config.toml ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── test.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── gateway-api ├── Cargo.toml ├── examples │ ├── Cargo.toml │ └── gep2257.rs └── src │ ├── apis │ ├── experimental │ │ ├── constants.rs │ │ ├── enum_defaults.rs │ │ ├── gatewayclasses.rs │ │ ├── gateways.rs │ │ ├── grpcroutes.rs │ │ ├── httproutes.rs │ │ ├── mod.rs │ │ ├── referencegrants.rs │ │ ├── tcproutes.rs │ │ ├── tlsroutes.rs │ │ └── udproutes.rs │ ├── mod.rs │ └── standard │ │ ├── constants.rs │ │ ├── enum_defaults.rs │ │ ├── gatewayclasses.rs │ │ ├── gateways.rs │ │ ├── grpcroutes.rs │ │ ├── httproutes.rs │ │ ├── mod.rs │ │ └── referencegrants.rs │ ├── duration.rs │ └── lib.rs ├── update.sh └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run -q --manifest-path ./xtask/Cargo.toml --" 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | Cargo.lock linguist-generated=false 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | push: 8 | branches: 9 | - 'main' 10 | tags: 11 | - '*' 12 | workflow_dispatch: {} 13 | 14 | jobs: 15 | unit-tests: 16 | runs-on: ubuntu-latest 17 | steps: 18 | 19 | - name: checkout repository 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: install rust 25 | uses: actions-rs/toolchain@v1 26 | with: 27 | toolchain: stable 28 | override: true 29 | 30 | - name: run unit tests 31 | uses: actions-rs/cargo@v1 32 | with: 33 | command: test 34 | args: -v -- --nocapture 35 | 36 | integration-tests: 37 | runs-on: ubuntu-latest 38 | needs: unit-tests 39 | steps: 40 | 41 | - name: checkout repository 42 | uses: actions/checkout@v3 43 | with: 44 | fetch-depth: 0 45 | 46 | - name: install rust 47 | uses: actions-rs/toolchain@v1 48 | with: 49 | toolchain: stable 50 | override: true 51 | 52 | - name: run integration tests 53 | uses: actions-rs/cargo@v1 54 | with: 55 | command: test 56 | args: -v -- --nocapture --ignored 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["gateway-api", "gateway-api/examples", "xtask"] 3 | resolver = "2" 4 | 5 | [workspace.package] 6 | authors = ["Shane Utt "] 7 | edition = "2021" 8 | license = "MIT" 9 | version = "0.15.0" 10 | 11 | [workspace.dependencies] 12 | anyhow = "1.0.98" 13 | delegate = "0.13.3" 14 | hyper-util = "0.1.13" 15 | kube = { version = "0.99.0" } 16 | k8s-openapi = { version = "0.24.0" } 17 | once_cell = "1.21.3" 18 | regex = { package = "regex-lite", version = "0.1.6" } 19 | schemars = "0.8.22" 20 | serde_json = "1.0.140" 21 | serde = { version = "1.0.219", features = ["derive"] } 22 | serde_yaml = "0.9.34" 23 | tokio = { version = "1.45.1", features = ["full"] } 24 | tower = { version = "0.5.2", features = ["limit"] } 25 | tracing = "0.1.41" 26 | tracing-subscriber = "0.3.19" 27 | uuid = { version = "1.17.0", features = ["v4", "fast-rng"] } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shane Utt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: build generate 3 | 4 | .PHONY: clean 5 | clean: 6 | cargo clean 7 | 8 | .PHONY: build 9 | build: 10 | cargo build 11 | 12 | 13 | .PHONY: generate 14 | generate: 15 | ./update.sh 16 | 17 | .PHONY: test.all 18 | test.all: test.unit test.integration 19 | 20 | .PHONY: test.unit 21 | test.unit: 22 | cargo test -vv -- --nocapture 23 | 24 | .PHONY: test.integration 25 | test.integration: 26 | cargo test -vv -- --nocapture --ignored 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Docs](https://img.shields.io/badge/docs-docs.rs-ff69b4.svg)](https://docs.rs/gateway-api/) 2 | [![crates.io](https://img.shields.io/crates/v/gateway-api.svg)](https://crates.io/crates/gateway-api) 3 | [![License](https://img.shields.io/badge/license-mit-blue.svg)](https://raw.githubusercontent.com/kube-rs/gateway-api-rs/main/LICENSE) 4 | 5 | > **Warning**: EXPERIMENTAL. **Not ready for production use**. 6 | 7 | > **Note**: While the aspiration is to eventually become the "official" Gateway 8 | > API bindings for Rust, [Kubernetes SIG Network] has not yet (and may never) 9 | > officially endorsed it so this should be considered "unofficial" for now. 10 | 11 | [Kubernetes SIG Network]:https://github.com/kubernetes/community/tree/master/sig-network 12 | 13 | # Gateway API (Rust) 14 | 15 | > **Note**: Currently supports [Gateway API version v1.2.1][gwv] 16 | 17 | This project provides bindings in [Rust] for [Kubernetes] [Gateway API]. 18 | 19 | [gwv]:https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.1 20 | [Rust]:https://rust-lang.org 21 | [Kubernetes]:https://kubernetes.io/ 22 | [Gateway API]:https://gateway-api.sigs.k8s.io/ 23 | 24 | ## Usage 25 | 26 | Basic usage involves using a [kube-rs] [Client] to perform create, read, update 27 | and delete (CRUD) operations on [Gateway API resources]. You can either use a 28 | basic `Client` to perform CRUD operations, or you can build a [Controller]. See 29 | the `gateway-api/examples/` directory for detailed (and specific) usage examples. 30 | 31 | [kube-rs]:https://github.com/kube-rs/kube 32 | [Gateway API resources]:https://gateway-api.sigs.k8s.io/api-types/gateway/ 33 | [Client]:https://docs.rs/kube/latest/kube/struct.Client.html 34 | [Controller]:https://kube.rs/controllers/intro/ 35 | 36 | ## Development 37 | 38 | This project uses [Kopium] to automatically generate API bindings from upstream 39 | Gateway API. Make sure you install `kopium` locally in order to run the 40 | generator: 41 | 42 | ```console 43 | $ cargo install kopium --version 0.21.1 44 | ``` 45 | 46 | After which you can run the `update.sh` script: 47 | 48 | ```console 49 | $ ./update.sh 50 | ``` 51 | 52 | Check for errors and/or a non-zero exit code, but upon success you should see 53 | updates automatically generated for code in the `gateway-api/src/api` directory 54 | which you can then commit. 55 | 56 | [Kopium]:https://github.com/kube-rs/kopium 57 | 58 | ## Contributions 59 | 60 | Contributions are welcome, and appreciated! In general (for larger changes) 61 | please create an issue describing the contribution needed prior to creating a 62 | PR. 63 | 64 | If you're looking for something to do, we organize the work for this project 65 | with a [project board][board], please check out the `next` column for 66 | unassigned tasks as these are the things prioritized to be worked on in the 67 | immediate. 68 | 69 | For development support we do have an org-wide [#kube channel on the Tokio 70 | Discord server][discord], but please note that for this project in particular we 71 | prefer questions be posted in the [discussions board][forum]. 72 | 73 | [board]:https://github.com/orgs/kube-rs/projects/3 74 | [discord]:https://discord.gg/tokio 75 | [forum]:https://github.com/kube-rs/gateway-api-rs/discussions 76 | -------------------------------------------------------------------------------- /gateway-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gateway-api" 3 | description = "Kubernetes Gateway API bindings in Rust" 4 | categories = ["api-bindings"] 5 | keywords = ["kubernetes", "gateway-api"] 6 | 7 | homepage = "https://docs.rs/crate/gateway-api/" 8 | readme = "../README.md" 9 | repository = "https://github.com/kube-rs/gateway-api-rs" 10 | 11 | authors.workspace = true 12 | edition.workspace = true 13 | license.workspace = true 14 | version.workspace = true 15 | 16 | [dependencies] 17 | delegate.workspace = true 18 | k8s-openapi = { workspace = true, features = ["schemars"] } 19 | kube = { workspace = true, features = ["derive"] } 20 | once_cell.workspace = true 21 | regex.workspace = true 22 | schemars.workspace = true 23 | serde_json.workspace = true 24 | serde.workspace = true 25 | serde_yaml.workspace = true 26 | 27 | [dev-dependencies] 28 | k8s-openapi = { workspace = true, features = ["v1_32", "schemars"] } 29 | kube = { workspace = true, features = ["derive"] } 30 | 31 | anyhow.workspace = true 32 | hyper-util.workspace = true 33 | tokio.workspace = true 34 | tower.workspace = true 35 | uuid.workspace = true 36 | 37 | [package.metadata.docs.rs] 38 | features = ["k8s-openapi/v1_32"] 39 | 40 | [features] 41 | default = [] 42 | experimental = [] 43 | -------------------------------------------------------------------------------- /gateway-api/examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gateway-api-examples" 3 | publish = false 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | version.workspace = true 9 | 10 | [package.metadata.release] 11 | release = false 12 | 13 | [dependencies] 14 | gateway-api = { path = "../" } 15 | 16 | anyhow.workspace = true 17 | hyper-util.workspace = true 18 | k8s-openapi.workspace = true 19 | kube.workspace = true 20 | serde_json.workspace = true 21 | tokio.workspace = true 22 | tower.workspace = true 23 | tracing.workspace = true 24 | tracing-subscriber.workspace = true 25 | uuid.workspace = true 26 | 27 | [features] 28 | default = [ "k8s-openapi/v1_32" ] 29 | 30 | [[bin]] 31 | name = "gep2257" 32 | path = "gep2257.rs" 33 | -------------------------------------------------------------------------------- /gateway-api/examples/gep2257.rs: -------------------------------------------------------------------------------- 1 | use gateway_api::Duration; 2 | use std::env; 3 | use std::str::FromStr; 4 | 5 | /// Simple example of using the gateway_api::Duration: just parse the duration 6 | /// string given on the command line, then print it back out (which formats it). 7 | /// 8 | /// See the format specification here: https://gateway-api.sigs.k8s.io/geps/gep-2257/ 9 | /// 10 | /// Good things to try: 11 | /// cargo run --example gep2257 1h (should print "Parsed duration: 1h") 12 | /// cargo run --example gep2257 1h30m (should print "Parsed duration: 1h30m") 13 | /// cargo run --example gep2257 30m1h10s5s (should print "Parsed duration: 1h30m15s") 14 | fn main() { 15 | // Get the command line argument 16 | let args: Vec = env::args().collect(); 17 | if args.len() < 2 { 18 | println!("Please provide a duration input"); 19 | return; 20 | } 21 | 22 | let value = &args[1]; 23 | 24 | // Parse the duration input using gateway_api::Duration 25 | match Duration::from_str(value) { 26 | Ok(duration) => { 27 | println!("Parsed duration: {}", duration); 28 | } 29 | Err(error) => { 30 | eprintln!( 31 | "Failed to parse duration from: {}\nError: {:#?}", 32 | value, error 33 | ); 34 | std::process::exit(1); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/constants.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated file - manual changes will be overriden 2 | 3 | #[derive(Debug, PartialEq, Eq)] 4 | pub enum GatewayClassConditionType { 5 | Accepted, 6 | SupportedVersion, 7 | } 8 | 9 | impl std::fmt::Display for GatewayClassConditionType { 10 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 11 | write!(f, "{:?}", self) 12 | } 13 | } 14 | 15 | #[derive(Debug, PartialEq, Eq)] 16 | pub enum GatewayClassConditionReason { 17 | Accepted, 18 | InvalidParameters, 19 | Pending, 20 | Unsupported, 21 | Waiting, 22 | SupportedVersion, 23 | UnsupportedVersion, 24 | } 25 | 26 | impl std::fmt::Display for GatewayClassConditionReason { 27 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 | write!(f, "{:?}", self) 29 | } 30 | } 31 | 32 | #[derive(Debug, PartialEq, Eq)] 33 | pub enum GatewayConditionType { 34 | Programmed, 35 | Accepted, 36 | Ready, 37 | } 38 | 39 | impl std::fmt::Display for GatewayConditionType { 40 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 41 | write!(f, "{:?}", self) 42 | } 43 | } 44 | 45 | #[derive(Debug, PartialEq, Eq)] 46 | pub enum GatewayConditionReason { 47 | Programmed, 48 | Invalid, 49 | NoResources, 50 | AddressNotAssigned, 51 | AddressNotUsable, 52 | Accepted, 53 | ListenersNotValid, 54 | Pending, 55 | UnsupportedAddress, 56 | InvalidParameters, 57 | Ready, 58 | ListenersNotReady, 59 | } 60 | 61 | impl std::fmt::Display for GatewayConditionReason { 62 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 63 | write!(f, "{:?}", self) 64 | } 65 | } 66 | 67 | #[derive(Debug, PartialEq, Eq)] 68 | pub enum ListenerConditionType { 69 | Conflicted, 70 | Accepted, 71 | ResolvedRefs, 72 | Programmed, 73 | Ready, 74 | } 75 | 76 | impl std::fmt::Display for ListenerConditionType { 77 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 78 | write!(f, "{:?}", self) 79 | } 80 | } 81 | 82 | #[derive(Debug, PartialEq, Eq)] 83 | pub enum ListenerConditionReason { 84 | HostnameConflict, 85 | ProtocolConflict, 86 | NoConflicts, 87 | Accepted, 88 | PortUnavailable, 89 | UnsupportedProtocol, 90 | ResolvedRefs, 91 | InvalidCertificateRef, 92 | InvalidRouteKinds, 93 | RefNotPermitted, 94 | Programmed, 95 | Invalid, 96 | Pending, 97 | Ready, 98 | } 99 | 100 | impl std::fmt::Display for ListenerConditionReason { 101 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 102 | write!(f, "{:?}", self) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/enum_defaults.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated file - manual changes will be overriden 2 | 3 | use super::httproutes::{ 4 | HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType, HTTPRouteRulesBackendRefsFiltersType, 5 | HTTPRouteRulesBackendRefsFiltersUrlRewritePathType, 6 | HTTPRouteRulesFiltersRequestRedirectPathType, HTTPRouteRulesFiltersType, 7 | HTTPRouteRulesFiltersUrlRewritePathType, 8 | }; 9 | 10 | use super::grpcroutes::{GRPCRouteRulesBackendRefsFiltersType, GRPCRouteRulesFiltersType}; 11 | 12 | impl Default for GRPCRouteRulesBackendRefsFiltersType { 13 | fn default() -> Self { 14 | GRPCRouteRulesBackendRefsFiltersType::RequestHeaderModifier 15 | } 16 | } 17 | 18 | impl Default for GRPCRouteRulesFiltersType { 19 | fn default() -> Self { 20 | GRPCRouteRulesFiltersType::RequestHeaderModifier 21 | } 22 | } 23 | 24 | impl Default for HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType { 25 | fn default() -> Self { 26 | HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType::ReplaceFullPath 27 | } 28 | } 29 | 30 | impl Default for HTTPRouteRulesBackendRefsFiltersType { 31 | fn default() -> Self { 32 | HTTPRouteRulesBackendRefsFiltersType::RequestHeaderModifier 33 | } 34 | } 35 | 36 | impl Default for HTTPRouteRulesBackendRefsFiltersUrlRewritePathType { 37 | fn default() -> Self { 38 | HTTPRouteRulesBackendRefsFiltersUrlRewritePathType::ReplaceFullPath 39 | } 40 | } 41 | 42 | impl Default for HTTPRouteRulesFiltersRequestRedirectPathType { 43 | fn default() -> Self { 44 | HTTPRouteRulesFiltersRequestRedirectPathType::ReplaceFullPath 45 | } 46 | } 47 | 48 | impl Default for HTTPRouteRulesFiltersType { 49 | fn default() -> Self { 50 | HTTPRouteRulesFiltersType::RequestHeaderModifier 51 | } 52 | } 53 | 54 | impl Default for HTTPRouteRulesFiltersUrlRewritePathType { 55 | fn default() -> Self { 56 | HTTPRouteRulesFiltersUrlRewritePathType::ReplaceFullPath 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/gatewayclasses.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; 8 | pub use kube::CustomResource; 9 | pub use schemars::JsonSchema; 10 | pub use serde::{Deserialize, Serialize}; 11 | } 12 | use self::prelude::*; 13 | 14 | /// Spec defines the desired state of GatewayClass. 15 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 16 | #[kube( 17 | group = "gateway.networking.k8s.io", 18 | version = "v1", 19 | kind = "GatewayClass", 20 | plural = "gatewayclasses" 21 | )] 22 | #[kube(status = "GatewayClassStatus")] 23 | #[kube(derive = "Default")] 24 | #[kube(derive = "PartialEq")] 25 | pub struct GatewayClassSpec { 26 | /// ControllerName is the name of the controller that is managing Gateways of 27 | /// this class. The value of this field MUST be a domain prefixed path. 28 | /// 29 | /// Example: "example.net/gateway-controller". 30 | /// 31 | /// This field is not mutable and cannot be empty. 32 | /// 33 | /// Support: Core 34 | #[serde(rename = "controllerName")] 35 | pub controller_name: String, 36 | /// Description helps describe a GatewayClass with more details. 37 | #[serde(default, skip_serializing_if = "Option::is_none")] 38 | pub description: Option, 39 | /// ParametersRef is a reference to a resource that contains the configuration 40 | /// parameters corresponding to the GatewayClass. This is optional if the 41 | /// controller does not require any additional configuration. 42 | /// 43 | /// ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, 44 | /// or an implementation-specific custom resource. The resource can be 45 | /// cluster-scoped or namespace-scoped. 46 | /// 47 | /// If the referent cannot be found, refers to an unsupported kind, or when 48 | /// the data within that resource is malformed, the GatewayClass SHOULD be 49 | /// rejected with the "Accepted" status condition set to "False" and an 50 | /// "InvalidParameters" reason. 51 | /// 52 | /// A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, 53 | /// the merging behavior is implementation specific. 54 | /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. 55 | /// 56 | /// Support: Implementation-specific 57 | #[serde( 58 | default, 59 | skip_serializing_if = "Option::is_none", 60 | rename = "parametersRef" 61 | )] 62 | pub parameters_ref: Option, 63 | } 64 | 65 | /// ParametersRef is a reference to a resource that contains the configuration 66 | /// parameters corresponding to the GatewayClass. This is optional if the 67 | /// controller does not require any additional configuration. 68 | /// 69 | /// ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, 70 | /// or an implementation-specific custom resource. The resource can be 71 | /// cluster-scoped or namespace-scoped. 72 | /// 73 | /// If the referent cannot be found, refers to an unsupported kind, or when 74 | /// the data within that resource is malformed, the GatewayClass SHOULD be 75 | /// rejected with the "Accepted" status condition set to "False" and an 76 | /// "InvalidParameters" reason. 77 | /// 78 | /// A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, 79 | /// the merging behavior is implementation specific. 80 | /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. 81 | /// 82 | /// Support: Implementation-specific 83 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 84 | pub struct GatewayClassParametersRef { 85 | /// Group is the group of the referent. 86 | pub group: String, 87 | /// Kind is kind of the referent. 88 | pub kind: String, 89 | /// Name is the name of the referent. 90 | pub name: String, 91 | /// Namespace is the namespace of the referent. 92 | /// This field is required when referring to a Namespace-scoped resource and 93 | /// MUST be unset when referring to a Cluster-scoped resource. 94 | #[serde(default, skip_serializing_if = "Option::is_none")] 95 | pub namespace: Option, 96 | } 97 | 98 | /// Status defines the current state of GatewayClass. 99 | /// 100 | /// Implementations MUST populate status on all GatewayClass resources which 101 | /// specify their controller name. 102 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 103 | pub struct GatewayClassStatus { 104 | /// Conditions is the current status from the controller for 105 | /// this GatewayClass. 106 | /// 107 | /// Controllers should prefer to publish conditions using values 108 | /// of GatewayClassConditionType for the type of each Condition. 109 | #[serde(default, skip_serializing_if = "Option::is_none")] 110 | pub conditions: Option>, 111 | /// SupportedFeatures is the set of features the GatewayClass support. 112 | /// It MUST be sorted in ascending alphabetical order by the Name key. 113 | /// 114 | #[serde( 115 | default, 116 | skip_serializing_if = "Option::is_none", 117 | rename = "supportedFeatures" 118 | )] 119 | pub supported_features: Option>, 120 | } 121 | 122 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 123 | pub struct GatewayClassStatusSupportedFeatures { 124 | /// FeatureName is used to describe distinct features that are covered by 125 | /// conformance tests. 126 | pub name: String, 127 | } 128 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING! generated file do not edit 2 | pub mod constants; 3 | mod enum_defaults; 4 | pub mod gatewayclasses; 5 | pub mod gateways; 6 | pub mod grpcroutes; 7 | pub mod httproutes; 8 | pub mod referencegrants; 9 | pub mod tcproutes; 10 | pub mod tlsroutes; 11 | pub mod udproutes; 12 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/referencegrants.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use kube::CustomResource; 8 | pub use schemars::JsonSchema; 9 | pub use serde::{Deserialize, Serialize}; 10 | } 11 | use self::prelude::*; 12 | 13 | /// Spec defines the desired state of ReferenceGrant. 14 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 15 | #[kube( 16 | group = "gateway.networking.k8s.io", 17 | version = "v1beta1", 18 | kind = "ReferenceGrant", 19 | plural = "referencegrants" 20 | )] 21 | #[kube(namespaced)] 22 | #[kube(derive = "Default")] 23 | #[kube(derive = "PartialEq")] 24 | pub struct ReferenceGrantSpec { 25 | /// From describes the trusted namespaces and kinds that can reference the 26 | /// resources described in "To". Each entry in this list MUST be considered 27 | /// to be an additional place that references can be valid from, or to put 28 | /// this another way, entries MUST be combined using OR. 29 | /// 30 | /// Support: Core 31 | pub from: Vec, 32 | /// To describes the resources that may be referenced by the resources 33 | /// described in "From". Each entry in this list MUST be considered to be an 34 | /// additional place that references can be valid to, or to put this another 35 | /// way, entries MUST be combined using OR. 36 | /// 37 | /// Support: Core 38 | pub to: Vec, 39 | } 40 | 41 | /// ReferenceGrantFrom describes trusted namespaces and kinds. 42 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 43 | pub struct ReferenceGrantFrom { 44 | /// Group is the group of the referent. 45 | /// When empty, the Kubernetes core API group is inferred. 46 | /// 47 | /// Support: Core 48 | pub group: String, 49 | /// Kind is the kind of the referent. Although implementations may support 50 | /// additional resources, the following types are part of the "Core" 51 | /// support level for this field. 52 | /// 53 | /// When used to permit a SecretObjectReference: 54 | /// 55 | /// * Gateway 56 | /// 57 | /// When used to permit a BackendObjectReference: 58 | /// 59 | /// * GRPCRoute 60 | /// * HTTPRoute 61 | /// * TCPRoute 62 | /// * TLSRoute 63 | /// * UDPRoute 64 | pub kind: String, 65 | /// Namespace is the namespace of the referent. 66 | /// 67 | /// Support: Core 68 | pub namespace: String, 69 | } 70 | 71 | /// ReferenceGrantTo describes what Kinds are allowed as targets of the 72 | /// references. 73 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 74 | pub struct ReferenceGrantTo { 75 | /// Group is the group of the referent. 76 | /// When empty, the Kubernetes core API group is inferred. 77 | /// 78 | /// Support: Core 79 | pub group: String, 80 | /// Kind is the kind of the referent. Although implementations may support 81 | /// additional resources, the following types are part of the "Core" 82 | /// support level for this field: 83 | /// 84 | /// * Secret when used to permit a SecretObjectReference 85 | /// * Service when used to permit a BackendObjectReference 86 | pub kind: String, 87 | /// Name is the name of the referent. When unspecified, this policy 88 | /// refers to all resources of the specified Group and Kind in the local 89 | /// namespace. 90 | #[serde(default, skip_serializing_if = "Option::is_none")] 91 | pub name: Option, 92 | } 93 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/tcproutes.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; 8 | pub use kube::CustomResource; 9 | pub use schemars::JsonSchema; 10 | pub use serde::{Deserialize, Serialize}; 11 | } 12 | use self::prelude::*; 13 | 14 | /// Spec defines the desired state of TCPRoute. 15 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 16 | #[kube( 17 | group = "gateway.networking.k8s.io", 18 | version = "v1alpha2", 19 | kind = "TCPRoute", 20 | plural = "tcproutes" 21 | )] 22 | #[kube(namespaced)] 23 | #[kube(status = "TCPRouteStatus")] 24 | #[kube(derive = "Default")] 25 | #[kube(derive = "PartialEq")] 26 | pub struct TCPRouteSpec { 27 | /// ParentRefs references the resources (usually Gateways) that a Route wants 28 | /// to be attached to. Note that the referenced parent resource needs to 29 | /// allow this for the attachment to be complete. For Gateways, that means 30 | /// the Gateway needs to allow attachment from Routes of this kind and 31 | /// namespace. For Services, that means the Service must either be in the same 32 | /// namespace for a "producer" route, or the mesh implementation must support 33 | /// and allow "consumer" routes for the referenced Service. ReferenceGrant is 34 | /// not applicable for governing ParentRefs to Services - it is not possible to 35 | /// create a "producer" route for a Service in a different namespace from the 36 | /// Route. 37 | /// 38 | /// There are two kinds of parent resources with "Core" support: 39 | /// 40 | /// * Gateway (Gateway conformance profile) 41 | /// * Service (Mesh conformance profile, ClusterIP Services only) 42 | /// 43 | /// This API may be extended in the future to support additional kinds of parent 44 | /// resources. 45 | /// 46 | /// ParentRefs must be _distinct_. This means either that: 47 | /// 48 | /// * They select different objects. If this is the case, then parentRef 49 | /// entries are distinct. In terms of fields, this means that the 50 | /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must 51 | /// be unique across all parentRef entries in the Route. 52 | /// * They do not select different objects, but for each optional field used, 53 | /// each ParentRef that selects the same object must set the same set of 54 | /// optional fields to different values. If one ParentRef sets a 55 | /// combination of optional fields, all must set the same combination. 56 | /// 57 | /// Some examples: 58 | /// 59 | /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the 60 | /// same object must also set `sectionName`. 61 | /// * If one ParentRef sets `port`, all ParentRefs referencing the same 62 | /// object must also set `port`. 63 | /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs 64 | /// referencing the same object must also set `sectionName` and `port`. 65 | /// 66 | /// It is possible to separately reference multiple distinct objects that may 67 | /// be collapsed by an implementation. For example, some implementations may 68 | /// choose to merge compatible Gateway Listeners together. If that is the 69 | /// case, the list of routes attached to those resources should also be 70 | /// merged. 71 | /// 72 | /// Note that for ParentRefs that cross namespace boundaries, there are specific 73 | /// rules. Cross-namespace references are only valid if they are explicitly 74 | /// allowed by something in the namespace they are referring to. For example, 75 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 76 | /// generic way to enable other kinds of cross-namespace reference. 77 | /// 78 | /// 79 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 80 | /// routes, which apply default routing rules to inbound connections from 81 | /// any namespace to the Service. 82 | /// 83 | /// ParentRefs from a Route to a Service in a different namespace are 84 | /// "consumer" routes, and these routing rules are only applied to outbound 85 | /// connections originating from the same namespace as the Route, for which 86 | /// the intended destination of the connections are a Service targeted as a 87 | /// ParentRef of the Route. 88 | /// 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | #[serde( 95 | default, 96 | skip_serializing_if = "Option::is_none", 97 | rename = "parentRefs" 98 | )] 99 | pub parent_refs: Option>, 100 | /// Rules are a list of TCP matchers and actions. 101 | /// 102 | /// 103 | pub rules: Vec, 104 | } 105 | 106 | /// ParentReference identifies an API object (usually a Gateway) that can be considered 107 | /// a parent of this resource (usually a route). There are two kinds of parent resources 108 | /// with "Core" support: 109 | /// 110 | /// * Gateway (Gateway conformance profile) 111 | /// * Service (Mesh conformance profile, ClusterIP Services only) 112 | /// 113 | /// This API may be extended in the future to support additional kinds of parent 114 | /// resources. 115 | /// 116 | /// The API object must be valid in the cluster; the Group and Kind must 117 | /// be registered in the cluster for this reference to be valid. 118 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 119 | pub struct TCPRouteParentRefs { 120 | /// Group is the group of the referent. 121 | /// When unspecified, "gateway.networking.k8s.io" is inferred. 122 | /// To set the core API group (such as for a "Service" kind referent), 123 | /// Group must be explicitly set to "" (empty string). 124 | /// 125 | /// Support: Core 126 | #[serde(default, skip_serializing_if = "Option::is_none")] 127 | pub group: Option, 128 | /// Kind is kind of the referent. 129 | /// 130 | /// There are two kinds of parent resources with "Core" support: 131 | /// 132 | /// * Gateway (Gateway conformance profile) 133 | /// * Service (Mesh conformance profile, ClusterIP Services only) 134 | /// 135 | /// Support for other resources is Implementation-Specific. 136 | #[serde(default, skip_serializing_if = "Option::is_none")] 137 | pub kind: Option, 138 | /// Name is the name of the referent. 139 | /// 140 | /// Support: Core 141 | pub name: String, 142 | /// Namespace is the namespace of the referent. When unspecified, this refers 143 | /// to the local namespace of the Route. 144 | /// 145 | /// Note that there are specific rules for ParentRefs which cross namespace 146 | /// boundaries. Cross-namespace references are only valid if they are explicitly 147 | /// allowed by something in the namespace they are referring to. For example: 148 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 149 | /// generic way to enable any other kind of cross-namespace reference. 150 | /// 151 | /// 152 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 153 | /// routes, which apply default routing rules to inbound connections from 154 | /// any namespace to the Service. 155 | /// 156 | /// ParentRefs from a Route to a Service in a different namespace are 157 | /// "consumer" routes, and these routing rules are only applied to outbound 158 | /// connections originating from the same namespace as the Route, for which 159 | /// the intended destination of the connections are a Service targeted as a 160 | /// ParentRef of the Route. 161 | /// 162 | /// 163 | /// Support: Core 164 | #[serde(default, skip_serializing_if = "Option::is_none")] 165 | pub namespace: Option, 166 | /// Port is the network port this Route targets. It can be interpreted 167 | /// differently based on the type of parent resource. 168 | /// 169 | /// When the parent resource is a Gateway, this targets all listeners 170 | /// listening on the specified port that also support this kind of Route(and 171 | /// select this Route). It's not recommended to set `Port` unless the 172 | /// networking behaviors specified in a Route must apply to a specific port 173 | /// as opposed to a listener(s) whose port(s) may be changed. When both Port 174 | /// and SectionName are specified, the name and port of the selected listener 175 | /// must match both specified values. 176 | /// 177 | /// 178 | /// When the parent resource is a Service, this targets a specific port in the 179 | /// Service spec. When both Port (experimental) and SectionName are specified, 180 | /// the name and port of the selected port must match both specified values. 181 | /// 182 | /// 183 | /// Implementations MAY choose to support other parent resources. 184 | /// Implementations supporting other types of parent resources MUST clearly 185 | /// document how/if Port is interpreted. 186 | /// 187 | /// For the purpose of status, an attachment is considered successful as 188 | /// long as the parent resource accepts it partially. For example, Gateway 189 | /// listeners can restrict which Routes can attach to them by Route kind, 190 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment 191 | /// from the referencing Route, the Route MUST be considered successfully 192 | /// attached. If no Gateway listeners accept attachment from this Route, 193 | /// the Route MUST be considered detached from the Gateway. 194 | /// 195 | /// Support: Extended 196 | #[serde(default, skip_serializing_if = "Option::is_none")] 197 | pub port: Option, 198 | /// SectionName is the name of a section within the target resource. In the 199 | /// following resources, SectionName is interpreted as the following: 200 | /// 201 | /// * Gateway: Listener name. When both Port (experimental) and SectionName 202 | /// are specified, the name and port of the selected listener must match 203 | /// both specified values. 204 | /// * Service: Port name. When both Port (experimental) and SectionName 205 | /// are specified, the name and port of the selected listener must match 206 | /// both specified values. 207 | /// 208 | /// Implementations MAY choose to support attaching Routes to other resources. 209 | /// If that is the case, they MUST clearly document how SectionName is 210 | /// interpreted. 211 | /// 212 | /// When unspecified (empty string), this will reference the entire resource. 213 | /// For the purpose of status, an attachment is considered successful if at 214 | /// least one section in the parent resource accepts it. For example, Gateway 215 | /// listeners can restrict which Routes can attach to them by Route kind, 216 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 217 | /// the referencing Route, the Route MUST be considered successfully 218 | /// attached. If no Gateway listeners accept attachment from this Route, the 219 | /// Route MUST be considered detached from the Gateway. 220 | /// 221 | /// Support: Core 222 | #[serde( 223 | default, 224 | skip_serializing_if = "Option::is_none", 225 | rename = "sectionName" 226 | )] 227 | pub section_name: Option, 228 | } 229 | 230 | /// TCPRouteRule is the configuration for a given rule. 231 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 232 | pub struct TCPRouteRules { 233 | /// BackendRefs defines the backend(s) where matching requests should be 234 | /// sent. If unspecified or invalid (refers to a non-existent resource or a 235 | /// Service with no endpoints), the underlying implementation MUST actively 236 | /// reject connection attempts to this backend. Connection rejections must 237 | /// respect weight; if an invalid backend is requested to have 80% of 238 | /// connections, then 80% of connections must be rejected instead. 239 | /// 240 | /// Support: Core for Kubernetes Service 241 | /// 242 | /// Support: Extended for Kubernetes ServiceImport 243 | /// 244 | /// Support: Implementation-specific for any other resource 245 | /// 246 | /// Support for weight: Extended 247 | #[serde( 248 | default, 249 | skip_serializing_if = "Option::is_none", 250 | rename = "backendRefs" 251 | )] 252 | pub backend_refs: Option>, 253 | /// Name is the name of the route rule. This name MUST be unique within a Route if it is set. 254 | /// 255 | /// Support: Extended 256 | #[serde(default, skip_serializing_if = "Option::is_none")] 257 | pub name: Option, 258 | } 259 | 260 | /// BackendRef defines how a Route should forward a request to a Kubernetes 261 | /// resource. 262 | /// 263 | /// Note that when a namespace different than the local namespace is specified, a 264 | /// ReferenceGrant object is required in the referent namespace to allow that 265 | /// namespace's owner to accept the reference. See the ReferenceGrant 266 | /// documentation for details. 267 | /// 268 | /// 269 | /// 270 | /// When the BackendRef points to a Kubernetes Service, implementations SHOULD 271 | /// honor the appProtocol field if it is set for the target Service Port. 272 | /// 273 | /// Implementations supporting appProtocol SHOULD recognize the Kubernetes 274 | /// Standard Application Protocols defined in KEP-3726. 275 | /// 276 | /// If a Service appProtocol isn't specified, an implementation MAY infer the 277 | /// backend protocol through its own means. Implementations MAY infer the 278 | /// protocol from the Route type referring to the backend Service. 279 | /// 280 | /// If a Route is not able to send traffic to the backend using the specified 281 | /// protocol then the backend is considered invalid. Implementations MUST set the 282 | /// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. 283 | /// 284 | /// 285 | /// 286 | /// Note that when the BackendTLSPolicy object is enabled by the implementation, 287 | /// there are some extra rules about validity to consider here. See the fields 288 | /// where this struct is used for more information about the exact behavior. 289 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 290 | pub struct TCPRouteRulesBackendRefs { 291 | /// Group is the group of the referent. For example, "gateway.networking.k8s.io". 292 | /// When unspecified or empty string, core API group is inferred. 293 | #[serde(default, skip_serializing_if = "Option::is_none")] 294 | pub group: Option, 295 | /// Kind is the Kubernetes resource kind of the referent. For example 296 | /// "Service". 297 | /// 298 | /// Defaults to "Service" when not specified. 299 | /// 300 | /// ExternalName services can refer to CNAME DNS records that may live 301 | /// outside of the cluster and as such are difficult to reason about in 302 | /// terms of conformance. They also may not be safe to forward to (see 303 | /// CVE-2021-25740 for more information). Implementations SHOULD NOT 304 | /// support ExternalName Services. 305 | /// 306 | /// Support: Core (Services with a type other than ExternalName) 307 | /// 308 | /// Support: Implementation-specific (Services with type ExternalName) 309 | #[serde(default, skip_serializing_if = "Option::is_none")] 310 | pub kind: Option, 311 | /// Name is the name of the referent. 312 | pub name: String, 313 | /// Namespace is the namespace of the backend. When unspecified, the local 314 | /// namespace is inferred. 315 | /// 316 | /// Note that when a namespace different than the local namespace is specified, 317 | /// a ReferenceGrant object is required in the referent namespace to allow that 318 | /// namespace's owner to accept the reference. See the ReferenceGrant 319 | /// documentation for details. 320 | /// 321 | /// Support: Core 322 | #[serde(default, skip_serializing_if = "Option::is_none")] 323 | pub namespace: Option, 324 | /// Port specifies the destination port number to use for this resource. 325 | /// Port is required when the referent is a Kubernetes Service. In this 326 | /// case, the port number is the service port number, not the target port. 327 | /// For other resources, destination port might be derived from the referent 328 | /// resource or this field. 329 | #[serde(default, skip_serializing_if = "Option::is_none")] 330 | pub port: Option, 331 | /// Weight specifies the proportion of requests forwarded to the referenced 332 | /// backend. This is computed as weight/(sum of all weights in this 333 | /// BackendRefs list). For non-zero values, there may be some epsilon from 334 | /// the exact proportion defined here depending on the precision an 335 | /// implementation supports. Weight is not a percentage and the sum of 336 | /// weights does not need to equal 100. 337 | /// 338 | /// If only one backend is specified and it has a weight greater than 0, 100% 339 | /// of the traffic is forwarded to that backend. If weight is set to 0, no 340 | /// traffic should be forwarded for this entry. If unspecified, weight 341 | /// defaults to 1. 342 | /// 343 | /// Support for this field varies based on the context where used. 344 | #[serde(default, skip_serializing_if = "Option::is_none")] 345 | pub weight: Option, 346 | } 347 | 348 | /// Status defines the current state of TCPRoute. 349 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 350 | pub struct TCPRouteStatus { 351 | /// Parents is a list of parent resources (usually Gateways) that are 352 | /// associated with the route, and the status of the route with respect to 353 | /// each parent. When this route attaches to a parent, the controller that 354 | /// manages the parent must add an entry to this list when the controller 355 | /// first sees the route and should update the entry as appropriate when the 356 | /// route or gateway is modified. 357 | /// 358 | /// Note that parent references that cannot be resolved by an implementation 359 | /// of this API will not be added to this list. Implementations of this API 360 | /// can only populate Route status for the Gateways/parent resources they are 361 | /// responsible for. 362 | /// 363 | /// A maximum of 32 Gateways will be represented in this list. An empty list 364 | /// means the route has not been attached to any Gateway. 365 | pub parents: Vec, 366 | } 367 | 368 | /// RouteParentStatus describes the status of a route with respect to an 369 | /// associated Parent. 370 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 371 | pub struct TCPRouteStatusParents { 372 | /// Conditions describes the status of the route with respect to the Gateway. 373 | /// Note that the route's availability is also subject to the Gateway's own 374 | /// status conditions and listener status. 375 | /// 376 | /// If the Route's ParentRef specifies an existing Gateway that supports 377 | /// Routes of this kind AND that Gateway's controller has sufficient access, 378 | /// then that Gateway's controller MUST set the "Accepted" condition on the 379 | /// Route, to indicate whether the route has been accepted or rejected by the 380 | /// Gateway, and why. 381 | /// 382 | /// A Route MUST be considered "Accepted" if at least one of the Route's 383 | /// rules is implemented by the Gateway. 384 | /// 385 | /// There are a number of cases where the "Accepted" condition may not be set 386 | /// due to lack of controller visibility, that includes when: 387 | /// 388 | /// * The Route refers to a non-existent parent. 389 | /// * The Route is of a type that the controller does not support. 390 | /// * The Route is in a namespace the controller does not have access to. 391 | #[serde(default, skip_serializing_if = "Option::is_none")] 392 | pub conditions: Option>, 393 | /// ControllerName is a domain/path string that indicates the name of the 394 | /// controller that wrote this status. This corresponds with the 395 | /// controllerName field on GatewayClass. 396 | /// 397 | /// Example: "example.net/gateway-controller". 398 | /// 399 | /// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are 400 | /// valid Kubernetes names 401 | /// (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). 402 | /// 403 | /// Controllers MUST populate this field when writing status. Controllers should ensure that 404 | /// entries to status populated with their ControllerName are cleaned up when they are no 405 | /// longer necessary. 406 | #[serde(rename = "controllerName")] 407 | pub controller_name: String, 408 | /// ParentRef corresponds with a ParentRef in the spec that this 409 | /// RouteParentStatus struct describes the status of. 410 | #[serde(rename = "parentRef")] 411 | pub parent_ref: TCPRouteStatusParentsParentRef, 412 | } 413 | 414 | /// ParentRef corresponds with a ParentRef in the spec that this 415 | /// RouteParentStatus struct describes the status of. 416 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 417 | pub struct TCPRouteStatusParentsParentRef { 418 | /// Group is the group of the referent. 419 | /// When unspecified, "gateway.networking.k8s.io" is inferred. 420 | /// To set the core API group (such as for a "Service" kind referent), 421 | /// Group must be explicitly set to "" (empty string). 422 | /// 423 | /// Support: Core 424 | #[serde(default, skip_serializing_if = "Option::is_none")] 425 | pub group: Option, 426 | /// Kind is kind of the referent. 427 | /// 428 | /// There are two kinds of parent resources with "Core" support: 429 | /// 430 | /// * Gateway (Gateway conformance profile) 431 | /// * Service (Mesh conformance profile, ClusterIP Services only) 432 | /// 433 | /// Support for other resources is Implementation-Specific. 434 | #[serde(default, skip_serializing_if = "Option::is_none")] 435 | pub kind: Option, 436 | /// Name is the name of the referent. 437 | /// 438 | /// Support: Core 439 | pub name: String, 440 | /// Namespace is the namespace of the referent. When unspecified, this refers 441 | /// to the local namespace of the Route. 442 | /// 443 | /// Note that there are specific rules for ParentRefs which cross namespace 444 | /// boundaries. Cross-namespace references are only valid if they are explicitly 445 | /// allowed by something in the namespace they are referring to. For example: 446 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 447 | /// generic way to enable any other kind of cross-namespace reference. 448 | /// 449 | /// 450 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 451 | /// routes, which apply default routing rules to inbound connections from 452 | /// any namespace to the Service. 453 | /// 454 | /// ParentRefs from a Route to a Service in a different namespace are 455 | /// "consumer" routes, and these routing rules are only applied to outbound 456 | /// connections originating from the same namespace as the Route, for which 457 | /// the intended destination of the connections are a Service targeted as a 458 | /// ParentRef of the Route. 459 | /// 460 | /// 461 | /// Support: Core 462 | #[serde(default, skip_serializing_if = "Option::is_none")] 463 | pub namespace: Option, 464 | /// Port is the network port this Route targets. It can be interpreted 465 | /// differently based on the type of parent resource. 466 | /// 467 | /// When the parent resource is a Gateway, this targets all listeners 468 | /// listening on the specified port that also support this kind of Route(and 469 | /// select this Route). It's not recommended to set `Port` unless the 470 | /// networking behaviors specified in a Route must apply to a specific port 471 | /// as opposed to a listener(s) whose port(s) may be changed. When both Port 472 | /// and SectionName are specified, the name and port of the selected listener 473 | /// must match both specified values. 474 | /// 475 | /// 476 | /// When the parent resource is a Service, this targets a specific port in the 477 | /// Service spec. When both Port (experimental) and SectionName are specified, 478 | /// the name and port of the selected port must match both specified values. 479 | /// 480 | /// 481 | /// Implementations MAY choose to support other parent resources. 482 | /// Implementations supporting other types of parent resources MUST clearly 483 | /// document how/if Port is interpreted. 484 | /// 485 | /// For the purpose of status, an attachment is considered successful as 486 | /// long as the parent resource accepts it partially. For example, Gateway 487 | /// listeners can restrict which Routes can attach to them by Route kind, 488 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment 489 | /// from the referencing Route, the Route MUST be considered successfully 490 | /// attached. If no Gateway listeners accept attachment from this Route, 491 | /// the Route MUST be considered detached from the Gateway. 492 | /// 493 | /// Support: Extended 494 | #[serde(default, skip_serializing_if = "Option::is_none")] 495 | pub port: Option, 496 | /// SectionName is the name of a section within the target resource. In the 497 | /// following resources, SectionName is interpreted as the following: 498 | /// 499 | /// * Gateway: Listener name. When both Port (experimental) and SectionName 500 | /// are specified, the name and port of the selected listener must match 501 | /// both specified values. 502 | /// * Service: Port name. When both Port (experimental) and SectionName 503 | /// are specified, the name and port of the selected listener must match 504 | /// both specified values. 505 | /// 506 | /// Implementations MAY choose to support attaching Routes to other resources. 507 | /// If that is the case, they MUST clearly document how SectionName is 508 | /// interpreted. 509 | /// 510 | /// When unspecified (empty string), this will reference the entire resource. 511 | /// For the purpose of status, an attachment is considered successful if at 512 | /// least one section in the parent resource accepts it. For example, Gateway 513 | /// listeners can restrict which Routes can attach to them by Route kind, 514 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 515 | /// the referencing Route, the Route MUST be considered successfully 516 | /// attached. If no Gateway listeners accept attachment from this Route, the 517 | /// Route MUST be considered detached from the Gateway. 518 | /// 519 | /// Support: Core 520 | #[serde( 521 | default, 522 | skip_serializing_if = "Option::is_none", 523 | rename = "sectionName" 524 | )] 525 | pub section_name: Option, 526 | } 527 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/tlsroutes.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; 8 | pub use kube::CustomResource; 9 | pub use schemars::JsonSchema; 10 | pub use serde::{Deserialize, Serialize}; 11 | } 12 | use self::prelude::*; 13 | 14 | /// Spec defines the desired state of TLSRoute. 15 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 16 | #[kube( 17 | group = "gateway.networking.k8s.io", 18 | version = "v1alpha2", 19 | kind = "TLSRoute", 20 | plural = "tlsroutes" 21 | )] 22 | #[kube(namespaced)] 23 | #[kube(status = "TLSRouteStatus")] 24 | #[kube(derive = "Default")] 25 | #[kube(derive = "PartialEq")] 26 | pub struct TLSRouteSpec { 27 | /// Hostnames defines a set of SNI names that should match against the 28 | /// SNI attribute of TLS ClientHello message in TLS handshake. This matches 29 | /// the RFC 1123 definition of a hostname with 2 notable exceptions: 30 | /// 31 | /// 1. IPs are not allowed in SNI names per RFC 6066. 32 | /// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard 33 | /// label must appear by itself as the first label. 34 | /// 35 | /// If a hostname is specified by both the Listener and TLSRoute, there 36 | /// must be at least one intersecting hostname for the TLSRoute to be 37 | /// attached to the Listener. For example: 38 | /// 39 | /// * A Listener with `test.example.com` as the hostname matches TLSRoutes 40 | /// that have either not specified any hostnames, or have specified at 41 | /// least one of `test.example.com` or `*.example.com`. 42 | /// * A Listener with `*.example.com` as the hostname matches TLSRoutes 43 | /// that have either not specified any hostnames or have specified at least 44 | /// one hostname that matches the Listener hostname. For example, 45 | /// `test.example.com` and `*.example.com` would both match. On the other 46 | /// hand, `example.com` and `test.example.net` would not match. 47 | /// 48 | /// If both the Listener and TLSRoute have specified hostnames, any 49 | /// TLSRoute hostnames that do not match the Listener hostname MUST be 50 | /// ignored. For example, if a Listener specified `*.example.com`, and the 51 | /// TLSRoute specified `test.example.com` and `test.example.net`, 52 | /// `test.example.net` must not be considered for a match. 53 | /// 54 | /// If both the Listener and TLSRoute have specified hostnames, and none 55 | /// match with the criteria above, then the TLSRoute is not accepted. The 56 | /// implementation must raise an 'Accepted' Condition with a status of 57 | /// `False` in the corresponding RouteParentStatus. 58 | /// 59 | /// Support: Core 60 | #[serde(default, skip_serializing_if = "Option::is_none")] 61 | pub hostnames: Option>, 62 | /// ParentRefs references the resources (usually Gateways) that a Route wants 63 | /// to be attached to. Note that the referenced parent resource needs to 64 | /// allow this for the attachment to be complete. For Gateways, that means 65 | /// the Gateway needs to allow attachment from Routes of this kind and 66 | /// namespace. For Services, that means the Service must either be in the same 67 | /// namespace for a "producer" route, or the mesh implementation must support 68 | /// and allow "consumer" routes for the referenced Service. ReferenceGrant is 69 | /// not applicable for governing ParentRefs to Services - it is not possible to 70 | /// create a "producer" route for a Service in a different namespace from the 71 | /// Route. 72 | /// 73 | /// There are two kinds of parent resources with "Core" support: 74 | /// 75 | /// * Gateway (Gateway conformance profile) 76 | /// * Service (Mesh conformance profile, ClusterIP Services only) 77 | /// 78 | /// This API may be extended in the future to support additional kinds of parent 79 | /// resources. 80 | /// 81 | /// ParentRefs must be _distinct_. This means either that: 82 | /// 83 | /// * They select different objects. If this is the case, then parentRef 84 | /// entries are distinct. In terms of fields, this means that the 85 | /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must 86 | /// be unique across all parentRef entries in the Route. 87 | /// * They do not select different objects, but for each optional field used, 88 | /// each ParentRef that selects the same object must set the same set of 89 | /// optional fields to different values. If one ParentRef sets a 90 | /// combination of optional fields, all must set the same combination. 91 | /// 92 | /// Some examples: 93 | /// 94 | /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the 95 | /// same object must also set `sectionName`. 96 | /// * If one ParentRef sets `port`, all ParentRefs referencing the same 97 | /// object must also set `port`. 98 | /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs 99 | /// referencing the same object must also set `sectionName` and `port`. 100 | /// 101 | /// It is possible to separately reference multiple distinct objects that may 102 | /// be collapsed by an implementation. For example, some implementations may 103 | /// choose to merge compatible Gateway Listeners together. If that is the 104 | /// case, the list of routes attached to those resources should also be 105 | /// merged. 106 | /// 107 | /// Note that for ParentRefs that cross namespace boundaries, there are specific 108 | /// rules. Cross-namespace references are only valid if they are explicitly 109 | /// allowed by something in the namespace they are referring to. For example, 110 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 111 | /// generic way to enable other kinds of cross-namespace reference. 112 | /// 113 | /// 114 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 115 | /// routes, which apply default routing rules to inbound connections from 116 | /// any namespace to the Service. 117 | /// 118 | /// ParentRefs from a Route to a Service in a different namespace are 119 | /// "consumer" routes, and these routing rules are only applied to outbound 120 | /// connections originating from the same namespace as the Route, for which 121 | /// the intended destination of the connections are a Service targeted as a 122 | /// ParentRef of the Route. 123 | /// 124 | /// 125 | /// 126 | /// 127 | /// 128 | /// 129 | #[serde( 130 | default, 131 | skip_serializing_if = "Option::is_none", 132 | rename = "parentRefs" 133 | )] 134 | pub parent_refs: Option>, 135 | /// Rules are a list of TLS matchers and actions. 136 | /// 137 | /// 138 | pub rules: Vec, 139 | } 140 | 141 | /// ParentReference identifies an API object (usually a Gateway) that can be considered 142 | /// a parent of this resource (usually a route). There are two kinds of parent resources 143 | /// with "Core" support: 144 | /// 145 | /// * Gateway (Gateway conformance profile) 146 | /// * Service (Mesh conformance profile, ClusterIP Services only) 147 | /// 148 | /// This API may be extended in the future to support additional kinds of parent 149 | /// resources. 150 | /// 151 | /// The API object must be valid in the cluster; the Group and Kind must 152 | /// be registered in the cluster for this reference to be valid. 153 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 154 | pub struct TLSRouteParentRefs { 155 | /// Group is the group of the referent. 156 | /// When unspecified, "gateway.networking.k8s.io" is inferred. 157 | /// To set the core API group (such as for a "Service" kind referent), 158 | /// Group must be explicitly set to "" (empty string). 159 | /// 160 | /// Support: Core 161 | #[serde(default, skip_serializing_if = "Option::is_none")] 162 | pub group: Option, 163 | /// Kind is kind of the referent. 164 | /// 165 | /// There are two kinds of parent resources with "Core" support: 166 | /// 167 | /// * Gateway (Gateway conformance profile) 168 | /// * Service (Mesh conformance profile, ClusterIP Services only) 169 | /// 170 | /// Support for other resources is Implementation-Specific. 171 | #[serde(default, skip_serializing_if = "Option::is_none")] 172 | pub kind: Option, 173 | /// Name is the name of the referent. 174 | /// 175 | /// Support: Core 176 | pub name: String, 177 | /// Namespace is the namespace of the referent. When unspecified, this refers 178 | /// to the local namespace of the Route. 179 | /// 180 | /// Note that there are specific rules for ParentRefs which cross namespace 181 | /// boundaries. Cross-namespace references are only valid if they are explicitly 182 | /// allowed by something in the namespace they are referring to. For example: 183 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 184 | /// generic way to enable any other kind of cross-namespace reference. 185 | /// 186 | /// 187 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 188 | /// routes, which apply default routing rules to inbound connections from 189 | /// any namespace to the Service. 190 | /// 191 | /// ParentRefs from a Route to a Service in a different namespace are 192 | /// "consumer" routes, and these routing rules are only applied to outbound 193 | /// connections originating from the same namespace as the Route, for which 194 | /// the intended destination of the connections are a Service targeted as a 195 | /// ParentRef of the Route. 196 | /// 197 | /// 198 | /// Support: Core 199 | #[serde(default, skip_serializing_if = "Option::is_none")] 200 | pub namespace: Option, 201 | /// Port is the network port this Route targets. It can be interpreted 202 | /// differently based on the type of parent resource. 203 | /// 204 | /// When the parent resource is a Gateway, this targets all listeners 205 | /// listening on the specified port that also support this kind of Route(and 206 | /// select this Route). It's not recommended to set `Port` unless the 207 | /// networking behaviors specified in a Route must apply to a specific port 208 | /// as opposed to a listener(s) whose port(s) may be changed. When both Port 209 | /// and SectionName are specified, the name and port of the selected listener 210 | /// must match both specified values. 211 | /// 212 | /// 213 | /// When the parent resource is a Service, this targets a specific port in the 214 | /// Service spec. When both Port (experimental) and SectionName are specified, 215 | /// the name and port of the selected port must match both specified values. 216 | /// 217 | /// 218 | /// Implementations MAY choose to support other parent resources. 219 | /// Implementations supporting other types of parent resources MUST clearly 220 | /// document how/if Port is interpreted. 221 | /// 222 | /// For the purpose of status, an attachment is considered successful as 223 | /// long as the parent resource accepts it partially. For example, Gateway 224 | /// listeners can restrict which Routes can attach to them by Route kind, 225 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment 226 | /// from the referencing Route, the Route MUST be considered successfully 227 | /// attached. If no Gateway listeners accept attachment from this Route, 228 | /// the Route MUST be considered detached from the Gateway. 229 | /// 230 | /// Support: Extended 231 | #[serde(default, skip_serializing_if = "Option::is_none")] 232 | pub port: Option, 233 | /// SectionName is the name of a section within the target resource. In the 234 | /// following resources, SectionName is interpreted as the following: 235 | /// 236 | /// * Gateway: Listener name. When both Port (experimental) and SectionName 237 | /// are specified, the name and port of the selected listener must match 238 | /// both specified values. 239 | /// * Service: Port name. When both Port (experimental) and SectionName 240 | /// are specified, the name and port of the selected listener must match 241 | /// both specified values. 242 | /// 243 | /// Implementations MAY choose to support attaching Routes to other resources. 244 | /// If that is the case, they MUST clearly document how SectionName is 245 | /// interpreted. 246 | /// 247 | /// When unspecified (empty string), this will reference the entire resource. 248 | /// For the purpose of status, an attachment is considered successful if at 249 | /// least one section in the parent resource accepts it. For example, Gateway 250 | /// listeners can restrict which Routes can attach to them by Route kind, 251 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 252 | /// the referencing Route, the Route MUST be considered successfully 253 | /// attached. If no Gateway listeners accept attachment from this Route, the 254 | /// Route MUST be considered detached from the Gateway. 255 | /// 256 | /// Support: Core 257 | #[serde( 258 | default, 259 | skip_serializing_if = "Option::is_none", 260 | rename = "sectionName" 261 | )] 262 | pub section_name: Option, 263 | } 264 | 265 | /// TLSRouteRule is the configuration for a given rule. 266 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 267 | pub struct TLSRouteRules { 268 | /// BackendRefs defines the backend(s) where matching requests should be 269 | /// sent. If unspecified or invalid (refers to a non-existent resource or 270 | /// a Service with no endpoints), the rule performs no forwarding; if no 271 | /// filters are specified that would result in a response being sent, the 272 | /// underlying implementation must actively reject request attempts to this 273 | /// backend, by rejecting the connection or returning a 500 status code. 274 | /// Request rejections must respect weight; if an invalid backend is 275 | /// requested to have 80% of requests, then 80% of requests must be rejected 276 | /// instead. 277 | /// 278 | /// Support: Core for Kubernetes Service 279 | /// 280 | /// Support: Extended for Kubernetes ServiceImport 281 | /// 282 | /// Support: Implementation-specific for any other resource 283 | /// 284 | /// Support for weight: Extended 285 | #[serde( 286 | default, 287 | skip_serializing_if = "Option::is_none", 288 | rename = "backendRefs" 289 | )] 290 | pub backend_refs: Option>, 291 | /// Name is the name of the route rule. This name MUST be unique within a Route if it is set. 292 | /// 293 | /// Support: Extended 294 | #[serde(default, skip_serializing_if = "Option::is_none")] 295 | pub name: Option, 296 | } 297 | 298 | /// BackendRef defines how a Route should forward a request to a Kubernetes 299 | /// resource. 300 | /// 301 | /// Note that when a namespace different than the local namespace is specified, a 302 | /// ReferenceGrant object is required in the referent namespace to allow that 303 | /// namespace's owner to accept the reference. See the ReferenceGrant 304 | /// documentation for details. 305 | /// 306 | /// 307 | /// 308 | /// When the BackendRef points to a Kubernetes Service, implementations SHOULD 309 | /// honor the appProtocol field if it is set for the target Service Port. 310 | /// 311 | /// Implementations supporting appProtocol SHOULD recognize the Kubernetes 312 | /// Standard Application Protocols defined in KEP-3726. 313 | /// 314 | /// If a Service appProtocol isn't specified, an implementation MAY infer the 315 | /// backend protocol through its own means. Implementations MAY infer the 316 | /// protocol from the Route type referring to the backend Service. 317 | /// 318 | /// If a Route is not able to send traffic to the backend using the specified 319 | /// protocol then the backend is considered invalid. Implementations MUST set the 320 | /// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. 321 | /// 322 | /// 323 | /// 324 | /// Note that when the BackendTLSPolicy object is enabled by the implementation, 325 | /// there are some extra rules about validity to consider here. See the fields 326 | /// where this struct is used for more information about the exact behavior. 327 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 328 | pub struct TLSRouteRulesBackendRefs { 329 | /// Group is the group of the referent. For example, "gateway.networking.k8s.io". 330 | /// When unspecified or empty string, core API group is inferred. 331 | #[serde(default, skip_serializing_if = "Option::is_none")] 332 | pub group: Option, 333 | /// Kind is the Kubernetes resource kind of the referent. For example 334 | /// "Service". 335 | /// 336 | /// Defaults to "Service" when not specified. 337 | /// 338 | /// ExternalName services can refer to CNAME DNS records that may live 339 | /// outside of the cluster and as such are difficult to reason about in 340 | /// terms of conformance. They also may not be safe to forward to (see 341 | /// CVE-2021-25740 for more information). Implementations SHOULD NOT 342 | /// support ExternalName Services. 343 | /// 344 | /// Support: Core (Services with a type other than ExternalName) 345 | /// 346 | /// Support: Implementation-specific (Services with type ExternalName) 347 | #[serde(default, skip_serializing_if = "Option::is_none")] 348 | pub kind: Option, 349 | /// Name is the name of the referent. 350 | pub name: String, 351 | /// Namespace is the namespace of the backend. When unspecified, the local 352 | /// namespace is inferred. 353 | /// 354 | /// Note that when a namespace different than the local namespace is specified, 355 | /// a ReferenceGrant object is required in the referent namespace to allow that 356 | /// namespace's owner to accept the reference. See the ReferenceGrant 357 | /// documentation for details. 358 | /// 359 | /// Support: Core 360 | #[serde(default, skip_serializing_if = "Option::is_none")] 361 | pub namespace: Option, 362 | /// Port specifies the destination port number to use for this resource. 363 | /// Port is required when the referent is a Kubernetes Service. In this 364 | /// case, the port number is the service port number, not the target port. 365 | /// For other resources, destination port might be derived from the referent 366 | /// resource or this field. 367 | #[serde(default, skip_serializing_if = "Option::is_none")] 368 | pub port: Option, 369 | /// Weight specifies the proportion of requests forwarded to the referenced 370 | /// backend. This is computed as weight/(sum of all weights in this 371 | /// BackendRefs list). For non-zero values, there may be some epsilon from 372 | /// the exact proportion defined here depending on the precision an 373 | /// implementation supports. Weight is not a percentage and the sum of 374 | /// weights does not need to equal 100. 375 | /// 376 | /// If only one backend is specified and it has a weight greater than 0, 100% 377 | /// of the traffic is forwarded to that backend. If weight is set to 0, no 378 | /// traffic should be forwarded for this entry. If unspecified, weight 379 | /// defaults to 1. 380 | /// 381 | /// Support for this field varies based on the context where used. 382 | #[serde(default, skip_serializing_if = "Option::is_none")] 383 | pub weight: Option, 384 | } 385 | 386 | /// Status defines the current state of TLSRoute. 387 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 388 | pub struct TLSRouteStatus { 389 | /// Parents is a list of parent resources (usually Gateways) that are 390 | /// associated with the route, and the status of the route with respect to 391 | /// each parent. When this route attaches to a parent, the controller that 392 | /// manages the parent must add an entry to this list when the controller 393 | /// first sees the route and should update the entry as appropriate when the 394 | /// route or gateway is modified. 395 | /// 396 | /// Note that parent references that cannot be resolved by an implementation 397 | /// of this API will not be added to this list. Implementations of this API 398 | /// can only populate Route status for the Gateways/parent resources they are 399 | /// responsible for. 400 | /// 401 | /// A maximum of 32 Gateways will be represented in this list. An empty list 402 | /// means the route has not been attached to any Gateway. 403 | pub parents: Vec, 404 | } 405 | 406 | /// RouteParentStatus describes the status of a route with respect to an 407 | /// associated Parent. 408 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 409 | pub struct TLSRouteStatusParents { 410 | /// Conditions describes the status of the route with respect to the Gateway. 411 | /// Note that the route's availability is also subject to the Gateway's own 412 | /// status conditions and listener status. 413 | /// 414 | /// If the Route's ParentRef specifies an existing Gateway that supports 415 | /// Routes of this kind AND that Gateway's controller has sufficient access, 416 | /// then that Gateway's controller MUST set the "Accepted" condition on the 417 | /// Route, to indicate whether the route has been accepted or rejected by the 418 | /// Gateway, and why. 419 | /// 420 | /// A Route MUST be considered "Accepted" if at least one of the Route's 421 | /// rules is implemented by the Gateway. 422 | /// 423 | /// There are a number of cases where the "Accepted" condition may not be set 424 | /// due to lack of controller visibility, that includes when: 425 | /// 426 | /// * The Route refers to a non-existent parent. 427 | /// * The Route is of a type that the controller does not support. 428 | /// * The Route is in a namespace the controller does not have access to. 429 | #[serde(default, skip_serializing_if = "Option::is_none")] 430 | pub conditions: Option>, 431 | /// ControllerName is a domain/path string that indicates the name of the 432 | /// controller that wrote this status. This corresponds with the 433 | /// controllerName field on GatewayClass. 434 | /// 435 | /// Example: "example.net/gateway-controller". 436 | /// 437 | /// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are 438 | /// valid Kubernetes names 439 | /// (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). 440 | /// 441 | /// Controllers MUST populate this field when writing status. Controllers should ensure that 442 | /// entries to status populated with their ControllerName are cleaned up when they are no 443 | /// longer necessary. 444 | #[serde(rename = "controllerName")] 445 | pub controller_name: String, 446 | /// ParentRef corresponds with a ParentRef in the spec that this 447 | /// RouteParentStatus struct describes the status of. 448 | #[serde(rename = "parentRef")] 449 | pub parent_ref: TLSRouteStatusParentsParentRef, 450 | } 451 | 452 | /// ParentRef corresponds with a ParentRef in the spec that this 453 | /// RouteParentStatus struct describes the status of. 454 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 455 | pub struct TLSRouteStatusParentsParentRef { 456 | /// Group is the group of the referent. 457 | /// When unspecified, "gateway.networking.k8s.io" is inferred. 458 | /// To set the core API group (such as for a "Service" kind referent), 459 | /// Group must be explicitly set to "" (empty string). 460 | /// 461 | /// Support: Core 462 | #[serde(default, skip_serializing_if = "Option::is_none")] 463 | pub group: Option, 464 | /// Kind is kind of the referent. 465 | /// 466 | /// There are two kinds of parent resources with "Core" support: 467 | /// 468 | /// * Gateway (Gateway conformance profile) 469 | /// * Service (Mesh conformance profile, ClusterIP Services only) 470 | /// 471 | /// Support for other resources is Implementation-Specific. 472 | #[serde(default, skip_serializing_if = "Option::is_none")] 473 | pub kind: Option, 474 | /// Name is the name of the referent. 475 | /// 476 | /// Support: Core 477 | pub name: String, 478 | /// Namespace is the namespace of the referent. When unspecified, this refers 479 | /// to the local namespace of the Route. 480 | /// 481 | /// Note that there are specific rules for ParentRefs which cross namespace 482 | /// boundaries. Cross-namespace references are only valid if they are explicitly 483 | /// allowed by something in the namespace they are referring to. For example: 484 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 485 | /// generic way to enable any other kind of cross-namespace reference. 486 | /// 487 | /// 488 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 489 | /// routes, which apply default routing rules to inbound connections from 490 | /// any namespace to the Service. 491 | /// 492 | /// ParentRefs from a Route to a Service in a different namespace are 493 | /// "consumer" routes, and these routing rules are only applied to outbound 494 | /// connections originating from the same namespace as the Route, for which 495 | /// the intended destination of the connections are a Service targeted as a 496 | /// ParentRef of the Route. 497 | /// 498 | /// 499 | /// Support: Core 500 | #[serde(default, skip_serializing_if = "Option::is_none")] 501 | pub namespace: Option, 502 | /// Port is the network port this Route targets. It can be interpreted 503 | /// differently based on the type of parent resource. 504 | /// 505 | /// When the parent resource is a Gateway, this targets all listeners 506 | /// listening on the specified port that also support this kind of Route(and 507 | /// select this Route). It's not recommended to set `Port` unless the 508 | /// networking behaviors specified in a Route must apply to a specific port 509 | /// as opposed to a listener(s) whose port(s) may be changed. When both Port 510 | /// and SectionName are specified, the name and port of the selected listener 511 | /// must match both specified values. 512 | /// 513 | /// 514 | /// When the parent resource is a Service, this targets a specific port in the 515 | /// Service spec. When both Port (experimental) and SectionName are specified, 516 | /// the name and port of the selected port must match both specified values. 517 | /// 518 | /// 519 | /// Implementations MAY choose to support other parent resources. 520 | /// Implementations supporting other types of parent resources MUST clearly 521 | /// document how/if Port is interpreted. 522 | /// 523 | /// For the purpose of status, an attachment is considered successful as 524 | /// long as the parent resource accepts it partially. For example, Gateway 525 | /// listeners can restrict which Routes can attach to them by Route kind, 526 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment 527 | /// from the referencing Route, the Route MUST be considered successfully 528 | /// attached. If no Gateway listeners accept attachment from this Route, 529 | /// the Route MUST be considered detached from the Gateway. 530 | /// 531 | /// Support: Extended 532 | #[serde(default, skip_serializing_if = "Option::is_none")] 533 | pub port: Option, 534 | /// SectionName is the name of a section within the target resource. In the 535 | /// following resources, SectionName is interpreted as the following: 536 | /// 537 | /// * Gateway: Listener name. When both Port (experimental) and SectionName 538 | /// are specified, the name and port of the selected listener must match 539 | /// both specified values. 540 | /// * Service: Port name. When both Port (experimental) and SectionName 541 | /// are specified, the name and port of the selected listener must match 542 | /// both specified values. 543 | /// 544 | /// Implementations MAY choose to support attaching Routes to other resources. 545 | /// If that is the case, they MUST clearly document how SectionName is 546 | /// interpreted. 547 | /// 548 | /// When unspecified (empty string), this will reference the entire resource. 549 | /// For the purpose of status, an attachment is considered successful if at 550 | /// least one section in the parent resource accepts it. For example, Gateway 551 | /// listeners can restrict which Routes can attach to them by Route kind, 552 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 553 | /// the referencing Route, the Route MUST be considered successfully 554 | /// attached. If no Gateway listeners accept attachment from this Route, the 555 | /// Route MUST be considered detached from the Gateway. 556 | /// 557 | /// Support: Core 558 | #[serde( 559 | default, 560 | skip_serializing_if = "Option::is_none", 561 | rename = "sectionName" 562 | )] 563 | pub section_name: Option, 564 | } 565 | -------------------------------------------------------------------------------- /gateway-api/src/apis/experimental/udproutes.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; 8 | pub use kube::CustomResource; 9 | pub use schemars::JsonSchema; 10 | pub use serde::{Deserialize, Serialize}; 11 | } 12 | use self::prelude::*; 13 | 14 | /// Spec defines the desired state of UDPRoute. 15 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 16 | #[kube( 17 | group = "gateway.networking.k8s.io", 18 | version = "v1alpha2", 19 | kind = "UDPRoute", 20 | plural = "udproutes" 21 | )] 22 | #[kube(namespaced)] 23 | #[kube(status = "UDPRouteStatus")] 24 | #[kube(derive = "Default")] 25 | #[kube(derive = "PartialEq")] 26 | pub struct UDPRouteSpec { 27 | /// ParentRefs references the resources (usually Gateways) that a Route wants 28 | /// to be attached to. Note that the referenced parent resource needs to 29 | /// allow this for the attachment to be complete. For Gateways, that means 30 | /// the Gateway needs to allow attachment from Routes of this kind and 31 | /// namespace. For Services, that means the Service must either be in the same 32 | /// namespace for a "producer" route, or the mesh implementation must support 33 | /// and allow "consumer" routes for the referenced Service. ReferenceGrant is 34 | /// not applicable for governing ParentRefs to Services - it is not possible to 35 | /// create a "producer" route for a Service in a different namespace from the 36 | /// Route. 37 | /// 38 | /// There are two kinds of parent resources with "Core" support: 39 | /// 40 | /// * Gateway (Gateway conformance profile) 41 | /// * Service (Mesh conformance profile, ClusterIP Services only) 42 | /// 43 | /// This API may be extended in the future to support additional kinds of parent 44 | /// resources. 45 | /// 46 | /// ParentRefs must be _distinct_. This means either that: 47 | /// 48 | /// * They select different objects. If this is the case, then parentRef 49 | /// entries are distinct. In terms of fields, this means that the 50 | /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must 51 | /// be unique across all parentRef entries in the Route. 52 | /// * They do not select different objects, but for each optional field used, 53 | /// each ParentRef that selects the same object must set the same set of 54 | /// optional fields to different values. If one ParentRef sets a 55 | /// combination of optional fields, all must set the same combination. 56 | /// 57 | /// Some examples: 58 | /// 59 | /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the 60 | /// same object must also set `sectionName`. 61 | /// * If one ParentRef sets `port`, all ParentRefs referencing the same 62 | /// object must also set `port`. 63 | /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs 64 | /// referencing the same object must also set `sectionName` and `port`. 65 | /// 66 | /// It is possible to separately reference multiple distinct objects that may 67 | /// be collapsed by an implementation. For example, some implementations may 68 | /// choose to merge compatible Gateway Listeners together. If that is the 69 | /// case, the list of routes attached to those resources should also be 70 | /// merged. 71 | /// 72 | /// Note that for ParentRefs that cross namespace boundaries, there are specific 73 | /// rules. Cross-namespace references are only valid if they are explicitly 74 | /// allowed by something in the namespace they are referring to. For example, 75 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 76 | /// generic way to enable other kinds of cross-namespace reference. 77 | /// 78 | /// 79 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 80 | /// routes, which apply default routing rules to inbound connections from 81 | /// any namespace to the Service. 82 | /// 83 | /// ParentRefs from a Route to a Service in a different namespace are 84 | /// "consumer" routes, and these routing rules are only applied to outbound 85 | /// connections originating from the same namespace as the Route, for which 86 | /// the intended destination of the connections are a Service targeted as a 87 | /// ParentRef of the Route. 88 | /// 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | #[serde( 95 | default, 96 | skip_serializing_if = "Option::is_none", 97 | rename = "parentRefs" 98 | )] 99 | pub parent_refs: Option>, 100 | /// Rules are a list of UDP matchers and actions. 101 | /// 102 | /// 103 | pub rules: Vec, 104 | } 105 | 106 | /// ParentReference identifies an API object (usually a Gateway) that can be considered 107 | /// a parent of this resource (usually a route). There are two kinds of parent resources 108 | /// with "Core" support: 109 | /// 110 | /// * Gateway (Gateway conformance profile) 111 | /// * Service (Mesh conformance profile, ClusterIP Services only) 112 | /// 113 | /// This API may be extended in the future to support additional kinds of parent 114 | /// resources. 115 | /// 116 | /// The API object must be valid in the cluster; the Group and Kind must 117 | /// be registered in the cluster for this reference to be valid. 118 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 119 | pub struct UDPRouteParentRefs { 120 | /// Group is the group of the referent. 121 | /// When unspecified, "gateway.networking.k8s.io" is inferred. 122 | /// To set the core API group (such as for a "Service" kind referent), 123 | /// Group must be explicitly set to "" (empty string). 124 | /// 125 | /// Support: Core 126 | #[serde(default, skip_serializing_if = "Option::is_none")] 127 | pub group: Option, 128 | /// Kind is kind of the referent. 129 | /// 130 | /// There are two kinds of parent resources with "Core" support: 131 | /// 132 | /// * Gateway (Gateway conformance profile) 133 | /// * Service (Mesh conformance profile, ClusterIP Services only) 134 | /// 135 | /// Support for other resources is Implementation-Specific. 136 | #[serde(default, skip_serializing_if = "Option::is_none")] 137 | pub kind: Option, 138 | /// Name is the name of the referent. 139 | /// 140 | /// Support: Core 141 | pub name: String, 142 | /// Namespace is the namespace of the referent. When unspecified, this refers 143 | /// to the local namespace of the Route. 144 | /// 145 | /// Note that there are specific rules for ParentRefs which cross namespace 146 | /// boundaries. Cross-namespace references are only valid if they are explicitly 147 | /// allowed by something in the namespace they are referring to. For example: 148 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 149 | /// generic way to enable any other kind of cross-namespace reference. 150 | /// 151 | /// 152 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 153 | /// routes, which apply default routing rules to inbound connections from 154 | /// any namespace to the Service. 155 | /// 156 | /// ParentRefs from a Route to a Service in a different namespace are 157 | /// "consumer" routes, and these routing rules are only applied to outbound 158 | /// connections originating from the same namespace as the Route, for which 159 | /// the intended destination of the connections are a Service targeted as a 160 | /// ParentRef of the Route. 161 | /// 162 | /// 163 | /// Support: Core 164 | #[serde(default, skip_serializing_if = "Option::is_none")] 165 | pub namespace: Option, 166 | /// Port is the network port this Route targets. It can be interpreted 167 | /// differently based on the type of parent resource. 168 | /// 169 | /// When the parent resource is a Gateway, this targets all listeners 170 | /// listening on the specified port that also support this kind of Route(and 171 | /// select this Route). It's not recommended to set `Port` unless the 172 | /// networking behaviors specified in a Route must apply to a specific port 173 | /// as opposed to a listener(s) whose port(s) may be changed. When both Port 174 | /// and SectionName are specified, the name and port of the selected listener 175 | /// must match both specified values. 176 | /// 177 | /// 178 | /// When the parent resource is a Service, this targets a specific port in the 179 | /// Service spec. When both Port (experimental) and SectionName are specified, 180 | /// the name and port of the selected port must match both specified values. 181 | /// 182 | /// 183 | /// Implementations MAY choose to support other parent resources. 184 | /// Implementations supporting other types of parent resources MUST clearly 185 | /// document how/if Port is interpreted. 186 | /// 187 | /// For the purpose of status, an attachment is considered successful as 188 | /// long as the parent resource accepts it partially. For example, Gateway 189 | /// listeners can restrict which Routes can attach to them by Route kind, 190 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment 191 | /// from the referencing Route, the Route MUST be considered successfully 192 | /// attached. If no Gateway listeners accept attachment from this Route, 193 | /// the Route MUST be considered detached from the Gateway. 194 | /// 195 | /// Support: Extended 196 | #[serde(default, skip_serializing_if = "Option::is_none")] 197 | pub port: Option, 198 | /// SectionName is the name of a section within the target resource. In the 199 | /// following resources, SectionName is interpreted as the following: 200 | /// 201 | /// * Gateway: Listener name. When both Port (experimental) and SectionName 202 | /// are specified, the name and port of the selected listener must match 203 | /// both specified values. 204 | /// * Service: Port name. When both Port (experimental) and SectionName 205 | /// are specified, the name and port of the selected listener must match 206 | /// both specified values. 207 | /// 208 | /// Implementations MAY choose to support attaching Routes to other resources. 209 | /// If that is the case, they MUST clearly document how SectionName is 210 | /// interpreted. 211 | /// 212 | /// When unspecified (empty string), this will reference the entire resource. 213 | /// For the purpose of status, an attachment is considered successful if at 214 | /// least one section in the parent resource accepts it. For example, Gateway 215 | /// listeners can restrict which Routes can attach to them by Route kind, 216 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 217 | /// the referencing Route, the Route MUST be considered successfully 218 | /// attached. If no Gateway listeners accept attachment from this Route, the 219 | /// Route MUST be considered detached from the Gateway. 220 | /// 221 | /// Support: Core 222 | #[serde( 223 | default, 224 | skip_serializing_if = "Option::is_none", 225 | rename = "sectionName" 226 | )] 227 | pub section_name: Option, 228 | } 229 | 230 | /// UDPRouteRule is the configuration for a given rule. 231 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 232 | pub struct UDPRouteRules { 233 | /// BackendRefs defines the backend(s) where matching requests should be 234 | /// sent. If unspecified or invalid (refers to a non-existent resource or a 235 | /// Service with no endpoints), the underlying implementation MUST actively 236 | /// reject connection attempts to this backend. Packet drops must 237 | /// respect weight; if an invalid backend is requested to have 80% of 238 | /// the packets, then 80% of packets must be dropped instead. 239 | /// 240 | /// Support: Core for Kubernetes Service 241 | /// 242 | /// Support: Extended for Kubernetes ServiceImport 243 | /// 244 | /// Support: Implementation-specific for any other resource 245 | /// 246 | /// Support for weight: Extended 247 | #[serde( 248 | default, 249 | skip_serializing_if = "Option::is_none", 250 | rename = "backendRefs" 251 | )] 252 | pub backend_refs: Option>, 253 | /// Name is the name of the route rule. This name MUST be unique within a Route if it is set. 254 | /// 255 | /// Support: Extended 256 | #[serde(default, skip_serializing_if = "Option::is_none")] 257 | pub name: Option, 258 | } 259 | 260 | /// BackendRef defines how a Route should forward a request to a Kubernetes 261 | /// resource. 262 | /// 263 | /// Note that when a namespace different than the local namespace is specified, a 264 | /// ReferenceGrant object is required in the referent namespace to allow that 265 | /// namespace's owner to accept the reference. See the ReferenceGrant 266 | /// documentation for details. 267 | /// 268 | /// 269 | /// 270 | /// When the BackendRef points to a Kubernetes Service, implementations SHOULD 271 | /// honor the appProtocol field if it is set for the target Service Port. 272 | /// 273 | /// Implementations supporting appProtocol SHOULD recognize the Kubernetes 274 | /// Standard Application Protocols defined in KEP-3726. 275 | /// 276 | /// If a Service appProtocol isn't specified, an implementation MAY infer the 277 | /// backend protocol through its own means. Implementations MAY infer the 278 | /// protocol from the Route type referring to the backend Service. 279 | /// 280 | /// If a Route is not able to send traffic to the backend using the specified 281 | /// protocol then the backend is considered invalid. Implementations MUST set the 282 | /// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. 283 | /// 284 | /// 285 | /// 286 | /// Note that when the BackendTLSPolicy object is enabled by the implementation, 287 | /// there are some extra rules about validity to consider here. See the fields 288 | /// where this struct is used for more information about the exact behavior. 289 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 290 | pub struct UDPRouteRulesBackendRefs { 291 | /// Group is the group of the referent. For example, "gateway.networking.k8s.io". 292 | /// When unspecified or empty string, core API group is inferred. 293 | #[serde(default, skip_serializing_if = "Option::is_none")] 294 | pub group: Option, 295 | /// Kind is the Kubernetes resource kind of the referent. For example 296 | /// "Service". 297 | /// 298 | /// Defaults to "Service" when not specified. 299 | /// 300 | /// ExternalName services can refer to CNAME DNS records that may live 301 | /// outside of the cluster and as such are difficult to reason about in 302 | /// terms of conformance. They also may not be safe to forward to (see 303 | /// CVE-2021-25740 for more information). Implementations SHOULD NOT 304 | /// support ExternalName Services. 305 | /// 306 | /// Support: Core (Services with a type other than ExternalName) 307 | /// 308 | /// Support: Implementation-specific (Services with type ExternalName) 309 | #[serde(default, skip_serializing_if = "Option::is_none")] 310 | pub kind: Option, 311 | /// Name is the name of the referent. 312 | pub name: String, 313 | /// Namespace is the namespace of the backend. When unspecified, the local 314 | /// namespace is inferred. 315 | /// 316 | /// Note that when a namespace different than the local namespace is specified, 317 | /// a ReferenceGrant object is required in the referent namespace to allow that 318 | /// namespace's owner to accept the reference. See the ReferenceGrant 319 | /// documentation for details. 320 | /// 321 | /// Support: Core 322 | #[serde(default, skip_serializing_if = "Option::is_none")] 323 | pub namespace: Option, 324 | /// Port specifies the destination port number to use for this resource. 325 | /// Port is required when the referent is a Kubernetes Service. In this 326 | /// case, the port number is the service port number, not the target port. 327 | /// For other resources, destination port might be derived from the referent 328 | /// resource or this field. 329 | #[serde(default, skip_serializing_if = "Option::is_none")] 330 | pub port: Option, 331 | /// Weight specifies the proportion of requests forwarded to the referenced 332 | /// backend. This is computed as weight/(sum of all weights in this 333 | /// BackendRefs list). For non-zero values, there may be some epsilon from 334 | /// the exact proportion defined here depending on the precision an 335 | /// implementation supports. Weight is not a percentage and the sum of 336 | /// weights does not need to equal 100. 337 | /// 338 | /// If only one backend is specified and it has a weight greater than 0, 100% 339 | /// of the traffic is forwarded to that backend. If weight is set to 0, no 340 | /// traffic should be forwarded for this entry. If unspecified, weight 341 | /// defaults to 1. 342 | /// 343 | /// Support for this field varies based on the context where used. 344 | #[serde(default, skip_serializing_if = "Option::is_none")] 345 | pub weight: Option, 346 | } 347 | 348 | /// Status defines the current state of UDPRoute. 349 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 350 | pub struct UDPRouteStatus { 351 | /// Parents is a list of parent resources (usually Gateways) that are 352 | /// associated with the route, and the status of the route with respect to 353 | /// each parent. When this route attaches to a parent, the controller that 354 | /// manages the parent must add an entry to this list when the controller 355 | /// first sees the route and should update the entry as appropriate when the 356 | /// route or gateway is modified. 357 | /// 358 | /// Note that parent references that cannot be resolved by an implementation 359 | /// of this API will not be added to this list. Implementations of this API 360 | /// can only populate Route status for the Gateways/parent resources they are 361 | /// responsible for. 362 | /// 363 | /// A maximum of 32 Gateways will be represented in this list. An empty list 364 | /// means the route has not been attached to any Gateway. 365 | pub parents: Vec, 366 | } 367 | 368 | /// RouteParentStatus describes the status of a route with respect to an 369 | /// associated Parent. 370 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 371 | pub struct UDPRouteStatusParents { 372 | /// Conditions describes the status of the route with respect to the Gateway. 373 | /// Note that the route's availability is also subject to the Gateway's own 374 | /// status conditions and listener status. 375 | /// 376 | /// If the Route's ParentRef specifies an existing Gateway that supports 377 | /// Routes of this kind AND that Gateway's controller has sufficient access, 378 | /// then that Gateway's controller MUST set the "Accepted" condition on the 379 | /// Route, to indicate whether the route has been accepted or rejected by the 380 | /// Gateway, and why. 381 | /// 382 | /// A Route MUST be considered "Accepted" if at least one of the Route's 383 | /// rules is implemented by the Gateway. 384 | /// 385 | /// There are a number of cases where the "Accepted" condition may not be set 386 | /// due to lack of controller visibility, that includes when: 387 | /// 388 | /// * The Route refers to a non-existent parent. 389 | /// * The Route is of a type that the controller does not support. 390 | /// * The Route is in a namespace the controller does not have access to. 391 | #[serde(default, skip_serializing_if = "Option::is_none")] 392 | pub conditions: Option>, 393 | /// ControllerName is a domain/path string that indicates the name of the 394 | /// controller that wrote this status. This corresponds with the 395 | /// controllerName field on GatewayClass. 396 | /// 397 | /// Example: "example.net/gateway-controller". 398 | /// 399 | /// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are 400 | /// valid Kubernetes names 401 | /// (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). 402 | /// 403 | /// Controllers MUST populate this field when writing status. Controllers should ensure that 404 | /// entries to status populated with their ControllerName are cleaned up when they are no 405 | /// longer necessary. 406 | #[serde(rename = "controllerName")] 407 | pub controller_name: String, 408 | /// ParentRef corresponds with a ParentRef in the spec that this 409 | /// RouteParentStatus struct describes the status of. 410 | #[serde(rename = "parentRef")] 411 | pub parent_ref: UDPRouteStatusParentsParentRef, 412 | } 413 | 414 | /// ParentRef corresponds with a ParentRef in the spec that this 415 | /// RouteParentStatus struct describes the status of. 416 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 417 | pub struct UDPRouteStatusParentsParentRef { 418 | /// Group is the group of the referent. 419 | /// When unspecified, "gateway.networking.k8s.io" is inferred. 420 | /// To set the core API group (such as for a "Service" kind referent), 421 | /// Group must be explicitly set to "" (empty string). 422 | /// 423 | /// Support: Core 424 | #[serde(default, skip_serializing_if = "Option::is_none")] 425 | pub group: Option, 426 | /// Kind is kind of the referent. 427 | /// 428 | /// There are two kinds of parent resources with "Core" support: 429 | /// 430 | /// * Gateway (Gateway conformance profile) 431 | /// * Service (Mesh conformance profile, ClusterIP Services only) 432 | /// 433 | /// Support for other resources is Implementation-Specific. 434 | #[serde(default, skip_serializing_if = "Option::is_none")] 435 | pub kind: Option, 436 | /// Name is the name of the referent. 437 | /// 438 | /// Support: Core 439 | pub name: String, 440 | /// Namespace is the namespace of the referent. When unspecified, this refers 441 | /// to the local namespace of the Route. 442 | /// 443 | /// Note that there are specific rules for ParentRefs which cross namespace 444 | /// boundaries. Cross-namespace references are only valid if they are explicitly 445 | /// allowed by something in the namespace they are referring to. For example: 446 | /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a 447 | /// generic way to enable any other kind of cross-namespace reference. 448 | /// 449 | /// 450 | /// ParentRefs from a Route to a Service in the same namespace are "producer" 451 | /// routes, which apply default routing rules to inbound connections from 452 | /// any namespace to the Service. 453 | /// 454 | /// ParentRefs from a Route to a Service in a different namespace are 455 | /// "consumer" routes, and these routing rules are only applied to outbound 456 | /// connections originating from the same namespace as the Route, for which 457 | /// the intended destination of the connections are a Service targeted as a 458 | /// ParentRef of the Route. 459 | /// 460 | /// 461 | /// Support: Core 462 | #[serde(default, skip_serializing_if = "Option::is_none")] 463 | pub namespace: Option, 464 | /// Port is the network port this Route targets. It can be interpreted 465 | /// differently based on the type of parent resource. 466 | /// 467 | /// When the parent resource is a Gateway, this targets all listeners 468 | /// listening on the specified port that also support this kind of Route(and 469 | /// select this Route). It's not recommended to set `Port` unless the 470 | /// networking behaviors specified in a Route must apply to a specific port 471 | /// as opposed to a listener(s) whose port(s) may be changed. When both Port 472 | /// and SectionName are specified, the name and port of the selected listener 473 | /// must match both specified values. 474 | /// 475 | /// 476 | /// When the parent resource is a Service, this targets a specific port in the 477 | /// Service spec. When both Port (experimental) and SectionName are specified, 478 | /// the name and port of the selected port must match both specified values. 479 | /// 480 | /// 481 | /// Implementations MAY choose to support other parent resources. 482 | /// Implementations supporting other types of parent resources MUST clearly 483 | /// document how/if Port is interpreted. 484 | /// 485 | /// For the purpose of status, an attachment is considered successful as 486 | /// long as the parent resource accepts it partially. For example, Gateway 487 | /// listeners can restrict which Routes can attach to them by Route kind, 488 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment 489 | /// from the referencing Route, the Route MUST be considered successfully 490 | /// attached. If no Gateway listeners accept attachment from this Route, 491 | /// the Route MUST be considered detached from the Gateway. 492 | /// 493 | /// Support: Extended 494 | #[serde(default, skip_serializing_if = "Option::is_none")] 495 | pub port: Option, 496 | /// SectionName is the name of a section within the target resource. In the 497 | /// following resources, SectionName is interpreted as the following: 498 | /// 499 | /// * Gateway: Listener name. When both Port (experimental) and SectionName 500 | /// are specified, the name and port of the selected listener must match 501 | /// both specified values. 502 | /// * Service: Port name. When both Port (experimental) and SectionName 503 | /// are specified, the name and port of the selected listener must match 504 | /// both specified values. 505 | /// 506 | /// Implementations MAY choose to support attaching Routes to other resources. 507 | /// If that is the case, they MUST clearly document how SectionName is 508 | /// interpreted. 509 | /// 510 | /// When unspecified (empty string), this will reference the entire resource. 511 | /// For the purpose of status, an attachment is considered successful if at 512 | /// least one section in the parent resource accepts it. For example, Gateway 513 | /// listeners can restrict which Routes can attach to them by Route kind, 514 | /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 515 | /// the referencing Route, the Route MUST be considered successfully 516 | /// attached. If no Gateway listeners accept attachment from this Route, the 517 | /// Route MUST be considered detached from the Gateway. 518 | /// 519 | /// Support: Core 520 | #[serde( 521 | default, 522 | skip_serializing_if = "Option::is_none", 523 | rename = "sectionName" 524 | )] 525 | pub section_name: Option, 526 | } 527 | -------------------------------------------------------------------------------- /gateway-api/src/apis/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod experimental; 2 | pub mod standard; 3 | -------------------------------------------------------------------------------- /gateway-api/src/apis/standard/constants.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated file - manual changes will be overriden 2 | 3 | #[derive(Debug, PartialEq, Eq)] 4 | pub enum GatewayClassConditionType { 5 | Accepted, 6 | } 7 | 8 | impl std::fmt::Display for GatewayClassConditionType { 9 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 10 | write!(f, "{:?}", self) 11 | } 12 | } 13 | 14 | #[derive(Debug, PartialEq, Eq)] 15 | pub enum GatewayClassConditionReason { 16 | Accepted, 17 | InvalidParameters, 18 | Pending, 19 | Unsupported, 20 | Waiting, 21 | } 22 | 23 | impl std::fmt::Display for GatewayClassConditionReason { 24 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 25 | write!(f, "{:?}", self) 26 | } 27 | } 28 | 29 | #[derive(Debug, PartialEq, Eq)] 30 | pub enum GatewayConditionType { 31 | Programmed, 32 | Accepted, 33 | Ready, 34 | } 35 | 36 | impl std::fmt::Display for GatewayConditionType { 37 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 38 | write!(f, "{:?}", self) 39 | } 40 | } 41 | 42 | #[derive(Debug, PartialEq, Eq)] 43 | pub enum GatewayConditionReason { 44 | Programmed, 45 | Invalid, 46 | NoResources, 47 | AddressNotAssigned, 48 | AddressNotUsable, 49 | Accepted, 50 | ListenersNotValid, 51 | Pending, 52 | UnsupportedAddress, 53 | InvalidParameters, 54 | Ready, 55 | ListenersNotReady, 56 | } 57 | 58 | impl std::fmt::Display for GatewayConditionReason { 59 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 60 | write!(f, "{:?}", self) 61 | } 62 | } 63 | 64 | #[derive(Debug, PartialEq, Eq)] 65 | pub enum ListenerConditionType { 66 | Conflicted, 67 | Accepted, 68 | ResolvedRefs, 69 | Programmed, 70 | Ready, 71 | } 72 | 73 | impl std::fmt::Display for ListenerConditionType { 74 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 75 | write!(f, "{:?}", self) 76 | } 77 | } 78 | 79 | #[derive(Debug, PartialEq, Eq)] 80 | pub enum ListenerConditionReason { 81 | HostnameConflict, 82 | ProtocolConflict, 83 | NoConflicts, 84 | Accepted, 85 | PortUnavailable, 86 | UnsupportedProtocol, 87 | ResolvedRefs, 88 | InvalidCertificateRef, 89 | InvalidRouteKinds, 90 | RefNotPermitted, 91 | Programmed, 92 | Invalid, 93 | Pending, 94 | Ready, 95 | } 96 | 97 | impl std::fmt::Display for ListenerConditionReason { 98 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 99 | write!(f, "{:?}", self) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /gateway-api/src/apis/standard/enum_defaults.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated file - manual changes will be overriden 2 | 3 | use super::httproutes::{ 4 | HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType, HTTPRouteRulesBackendRefsFiltersType, 5 | HTTPRouteRulesBackendRefsFiltersUrlRewritePathType, 6 | HTTPRouteRulesFiltersRequestRedirectPathType, HTTPRouteRulesFiltersType, 7 | HTTPRouteRulesFiltersUrlRewritePathType, 8 | }; 9 | 10 | use super::grpcroutes::{GRPCRouteRulesBackendRefsFiltersType, GRPCRouteRulesFiltersType}; 11 | 12 | impl Default for GRPCRouteRulesBackendRefsFiltersType { 13 | fn default() -> Self { 14 | GRPCRouteRulesBackendRefsFiltersType::RequestHeaderModifier 15 | } 16 | } 17 | 18 | impl Default for GRPCRouteRulesFiltersType { 19 | fn default() -> Self { 20 | GRPCRouteRulesFiltersType::RequestHeaderModifier 21 | } 22 | } 23 | 24 | impl Default for HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType { 25 | fn default() -> Self { 26 | HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType::ReplaceFullPath 27 | } 28 | } 29 | 30 | impl Default for HTTPRouteRulesBackendRefsFiltersType { 31 | fn default() -> Self { 32 | HTTPRouteRulesBackendRefsFiltersType::RequestHeaderModifier 33 | } 34 | } 35 | 36 | impl Default for HTTPRouteRulesBackendRefsFiltersUrlRewritePathType { 37 | fn default() -> Self { 38 | HTTPRouteRulesBackendRefsFiltersUrlRewritePathType::ReplaceFullPath 39 | } 40 | } 41 | 42 | impl Default for HTTPRouteRulesFiltersRequestRedirectPathType { 43 | fn default() -> Self { 44 | HTTPRouteRulesFiltersRequestRedirectPathType::ReplaceFullPath 45 | } 46 | } 47 | 48 | impl Default for HTTPRouteRulesFiltersType { 49 | fn default() -> Self { 50 | HTTPRouteRulesFiltersType::RequestHeaderModifier 51 | } 52 | } 53 | 54 | impl Default for HTTPRouteRulesFiltersUrlRewritePathType { 55 | fn default() -> Self { 56 | HTTPRouteRulesFiltersUrlRewritePathType::ReplaceFullPath 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /gateway-api/src/apis/standard/gatewayclasses.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; 8 | pub use kube::CustomResource; 9 | pub use schemars::JsonSchema; 10 | pub use serde::{Deserialize, Serialize}; 11 | } 12 | use self::prelude::*; 13 | 14 | /// Spec defines the desired state of GatewayClass. 15 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 16 | #[kube( 17 | group = "gateway.networking.k8s.io", 18 | version = "v1", 19 | kind = "GatewayClass", 20 | plural = "gatewayclasses" 21 | )] 22 | #[kube(status = "GatewayClassStatus")] 23 | #[kube(derive = "Default")] 24 | #[kube(derive = "PartialEq")] 25 | pub struct GatewayClassSpec { 26 | /// ControllerName is the name of the controller that is managing Gateways of 27 | /// this class. The value of this field MUST be a domain prefixed path. 28 | /// 29 | /// Example: "example.net/gateway-controller". 30 | /// 31 | /// This field is not mutable and cannot be empty. 32 | /// 33 | /// Support: Core 34 | #[serde(rename = "controllerName")] 35 | pub controller_name: String, 36 | /// Description helps describe a GatewayClass with more details. 37 | #[serde(default, skip_serializing_if = "Option::is_none")] 38 | pub description: Option, 39 | /// ParametersRef is a reference to a resource that contains the configuration 40 | /// parameters corresponding to the GatewayClass. This is optional if the 41 | /// controller does not require any additional configuration. 42 | /// 43 | /// ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, 44 | /// or an implementation-specific custom resource. The resource can be 45 | /// cluster-scoped or namespace-scoped. 46 | /// 47 | /// If the referent cannot be found, refers to an unsupported kind, or when 48 | /// the data within that resource is malformed, the GatewayClass SHOULD be 49 | /// rejected with the "Accepted" status condition set to "False" and an 50 | /// "InvalidParameters" reason. 51 | /// 52 | /// A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, 53 | /// the merging behavior is implementation specific. 54 | /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. 55 | /// 56 | /// Support: Implementation-specific 57 | #[serde( 58 | default, 59 | skip_serializing_if = "Option::is_none", 60 | rename = "parametersRef" 61 | )] 62 | pub parameters_ref: Option, 63 | } 64 | 65 | /// ParametersRef is a reference to a resource that contains the configuration 66 | /// parameters corresponding to the GatewayClass. This is optional if the 67 | /// controller does not require any additional configuration. 68 | /// 69 | /// ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, 70 | /// or an implementation-specific custom resource. The resource can be 71 | /// cluster-scoped or namespace-scoped. 72 | /// 73 | /// If the referent cannot be found, refers to an unsupported kind, or when 74 | /// the data within that resource is malformed, the GatewayClass SHOULD be 75 | /// rejected with the "Accepted" status condition set to "False" and an 76 | /// "InvalidParameters" reason. 77 | /// 78 | /// A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, 79 | /// the merging behavior is implementation specific. 80 | /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. 81 | /// 82 | /// Support: Implementation-specific 83 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 84 | pub struct GatewayClassParametersRef { 85 | /// Group is the group of the referent. 86 | pub group: String, 87 | /// Kind is kind of the referent. 88 | pub kind: String, 89 | /// Name is the name of the referent. 90 | pub name: String, 91 | /// Namespace is the namespace of the referent. 92 | /// This field is required when referring to a Namespace-scoped resource and 93 | /// MUST be unset when referring to a Cluster-scoped resource. 94 | #[serde(default, skip_serializing_if = "Option::is_none")] 95 | pub namespace: Option, 96 | } 97 | 98 | /// Status defines the current state of GatewayClass. 99 | /// 100 | /// Implementations MUST populate status on all GatewayClass resources which 101 | /// specify their controller name. 102 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 103 | pub struct GatewayClassStatus { 104 | /// Conditions is the current status from the controller for 105 | /// this GatewayClass. 106 | /// 107 | /// Controllers should prefer to publish conditions using values 108 | /// of GatewayClassConditionType for the type of each Condition. 109 | #[serde(default, skip_serializing_if = "Option::is_none")] 110 | pub conditions: Option>, 111 | } 112 | -------------------------------------------------------------------------------- /gateway-api/src/apis/standard/gateways.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; 8 | pub use kube::CustomResource; 9 | pub use schemars::JsonSchema; 10 | pub use serde::{Deserialize, Serialize}; 11 | pub use std::collections::BTreeMap; 12 | } 13 | use self::prelude::*; 14 | 15 | /// Spec defines the desired state of Gateway. 16 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 17 | #[kube( 18 | group = "gateway.networking.k8s.io", 19 | version = "v1", 20 | kind = "Gateway", 21 | plural = "gateways" 22 | )] 23 | #[kube(namespaced)] 24 | #[kube(status = "GatewayStatus")] 25 | #[kube(derive = "Default")] 26 | #[kube(derive = "PartialEq")] 27 | pub struct GatewaySpec { 28 | /// Addresses requested for this Gateway. This is optional and behavior can 29 | /// depend on the implementation. If a value is set in the spec and the 30 | /// requested address is invalid or unavailable, the implementation MUST 31 | /// indicate this in the associated entry in GatewayStatus.Addresses. 32 | /// 33 | /// The Addresses field represents a request for the address(es) on the 34 | /// "outside of the Gateway", that traffic bound for this Gateway will use. 35 | /// This could be the IP address or hostname of an external load balancer or 36 | /// other networking infrastructure, or some other address that traffic will 37 | /// be sent to. 38 | /// 39 | /// If no Addresses are specified, the implementation MAY schedule the 40 | /// Gateway in an implementation-specific manner, assigning an appropriate 41 | /// set of Addresses. 42 | /// 43 | /// The implementation MUST bind all Listeners to every GatewayAddress that 44 | /// it assigns to the Gateway and add a corresponding entry in 45 | /// GatewayStatus.Addresses. 46 | /// 47 | /// Support: Extended 48 | /// 49 | /// 50 | #[serde(default, skip_serializing_if = "Option::is_none")] 51 | pub addresses: Option>, 52 | /// GatewayClassName used for this Gateway. This is the name of a 53 | /// GatewayClass resource. 54 | #[serde(rename = "gatewayClassName")] 55 | pub gateway_class_name: String, 56 | /// Infrastructure defines infrastructure level attributes about this Gateway instance. 57 | /// 58 | /// Support: Extended 59 | #[serde(default, skip_serializing_if = "Option::is_none")] 60 | pub infrastructure: Option, 61 | /// Listeners associated with this Gateway. Listeners define 62 | /// logical endpoints that are bound on this Gateway's addresses. 63 | /// At least one Listener MUST be specified. 64 | /// 65 | /// Each Listener in a set of Listeners (for example, in a single Gateway) 66 | /// MUST be _distinct_, in that a traffic flow MUST be able to be assigned to 67 | /// exactly one listener. (This section uses "set of Listeners" rather than 68 | /// "Listeners in a single Gateway" because implementations MAY merge configuration 69 | /// from multiple Gateways onto a single data plane, and these rules _also_ 70 | /// apply in that case). 71 | /// 72 | /// Practically, this means that each listener in a set MUST have a unique 73 | /// combination of Port, Protocol, and, if supported by the protocol, Hostname. 74 | /// 75 | /// Some combinations of port, protocol, and TLS settings are considered 76 | /// Core support and MUST be supported by implementations based on their 77 | /// targeted conformance profile: 78 | /// 79 | /// HTTP Profile 80 | /// 81 | /// 1. HTTPRoute, Port: 80, Protocol: HTTP 82 | /// 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided 83 | /// 84 | /// TLS Profile 85 | /// 86 | /// 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough 87 | /// 88 | /// "Distinct" Listeners have the following property: 89 | /// 90 | /// The implementation can match inbound requests to a single distinct 91 | /// Listener. When multiple Listeners share values for fields (for 92 | /// example, two Listeners with the same Port value), the implementation 93 | /// can match requests to only one of the Listeners using other 94 | /// Listener fields. 95 | /// 96 | /// For example, the following Listener scenarios are distinct: 97 | /// 98 | /// 1. Multiple Listeners with the same Port that all use the "HTTP" 99 | /// Protocol that all have unique Hostname values. 100 | /// 2. Multiple Listeners with the same Port that use either the "HTTPS" or 101 | /// "TLS" Protocol that all have unique Hostname values. 102 | /// 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener 103 | /// with the same Protocol has the same Port value. 104 | /// 105 | /// Some fields in the Listener struct have possible values that affect 106 | /// whether the Listener is distinct. Hostname is particularly relevant 107 | /// for HTTP or HTTPS protocols. 108 | /// 109 | /// When using the Hostname value to select between same-Port, same-Protocol 110 | /// Listeners, the Hostname value must be different on each Listener for the 111 | /// Listener to be distinct. 112 | /// 113 | /// When the Listeners are distinct based on Hostname, inbound request 114 | /// hostnames MUST match from the most specific to least specific Hostname 115 | /// values to choose the correct Listener and its associated set of Routes. 116 | /// 117 | /// Exact matches must be processed before wildcard matches, and wildcard 118 | /// matches must be processed before fallback (empty Hostname value) 119 | /// matches. For example, `"foo.example.com"` takes precedence over 120 | /// `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. 121 | /// 122 | /// Additionally, if there are multiple wildcard entries, more specific 123 | /// wildcard entries must be processed before less specific wildcard entries. 124 | /// For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. 125 | /// The precise definition here is that the higher the number of dots in the 126 | /// hostname to the right of the wildcard character, the higher the precedence. 127 | /// 128 | /// The wildcard character will match any number of characters _and dots_ to 129 | /// the left, however, so `"*.example.com"` will match both 130 | /// `"foo.bar.example.com"` _and_ `"bar.example.com"`. 131 | /// 132 | /// If a set of Listeners contains Listeners that are not distinct, then those 133 | /// Listeners are Conflicted, and the implementation MUST set the "Conflicted" 134 | /// condition in the Listener Status to "True". 135 | /// 136 | /// Implementations MAY choose to accept a Gateway with some Conflicted 137 | /// Listeners only if they only accept the partial Listener set that contains 138 | /// no Conflicted Listeners. To put this another way, implementations may 139 | /// accept a partial Listener set only if they throw out *all* the conflicting 140 | /// Listeners. No picking one of the conflicting listeners as the winner. 141 | /// This also means that the Gateway must have at least one non-conflicting 142 | /// Listener in this case, otherwise it violates the requirement that at 143 | /// least one Listener must be present. 144 | /// 145 | /// The implementation MUST set a "ListenersNotValid" condition on the 146 | /// Gateway Status when the Gateway contains Conflicted Listeners whether or 147 | /// not they accept the Gateway. That Condition SHOULD clearly 148 | /// indicate in the Message which Listeners are conflicted, and which are 149 | /// Accepted. Additionally, the Listener status for those listeners SHOULD 150 | /// indicate which Listeners are conflicted and not Accepted. 151 | /// 152 | /// A Gateway's Listeners are considered "compatible" if: 153 | /// 154 | /// 1. They are distinct. 155 | /// 2. The implementation can serve them in compliance with the Addresses 156 | /// requirement that all Listeners are available on all assigned 157 | /// addresses. 158 | /// 159 | /// Compatible combinations in Extended support are expected to vary across 160 | /// implementations. A combination that is compatible for one implementation 161 | /// may not be compatible for another. 162 | /// 163 | /// For example, an implementation that cannot serve both TCP and UDP listeners 164 | /// on the same address, or cannot mix HTTPS and generic TLS listens on the same port 165 | /// would not consider those cases compatible, even though they are distinct. 166 | /// 167 | /// Note that requests SHOULD match at most one Listener. For example, if 168 | /// Listeners are defined for "foo.example.com" and "*.example.com", a 169 | /// request to "foo.example.com" SHOULD only be routed using routes attached 170 | /// to the "foo.example.com" Listener (and not the "*.example.com" Listener). 171 | /// This concept is known as "Listener Isolation". Implementations that do 172 | /// not support Listener Isolation MUST clearly document this. 173 | /// 174 | /// Implementations MAY merge separate Gateways onto a single set of 175 | /// Addresses if all Listeners across all Gateways are compatible. 176 | /// 177 | /// Support: Core 178 | pub listeners: Vec, 179 | } 180 | 181 | /// GatewayAddress describes an address that can be bound to a Gateway. 182 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 183 | pub struct GatewayAddresses { 184 | /// Type of the address. 185 | #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")] 186 | pub r#type: Option, 187 | /// Value of the address. The validity of the values will depend 188 | /// on the type and support by the controller. 189 | /// 190 | /// Examples: `1.2.3.4`, `128::1`, `my-ip-address`. 191 | pub value: String, 192 | } 193 | 194 | /// Infrastructure defines infrastructure level attributes about this Gateway instance. 195 | /// 196 | /// Support: Extended 197 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 198 | pub struct GatewayInfrastructure { 199 | /// Annotations that SHOULD be applied to any resources created in response to this Gateway. 200 | /// 201 | /// For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. 202 | /// For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. 203 | /// 204 | /// An implementation may chose to add additional implementation-specific annotations as they see fit. 205 | /// 206 | /// Support: Extended 207 | #[serde(default, skip_serializing_if = "Option::is_none")] 208 | pub annotations: Option>, 209 | /// Labels that SHOULD be applied to any resources created in response to this Gateway. 210 | /// 211 | /// For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. 212 | /// For other implementations, this refers to any relevant (implementation specific) "labels" concepts. 213 | /// 214 | /// An implementation may chose to add additional implementation-specific labels as they see fit. 215 | /// 216 | /// If an implementation maps these labels to Pods, or any other resource that would need to be recreated when labels 217 | /// change, it SHOULD clearly warn about this behavior in documentation. 218 | /// 219 | /// Support: Extended 220 | #[serde(default, skip_serializing_if = "Option::is_none")] 221 | pub labels: Option>, 222 | /// ParametersRef is a reference to a resource that contains the configuration 223 | /// parameters corresponding to the Gateway. This is optional if the 224 | /// controller does not require any additional configuration. 225 | /// 226 | /// This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis 227 | /// 228 | /// The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, 229 | /// the merging behavior is implementation specific. 230 | /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. 231 | /// 232 | /// Support: Implementation-specific 233 | #[serde( 234 | default, 235 | skip_serializing_if = "Option::is_none", 236 | rename = "parametersRef" 237 | )] 238 | pub parameters_ref: Option, 239 | } 240 | 241 | /// ParametersRef is a reference to a resource that contains the configuration 242 | /// parameters corresponding to the Gateway. This is optional if the 243 | /// controller does not require any additional configuration. 244 | /// 245 | /// This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis 246 | /// 247 | /// The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, 248 | /// the merging behavior is implementation specific. 249 | /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. 250 | /// 251 | /// Support: Implementation-specific 252 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 253 | pub struct GatewayInfrastructureParametersRef { 254 | /// Group is the group of the referent. 255 | pub group: String, 256 | /// Kind is kind of the referent. 257 | pub kind: String, 258 | /// Name is the name of the referent. 259 | pub name: String, 260 | } 261 | 262 | /// Listener embodies the concept of a logical endpoint where a Gateway accepts 263 | /// network connections. 264 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 265 | pub struct GatewayListeners { 266 | /// AllowedRoutes defines the types of routes that MAY be attached to a 267 | /// Listener and the trusted namespaces where those Route resources MAY be 268 | /// present. 269 | /// 270 | /// Although a client request may match multiple route rules, only one rule 271 | /// may ultimately receive the request. Matching precedence MUST be 272 | /// determined in order of the following criteria: 273 | /// 274 | /// * The most specific match as defined by the Route type. 275 | /// * The oldest Route based on creation timestamp. For example, a Route with 276 | /// a creation timestamp of "2020-09-08 01:02:03" is given precedence over 277 | /// a Route with a creation timestamp of "2020-09-08 01:02:04". 278 | /// * If everything else is equivalent, the Route appearing first in 279 | /// alphabetical order (namespace/name) should be given precedence. For 280 | /// example, foo/bar is given precedence over foo/baz. 281 | /// 282 | /// All valid rules within a Route attached to this Listener should be 283 | /// implemented. Invalid Route rules can be ignored (sometimes that will mean 284 | /// the full Route). If a Route rule transitions from valid to invalid, 285 | /// support for that Route rule should be dropped to ensure consistency. For 286 | /// example, even if a filter specified by a Route rule is invalid, the rest 287 | /// of the rules within that Route should still be supported. 288 | /// 289 | /// Support: Core 290 | #[serde( 291 | default, 292 | skip_serializing_if = "Option::is_none", 293 | rename = "allowedRoutes" 294 | )] 295 | pub allowed_routes: Option, 296 | /// Hostname specifies the virtual hostname to match for protocol types that 297 | /// define this concept. When unspecified, all hostnames are matched. This 298 | /// field is ignored for protocols that don't require hostname based 299 | /// matching. 300 | /// 301 | /// Implementations MUST apply Hostname matching appropriately for each of 302 | /// the following protocols: 303 | /// 304 | /// * TLS: The Listener Hostname MUST match the SNI. 305 | /// * HTTP: The Listener Hostname MUST match the Host header of the request. 306 | /// * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP 307 | /// protocol layers as described above. If an implementation does not 308 | /// ensure that both the SNI and Host header match the Listener hostname, 309 | /// it MUST clearly document that. 310 | /// 311 | /// For HTTPRoute and TLSRoute resources, there is an interaction with the 312 | /// `spec.hostnames` array. When both listener and route specify hostnames, 313 | /// there MUST be an intersection between the values for a Route to be 314 | /// accepted. For more information, refer to the Route specific Hostnames 315 | /// documentation. 316 | /// 317 | /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted 318 | /// as a suffix match. That means that a match for `*.example.com` would match 319 | /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. 320 | /// 321 | /// Support: Core 322 | #[serde(default, skip_serializing_if = "Option::is_none")] 323 | pub hostname: Option, 324 | /// Name is the name of the Listener. This name MUST be unique within a 325 | /// Gateway. 326 | /// 327 | /// Support: Core 328 | pub name: String, 329 | /// Port is the network port. Multiple listeners may use the 330 | /// same port, subject to the Listener compatibility rules. 331 | /// 332 | /// Support: Core 333 | pub port: i32, 334 | /// Protocol specifies the network protocol this listener expects to receive. 335 | /// 336 | /// Support: Core 337 | pub protocol: String, 338 | /// TLS is the TLS configuration for the Listener. This field is required if 339 | /// the Protocol field is "HTTPS" or "TLS". It is invalid to set this field 340 | /// if the Protocol field is "HTTP", "TCP", or "UDP". 341 | /// 342 | /// The association of SNIs to Certificate defined in GatewayTLSConfig is 343 | /// defined based on the Hostname field for this listener. 344 | /// 345 | /// The GatewayClass MUST use the longest matching SNI out of all 346 | /// available certificates for any TLS handshake. 347 | /// 348 | /// Support: Core 349 | #[serde(default, skip_serializing_if = "Option::is_none")] 350 | pub tls: Option, 351 | } 352 | 353 | /// AllowedRoutes defines the types of routes that MAY be attached to a 354 | /// Listener and the trusted namespaces where those Route resources MAY be 355 | /// present. 356 | /// 357 | /// Although a client request may match multiple route rules, only one rule 358 | /// may ultimately receive the request. Matching precedence MUST be 359 | /// determined in order of the following criteria: 360 | /// 361 | /// * The most specific match as defined by the Route type. 362 | /// * The oldest Route based on creation timestamp. For example, a Route with 363 | /// a creation timestamp of "2020-09-08 01:02:03" is given precedence over 364 | /// a Route with a creation timestamp of "2020-09-08 01:02:04". 365 | /// * If everything else is equivalent, the Route appearing first in 366 | /// alphabetical order (namespace/name) should be given precedence. For 367 | /// example, foo/bar is given precedence over foo/baz. 368 | /// 369 | /// All valid rules within a Route attached to this Listener should be 370 | /// implemented. Invalid Route rules can be ignored (sometimes that will mean 371 | /// the full Route). If a Route rule transitions from valid to invalid, 372 | /// support for that Route rule should be dropped to ensure consistency. For 373 | /// example, even if a filter specified by a Route rule is invalid, the rest 374 | /// of the rules within that Route should still be supported. 375 | /// 376 | /// Support: Core 377 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 378 | pub struct GatewayListenersAllowedRoutes { 379 | /// Kinds specifies the groups and kinds of Routes that are allowed to bind 380 | /// to this Gateway Listener. When unspecified or empty, the kinds of Routes 381 | /// selected are determined using the Listener protocol. 382 | /// 383 | /// A RouteGroupKind MUST correspond to kinds of Routes that are compatible 384 | /// with the application protocol specified in the Listener's Protocol field. 385 | /// If an implementation does not support or recognize this resource type, it 386 | /// MUST set the "ResolvedRefs" condition to False for this Listener with the 387 | /// "InvalidRouteKinds" reason. 388 | /// 389 | /// Support: Core 390 | #[serde(default, skip_serializing_if = "Option::is_none")] 391 | pub kinds: Option>, 392 | /// Namespaces indicates namespaces from which Routes may be attached to this 393 | /// Listener. This is restricted to the namespace of this Gateway by default. 394 | /// 395 | /// Support: Core 396 | #[serde(default, skip_serializing_if = "Option::is_none")] 397 | pub namespaces: Option, 398 | } 399 | 400 | /// RouteGroupKind indicates the group and kind of a Route resource. 401 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 402 | pub struct GatewayListenersAllowedRoutesKinds { 403 | /// Group is the group of the Route. 404 | #[serde(default, skip_serializing_if = "Option::is_none")] 405 | pub group: Option, 406 | /// Kind is the kind of the Route. 407 | pub kind: String, 408 | } 409 | 410 | /// Namespaces indicates namespaces from which Routes may be attached to this 411 | /// Listener. This is restricted to the namespace of this Gateway by default. 412 | /// 413 | /// Support: Core 414 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 415 | pub struct GatewayListenersAllowedRoutesNamespaces { 416 | /// From indicates where Routes will be selected for this Gateway. Possible 417 | /// values are: 418 | /// 419 | /// * All: Routes in all namespaces may be used by this Gateway. 420 | /// * Selector: Routes in namespaces selected by the selector may be used by 421 | /// this Gateway. 422 | /// * Same: Only Routes in the same namespace may be used by this Gateway. 423 | /// 424 | /// Support: Core 425 | #[serde(default, skip_serializing_if = "Option::is_none")] 426 | pub from: Option, 427 | /// Selector must be specified when From is set to "Selector". In that case, 428 | /// only Routes in Namespaces matching this Selector will be selected by this 429 | /// Gateway. This field is ignored for other values of "From". 430 | /// 431 | /// Support: Core 432 | #[serde(default, skip_serializing_if = "Option::is_none")] 433 | pub selector: Option, 434 | } 435 | 436 | /// Namespaces indicates namespaces from which Routes may be attached to this 437 | /// Listener. This is restricted to the namespace of this Gateway by default. 438 | /// 439 | /// Support: Core 440 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] 441 | pub enum GatewayListenersAllowedRoutesNamespacesFrom { 442 | All, 443 | Selector, 444 | Same, 445 | } 446 | 447 | /// Selector must be specified when From is set to "Selector". In that case, 448 | /// only Routes in Namespaces matching this Selector will be selected by this 449 | /// Gateway. This field is ignored for other values of "From". 450 | /// 451 | /// Support: Core 452 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 453 | pub struct GatewayListenersAllowedRoutesNamespacesSelector { 454 | /// matchExpressions is a list of label selector requirements. The requirements are ANDed. 455 | #[serde( 456 | default, 457 | skip_serializing_if = "Option::is_none", 458 | rename = "matchExpressions" 459 | )] 460 | pub match_expressions: 461 | Option>, 462 | /// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 463 | /// map is equivalent to an element of matchExpressions, whose key field is "key", the 464 | /// operator is "In", and the values array contains only "value". The requirements are ANDed. 465 | #[serde( 466 | default, 467 | skip_serializing_if = "Option::is_none", 468 | rename = "matchLabels" 469 | )] 470 | pub match_labels: Option>, 471 | } 472 | 473 | /// A label selector requirement is a selector that contains values, a key, and an operator that 474 | /// relates the key and values. 475 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 476 | pub struct GatewayListenersAllowedRoutesNamespacesSelectorMatchExpressions { 477 | /// key is the label key that the selector applies to. 478 | pub key: String, 479 | /// operator represents a key's relationship to a set of values. 480 | /// Valid operators are In, NotIn, Exists and DoesNotExist. 481 | pub operator: String, 482 | /// values is an array of string values. If the operator is In or NotIn, 483 | /// the values array must be non-empty. If the operator is Exists or DoesNotExist, 484 | /// the values array must be empty. This array is replaced during a strategic 485 | /// merge patch. 486 | #[serde(default, skip_serializing_if = "Option::is_none")] 487 | pub values: Option>, 488 | } 489 | 490 | /// TLS is the TLS configuration for the Listener. This field is required if 491 | /// the Protocol field is "HTTPS" or "TLS". It is invalid to set this field 492 | /// if the Protocol field is "HTTP", "TCP", or "UDP". 493 | /// 494 | /// The association of SNIs to Certificate defined in GatewayTLSConfig is 495 | /// defined based on the Hostname field for this listener. 496 | /// 497 | /// The GatewayClass MUST use the longest matching SNI out of all 498 | /// available certificates for any TLS handshake. 499 | /// 500 | /// Support: Core 501 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 502 | pub struct GatewayListenersTls { 503 | /// CertificateRefs contains a series of references to Kubernetes objects that 504 | /// contains TLS certificates and private keys. These certificates are used to 505 | /// establish a TLS handshake for requests that match the hostname of the 506 | /// associated listener. 507 | /// 508 | /// A single CertificateRef to a Kubernetes Secret has "Core" support. 509 | /// Implementations MAY choose to support attaching multiple certificates to 510 | /// a Listener, but this behavior is implementation-specific. 511 | /// 512 | /// References to a resource in different namespace are invalid UNLESS there 513 | /// is a ReferenceGrant in the target namespace that allows the certificate 514 | /// to be attached. If a ReferenceGrant does not allow this reference, the 515 | /// "ResolvedRefs" condition MUST be set to False for this listener with the 516 | /// "RefNotPermitted" reason. 517 | /// 518 | /// This field is required to have at least one element when the mode is set 519 | /// to "Terminate" (default) and is optional otherwise. 520 | /// 521 | /// CertificateRefs can reference to standard Kubernetes resources, i.e. 522 | /// Secret, or implementation-specific custom resources. 523 | /// 524 | /// Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls 525 | /// 526 | /// Support: Implementation-specific (More than one reference or other resource types) 527 | #[serde( 528 | default, 529 | skip_serializing_if = "Option::is_none", 530 | rename = "certificateRefs" 531 | )] 532 | pub certificate_refs: Option>, 533 | /// Mode defines the TLS behavior for the TLS session initiated by the client. 534 | /// There are two possible modes: 535 | /// 536 | /// - Terminate: The TLS session between the downstream client and the 537 | /// Gateway is terminated at the Gateway. This mode requires certificates 538 | /// to be specified in some way, such as populating the certificateRefs 539 | /// field. 540 | /// - Passthrough: The TLS session is NOT terminated by the Gateway. This 541 | /// implies that the Gateway can't decipher the TLS stream except for 542 | /// the ClientHello message of the TLS protocol. The certificateRefs field 543 | /// is ignored in this mode. 544 | /// 545 | /// Support: Core 546 | #[serde(default, skip_serializing_if = "Option::is_none")] 547 | pub mode: Option, 548 | /// Options are a list of key/value pairs to enable extended TLS 549 | /// configuration for each implementation. For example, configuring the 550 | /// minimum TLS version or supported cipher suites. 551 | /// 552 | /// A set of common keys MAY be defined by the API in the future. To avoid 553 | /// any ambiguity, implementation-specific definitions MUST use 554 | /// domain-prefixed names, such as `example.com/my-custom-option`. 555 | /// Un-prefixed names are reserved for key names defined by Gateway API. 556 | /// 557 | /// Support: Implementation-specific 558 | #[serde(default, skip_serializing_if = "Option::is_none")] 559 | pub options: Option>, 560 | } 561 | 562 | /// SecretObjectReference identifies an API object including its namespace, 563 | /// defaulting to Secret. 564 | /// 565 | /// The API object must be valid in the cluster; the Group and Kind must 566 | /// be registered in the cluster for this reference to be valid. 567 | /// 568 | /// References to objects with invalid Group and Kind are not valid, and must 569 | /// be rejected by the implementation, with appropriate Conditions set 570 | /// on the containing object. 571 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 572 | pub struct GatewayListenersTlsCertificateRefs { 573 | /// Group is the group of the referent. For example, "gateway.networking.k8s.io". 574 | /// When unspecified or empty string, core API group is inferred. 575 | #[serde(default, skip_serializing_if = "Option::is_none")] 576 | pub group: Option, 577 | /// Kind is kind of the referent. For example "Secret". 578 | #[serde(default, skip_serializing_if = "Option::is_none")] 579 | pub kind: Option, 580 | /// Name is the name of the referent. 581 | pub name: String, 582 | /// Namespace is the namespace of the referenced object. When unspecified, the local 583 | /// namespace is inferred. 584 | /// 585 | /// Note that when a namespace different than the local namespace is specified, 586 | /// a ReferenceGrant object is required in the referent namespace to allow that 587 | /// namespace's owner to accept the reference. See the ReferenceGrant 588 | /// documentation for details. 589 | /// 590 | /// Support: Core 591 | #[serde(default, skip_serializing_if = "Option::is_none")] 592 | pub namespace: Option, 593 | } 594 | 595 | /// TLS is the TLS configuration for the Listener. This field is required if 596 | /// the Protocol field is "HTTPS" or "TLS". It is invalid to set this field 597 | /// if the Protocol field is "HTTP", "TCP", or "UDP". 598 | /// 599 | /// The association of SNIs to Certificate defined in GatewayTLSConfig is 600 | /// defined based on the Hostname field for this listener. 601 | /// 602 | /// The GatewayClass MUST use the longest matching SNI out of all 603 | /// available certificates for any TLS handshake. 604 | /// 605 | /// Support: Core 606 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] 607 | pub enum GatewayListenersTlsMode { 608 | Terminate, 609 | Passthrough, 610 | } 611 | 612 | /// Status defines the current state of Gateway. 613 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 614 | pub struct GatewayStatus { 615 | /// Addresses lists the network addresses that have been bound to the 616 | /// Gateway. 617 | /// 618 | /// This list may differ from the addresses provided in the spec under some 619 | /// conditions: 620 | /// 621 | /// * no addresses are specified, all addresses are dynamically assigned 622 | /// * a combination of specified and dynamic addresses are assigned 623 | /// * a specified address was unusable (e.g. already in use) 624 | /// 625 | /// 626 | #[serde(default, skip_serializing_if = "Option::is_none")] 627 | pub addresses: Option>, 628 | /// Conditions describe the current conditions of the Gateway. 629 | /// 630 | /// Implementations should prefer to express Gateway conditions 631 | /// using the `GatewayConditionType` and `GatewayConditionReason` 632 | /// constants so that operators and tools can converge on a common 633 | /// vocabulary to describe Gateway state. 634 | /// 635 | /// Known condition types are: 636 | /// 637 | /// * "Accepted" 638 | /// * "Programmed" 639 | /// * "Ready" 640 | #[serde(default, skip_serializing_if = "Option::is_none")] 641 | pub conditions: Option>, 642 | /// Listeners provide status for each unique listener port defined in the Spec. 643 | #[serde(default, skip_serializing_if = "Option::is_none")] 644 | pub listeners: Option>, 645 | } 646 | 647 | /// GatewayStatusAddress describes a network address that is bound to a Gateway. 648 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 649 | pub struct GatewayStatusAddresses { 650 | /// Type of the address. 651 | #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")] 652 | pub r#type: Option, 653 | /// Value of the address. The validity of the values will depend 654 | /// on the type and support by the controller. 655 | /// 656 | /// Examples: `1.2.3.4`, `128::1`, `my-ip-address`. 657 | pub value: String, 658 | } 659 | 660 | /// ListenerStatus is the status associated with a Listener. 661 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 662 | pub struct GatewayStatusListeners { 663 | /// AttachedRoutes represents the total number of Routes that have been 664 | /// successfully attached to this Listener. 665 | /// 666 | /// Successful attachment of a Route to a Listener is based solely on the 667 | /// combination of the AllowedRoutes field on the corresponding Listener 668 | /// and the Route's ParentRefs field. A Route is successfully attached to 669 | /// a Listener when it is selected by the Listener's AllowedRoutes field 670 | /// AND the Route has a valid ParentRef selecting the whole Gateway 671 | /// resource or a specific Listener as a parent resource (more detail on 672 | /// attachment semantics can be found in the documentation on the various 673 | /// Route kinds ParentRefs fields). Listener or Route status does not impact 674 | /// successful attachment, i.e. the AttachedRoutes field count MUST be set 675 | /// for Listeners with condition Accepted: false and MUST count successfully 676 | /// attached Routes that may themselves have Accepted: false conditions. 677 | /// 678 | /// Uses for this field include troubleshooting Route attachment and 679 | /// measuring blast radius/impact of changes to a Listener. 680 | #[serde(rename = "attachedRoutes")] 681 | pub attached_routes: i32, 682 | /// Conditions describe the current condition of this listener. 683 | pub conditions: Vec, 684 | /// Name is the name of the Listener that this status corresponds to. 685 | pub name: String, 686 | /// SupportedKinds is the list indicating the Kinds supported by this 687 | /// listener. This MUST represent the kinds an implementation supports for 688 | /// that Listener configuration. 689 | /// 690 | /// If kinds are specified in Spec that are not supported, they MUST NOT 691 | /// appear in this list and an implementation MUST set the "ResolvedRefs" 692 | /// condition to "False" with the "InvalidRouteKinds" reason. If both valid 693 | /// and invalid Route kinds are specified, the implementation MUST 694 | /// reference the valid Route kinds that have been specified. 695 | #[serde(rename = "supportedKinds")] 696 | pub supported_kinds: Vec, 697 | } 698 | 699 | /// RouteGroupKind indicates the group and kind of a Route resource. 700 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 701 | pub struct GatewayStatusListenersSupportedKinds { 702 | /// Group is the group of the Route. 703 | #[serde(default, skip_serializing_if = "Option::is_none")] 704 | pub group: Option, 705 | /// Kind is the kind of the Route. 706 | pub kind: String, 707 | } 708 | -------------------------------------------------------------------------------- /gateway-api/src/apis/standard/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING! generated file do not edit 2 | pub mod constants; 3 | mod enum_defaults; 4 | pub mod gatewayclasses; 5 | pub mod gateways; 6 | pub mod grpcroutes; 7 | pub mod httproutes; 8 | pub mod referencegrants; 9 | -------------------------------------------------------------------------------- /gateway-api/src/apis/standard/referencegrants.rs: -------------------------------------------------------------------------------- 1 | // WARNING: generated by kopium - manual changes will be overwritten 2 | // kopium command: kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - 3 | // kopium version: 0.21.1 4 | 5 | #[allow(unused_imports)] 6 | mod prelude { 7 | pub use kube::CustomResource; 8 | pub use schemars::JsonSchema; 9 | pub use serde::{Deserialize, Serialize}; 10 | } 11 | use self::prelude::*; 12 | 13 | /// Spec defines the desired state of ReferenceGrant. 14 | #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 15 | #[kube( 16 | group = "gateway.networking.k8s.io", 17 | version = "v1beta1", 18 | kind = "ReferenceGrant", 19 | plural = "referencegrants" 20 | )] 21 | #[kube(namespaced)] 22 | #[kube(derive = "Default")] 23 | #[kube(derive = "PartialEq")] 24 | pub struct ReferenceGrantSpec { 25 | /// From describes the trusted namespaces and kinds that can reference the 26 | /// resources described in "To". Each entry in this list MUST be considered 27 | /// to be an additional place that references can be valid from, or to put 28 | /// this another way, entries MUST be combined using OR. 29 | /// 30 | /// Support: Core 31 | pub from: Vec, 32 | /// To describes the resources that may be referenced by the resources 33 | /// described in "From". Each entry in this list MUST be considered to be an 34 | /// additional place that references can be valid to, or to put this another 35 | /// way, entries MUST be combined using OR. 36 | /// 37 | /// Support: Core 38 | pub to: Vec, 39 | } 40 | 41 | /// ReferenceGrantFrom describes trusted namespaces and kinds. 42 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 43 | pub struct ReferenceGrantFrom { 44 | /// Group is the group of the referent. 45 | /// When empty, the Kubernetes core API group is inferred. 46 | /// 47 | /// Support: Core 48 | pub group: String, 49 | /// Kind is the kind of the referent. Although implementations may support 50 | /// additional resources, the following types are part of the "Core" 51 | /// support level for this field. 52 | /// 53 | /// When used to permit a SecretObjectReference: 54 | /// 55 | /// * Gateway 56 | /// 57 | /// When used to permit a BackendObjectReference: 58 | /// 59 | /// * GRPCRoute 60 | /// * HTTPRoute 61 | /// * TCPRoute 62 | /// * TLSRoute 63 | /// * UDPRoute 64 | pub kind: String, 65 | /// Namespace is the namespace of the referent. 66 | /// 67 | /// Support: Core 68 | pub namespace: String, 69 | } 70 | 71 | /// ReferenceGrantTo describes what Kinds are allowed as targets of the 72 | /// references. 73 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] 74 | pub struct ReferenceGrantTo { 75 | /// Group is the group of the referent. 76 | /// When empty, the Kubernetes core API group is inferred. 77 | /// 78 | /// Support: Core 79 | pub group: String, 80 | /// Kind is the kind of the referent. Although implementations may support 81 | /// additional resources, the following types are part of the "Core" 82 | /// support level for this field: 83 | /// 84 | /// * Secret when used to permit a SecretObjectReference 85 | /// * Service when used to permit a BackendObjectReference 86 | pub kind: String, 87 | /// Name is the name of the referent. When unspecified, this policy 88 | /// refers to all resources of the specified Group and Kind in the local 89 | /// namespace. 90 | #[serde(default, skip_serializing_if = "Option::is_none")] 91 | pub name: Option, 92 | } 93 | -------------------------------------------------------------------------------- /gateway-api/src/duration.rs: -------------------------------------------------------------------------------- 1 | //! GEP-2257-compliant Duration type for Gateway API 2 | //! 3 | //! `gateway_api::Duration` is a duration type where parsing and formatting 4 | //! obey GEP-2257. It is based on `std::time::Duration` and uses 5 | //! `kube::core::Duration` for the heavy lifting of parsing. 6 | //! 7 | //! GEP-2257 defines a duration format for the Gateway API that is based on 8 | //! Go's `time.ParseDuration`, with additional restrictions: negative 9 | //! durations, units smaller than millisecond, and floating point are not 10 | //! allowed, and durations are limited to four components of no more than five 11 | //! digits each. See https://gateway-api.sigs.k8s.io/geps/gep-2257 for the 12 | //! complete specification. 13 | 14 | use kube::core::Duration as k8sDuration; 15 | use once_cell::sync::Lazy; 16 | use regex::Regex; 17 | use std::fmt; 18 | use std::str::FromStr; 19 | use std::time::Duration as stdDuration; 20 | 21 | /// GEP-2257-compliant Duration type for Gateway API 22 | /// 23 | /// `gateway_api::Duration` is a duration type where parsing and formatting 24 | /// obey GEP-2257. It is based on `std::time::Duration` and uses 25 | /// `kube::core::Duration` for the heavy lifting of parsing. 26 | /// 27 | /// See https://gateway-api.sigs.k8s.io/geps/gep-2257 for the complete 28 | /// specification. 29 | /// 30 | /// Per GEP-2257, when parsing a `gateway_api::Duration` from a string, the 31 | /// string must match 32 | /// 33 | /// `^([0-9]{1,5}(h|m|s|ms)){1,4}$` 34 | /// 35 | /// and is otherwise parsed the same way that Go's `time.ParseDuration` parses 36 | /// durations. When formatting a `gateway_api::Duration` as a string, 37 | /// zero-valued durations must always be formatted as `0s`, and non-zero 38 | /// durations must be formatted to with only one instance of each applicable 39 | /// unit, greatest unit first. 40 | /// 41 | /// The rules above imply that `gateway_api::Duration` cannot represent 42 | /// negative durations, durations with sub-millisecond precision, or durations 43 | /// larger than 99999h59m59s999ms. Since there's no meaningful way in Rust to 44 | /// allow string formatting to fail, these conditions are checked instead when 45 | /// instantiating `gateway_api::Duration`. 46 | #[derive(Copy, Clone, PartialEq, Eq)] 47 | pub struct Duration(stdDuration); 48 | 49 | /// Regex pattern defining valid GEP-2257 Duration strings. 50 | const GEP2257_PATTERN: &str = r"^([0-9]{1,5}(h|m|s|ms)){1,4}$"; 51 | 52 | /// Maximum duration that can be represented by GEP-2257, in milliseconds. 53 | const MAX_DURATION_MS: u128 = (((99999 * 3600) + (59 * 60) + 59) * 1_000) + 999; 54 | 55 | /// Checks if a duration is valid according to GEP-2257. If it's not, return 56 | /// an error result explaining why the duration is not valid. 57 | /// 58 | /// ```rust 59 | /// use gateway_api::duration::is_valid; 60 | /// use std::time::Duration as stdDuration; 61 | /// 62 | /// // sub-millisecond precision is not allowed 63 | /// let sub_millisecond_duration = stdDuration::from_nanos(600); 64 | /// # assert!(is_valid(sub_millisecond_duration).is_err()); 65 | /// 66 | /// // but precision at a millisecond is fine 67 | /// let non_sub_millisecond_duration = stdDuration::from_millis(1); 68 | /// # assert!(is_valid(non_sub_millisecond_duration).is_ok()); 69 | /// ``` 70 | pub fn is_valid(duration: stdDuration) -> Result<(), String> { 71 | // Check nanoseconds to see if we have sub-millisecond precision in 72 | // this duration. 73 | if duration.subsec_nanos() % 1_000_000 != 0 { 74 | return Err("Cannot express sub-millisecond precision in GEP-2257".to_string()); 75 | } 76 | 77 | // Check the duration to see if it's greater than GEP-2257's maximum. 78 | if duration.as_millis() > MAX_DURATION_MS { 79 | return Err("Duration exceeds GEP-2257 maximum 99999h59m59s999ms".to_string()); 80 | } 81 | 82 | Ok(()) 83 | } 84 | 85 | /// Converting from `std::time::Duration` to `gateway_api::Duration` is 86 | /// allowed, but we need to make sure that the incoming duration is valid 87 | /// according to GEP-2257. 88 | /// 89 | /// ```rust 90 | /// use gateway_api::Duration; 91 | /// use std::convert::TryFrom; 92 | /// use std::time::Duration as stdDuration; 93 | /// 94 | /// // A one-hour duration is valid according to GEP-2257. 95 | /// let std_duration = stdDuration::from_secs(3600); 96 | /// let duration = Duration::try_from(std_duration); 97 | /// # assert!(duration.as_ref().is_ok()); 98 | /// # assert_eq!(format!("{}", duration.as_ref().unwrap()), "1h"); 99 | /// 100 | /// // This should output "Duration: 1h". 101 | /// match duration { 102 | /// Ok(d) => println!("Duration: {}", d), 103 | /// Err(e) => eprintln!("Error: {}", e), 104 | /// } 105 | /// 106 | /// // A 600-nanosecond duration is not valid according to GEP-2257. 107 | /// let std_duration = stdDuration::from_nanos(600); 108 | /// let duration = Duration::try_from(std_duration); 109 | /// # assert!(duration.is_err()); 110 | /// 111 | /// // This should output "Error: Cannot express sub-millisecond 112 | /// // precision in GEP-2257". 113 | /// match duration { 114 | /// Ok(d) => println!("Duration: {}", d), 115 | /// Err(e) => eprintln!("Error: {}", e), 116 | /// } 117 | /// ``` 118 | impl TryFrom for Duration { 119 | type Error = String; 120 | 121 | fn try_from(duration: stdDuration) -> Result { 122 | // Check validity, and propagate any error if it's not. 123 | is_valid(duration)?; 124 | 125 | // It's valid, so we can safely convert it to a gateway_api::Duration. 126 | Ok(Duration(duration)) 127 | } 128 | } 129 | 130 | /// Converting from `k8s::time::Duration` to `gateway_api::Duration` is 131 | /// allowed, but we need to make sure that the incoming duration is valid 132 | /// according to GEP-2257. 133 | /// 134 | /// ```rust 135 | /// use gateway_api::Duration; 136 | /// use std::convert::TryFrom; 137 | /// use std::str::FromStr; 138 | /// use kube::core::Duration as k8sDuration; 139 | /// 140 | /// // A one-hour duration is valid according to GEP-2257. 141 | /// let k8s_duration = k8sDuration::from_str("1h").unwrap(); 142 | /// let duration = Duration::try_from(k8s_duration); 143 | /// # assert!(duration.as_ref().is_ok()); 144 | /// # assert_eq!(format!("{}", duration.as_ref().unwrap()), "1h"); 145 | /// 146 | /// // This should output "Duration: 1h". 147 | /// match duration { 148 | /// Ok(d) => println!("Duration: {}", d), 149 | /// Err(e) => eprintln!("Error: {}", e), 150 | /// } 151 | /// 152 | /// // A 600-nanosecond duration is not valid according to GEP-2257. 153 | /// let k8s_duration = k8sDuration::from_str("600ns").unwrap(); 154 | /// let duration = Duration::try_from(k8s_duration); 155 | /// # assert!(duration.as_ref().is_err()); 156 | /// 157 | /// // This should output "Error: Cannot express sub-millisecond 158 | /// // precision in GEP-2257". 159 | /// match duration { 160 | /// Ok(d) => println!("Duration: {}", d), 161 | /// Err(e) => eprintln!("Error: {}", e), 162 | /// } 163 | /// 164 | /// // kube::core::Duration can also express negative durations, which are not 165 | /// // valid according to GEP-2257. 166 | /// let k8s_duration = k8sDuration::from_str("-5s").unwrap(); 167 | /// let duration = Duration::try_from(k8s_duration); 168 | /// # assert!(duration.as_ref().is_err()); 169 | /// 170 | /// // This should output "Error: Cannot express sub-millisecond 171 | /// // precision in GEP-2257". 172 | /// match duration { 173 | /// Ok(d) => println!("Duration: {}", d), 174 | /// Err(e) => eprintln!("Error: {}", e), 175 | /// } 176 | /// ``` 177 | 178 | impl TryFrom for Duration { 179 | type Error = String; 180 | 181 | fn try_from(duration: k8sDuration) -> Result { 182 | // We can't rely on kube::core::Duration to check validity for 183 | // gateway_api::Duration, so first we need to make sure that our 184 | // k8sDuration is not negative... 185 | if duration.is_negative() { 186 | return Err("Duration cannot be negative".to_string()); 187 | } 188 | 189 | // Once we know it's not negative, we can safely convert it to a 190 | // std::time::Duration (which will always succeed) and then check it 191 | // for validity as in TryFrom. 192 | let stddur = stdDuration::from(duration); 193 | is_valid(stddur)?; 194 | Ok(Duration(stddur)) 195 | } 196 | } 197 | 198 | impl Duration { 199 | /// Create a new `gateway_api::Duration` from seconds and nanoseconds, 200 | /// while requiring that the resulting duration is valid according to 201 | /// GEP-2257. 202 | /// 203 | /// ```rust 204 | /// use gateway_api::Duration; 205 | /// 206 | /// let duration = Duration::new(7200, 600_000_000); 207 | /// # assert!(duration.as_ref().is_ok()); 208 | /// # assert_eq!(format!("{}", duration.unwrap()), "2h600ms"); 209 | /// ``` 210 | pub fn new(secs: u64, nanos: u32) -> Result { 211 | let stddur = stdDuration::new(secs, nanos); 212 | 213 | // Propagate errors if not valid, or unwrap the new Duration if all's 214 | // well. 215 | is_valid(stddur)?; 216 | Ok(Self(stddur)) 217 | } 218 | 219 | /// Create a new `gateway_api::Duration` from seconds, while requiring 220 | /// that the resulting duration is valid according to GEP-2257. 221 | /// 222 | /// ```rust 223 | /// use gateway_api::Duration; 224 | /// let duration = Duration::from_secs(3600); 225 | /// # assert!(duration.as_ref().is_ok()); 226 | /// # assert_eq!(format!("{}", duration.unwrap()), "1h"); 227 | /// ``` 228 | pub fn from_secs(secs: u64) -> Result { 229 | Self::new(secs, 0) 230 | } 231 | 232 | /// Create a new `gateway_api::Duration` from microseconds, while 233 | /// requiring that the resulting duration is valid according to GEP-2257. 234 | /// 235 | /// ```rust 236 | /// use gateway_api::Duration; 237 | /// let duration = Duration::from_micros(1_000_000); 238 | /// # assert!(duration.as_ref().is_ok()); 239 | /// # assert_eq!(format!("{}", duration.unwrap()), "1s"); 240 | /// ``` 241 | pub fn from_micros(micros: u64) -> Result { 242 | let sec = micros / 1_000_000; 243 | let ns = ((micros % 1_000_000) * 1_000) as u32; 244 | 245 | Self::new(sec, ns) 246 | } 247 | 248 | /// Create a new `gateway_api::Duration` from milliseconds, while 249 | /// requiring that the resulting duration is valid according to GEP-2257. 250 | /// 251 | /// ```rust 252 | /// use gateway_api::Duration; 253 | /// let duration = Duration::from_millis(1000); 254 | /// # assert!(duration.as_ref().is_ok()); 255 | /// # assert_eq!(format!("{}", duration.unwrap()), "1s"); 256 | /// ``` 257 | pub fn from_millis(millis: u64) -> Result { 258 | let sec = millis / 1_000; 259 | let ns = ((millis % 1_000) * 1_000_000) as u32; 260 | 261 | Self::new(sec, ns) 262 | } 263 | 264 | /// The number of whole seconds in the entire duration. 265 | /// 266 | /// ```rust 267 | /// use gateway_api::Duration; 268 | /// 269 | /// let duration = Duration::from_secs(3600); // 1h 270 | /// # assert!(duration.as_ref().is_ok()); 271 | /// let seconds = duration.unwrap().as_secs(); // 3600 272 | /// # assert_eq!(seconds, 3600); 273 | /// 274 | /// let duration = Duration::from_millis(1500); // 1s500ms 275 | /// # assert!(duration.as_ref().is_ok()); 276 | /// let seconds = duration.unwrap().as_secs(); // 1 277 | /// # assert_eq!(seconds, 1); 278 | /// ``` 279 | pub fn as_secs(&self) -> u64 { 280 | self.0.as_secs() 281 | } 282 | 283 | /// The number of milliseconds in the whole duration. GEP-2257 doesn't 284 | /// support sub-millisecond precision, so this is always exact. 285 | /// 286 | /// ```rust 287 | /// use gateway_api::Duration; 288 | /// 289 | /// let duration = Duration::from_millis(1500); // 1s500ms 290 | /// # assert!(duration.as_ref().is_ok()); 291 | /// let millis = duration.unwrap().as_millis(); // 1500 292 | /// # assert_eq!(millis, 1500); 293 | /// ``` 294 | pub fn as_millis(&self) -> u128 { 295 | self.0.as_millis() 296 | } 297 | 298 | /// The number of nanoseconds in the whole duration. This is always exact. 299 | /// 300 | /// ```rust 301 | /// use gateway_api::Duration; 302 | /// 303 | /// let duration = Duration::from_millis(1500); // 1s500ms 304 | /// # assert!(duration.as_ref().is_ok()); 305 | /// let nanos = duration.unwrap().as_nanos(); // 1_500_000_000 306 | /// # assert_eq!(nanos, 1_500_000_000); 307 | /// ``` 308 | pub fn as_nanos(&self) -> u128 { 309 | self.0.as_nanos() 310 | } 311 | 312 | /// The number of nanoseconds in the part of the duration that's not whole 313 | /// seconds. Since GEP-2257 doesn't support sub-millisecond precision, this 314 | /// will always be 0 or a multiple of 1,000,000. 315 | /// 316 | /// ```rust 317 | /// use gateway_api::Duration; 318 | /// 319 | /// let duration = Duration::from_millis(1500); // 1s500ms 320 | /// # assert!(duration.as_ref().is_ok()); 321 | /// let subsec_nanos = duration.unwrap().subsec_nanos(); // 500_000_000 322 | /// # assert_eq!(subsec_nanos, 500_000_000); 323 | /// ``` 324 | pub fn subsec_nanos(&self) -> u32 { 325 | self.0.subsec_nanos() 326 | } 327 | 328 | /// Checks whether the duration is zero. 329 | /// 330 | /// ```rust 331 | /// use gateway_api::Duration; 332 | /// 333 | /// let duration = Duration::from_secs(0); 334 | /// # assert!(duration.as_ref().is_ok()); 335 | /// assert!(duration.unwrap().is_zero()); 336 | /// 337 | /// let duration = Duration::from_secs(1); 338 | /// # assert!(duration.as_ref().is_ok()); 339 | /// assert!(!duration.unwrap().is_zero()); 340 | /// ``` 341 | pub fn is_zero(&self) -> bool { 342 | self.0.is_zero() 343 | } 344 | } 345 | 346 | /// Parsing a `gateway_api::Duration` from a string requires that the input 347 | /// string obey GEP-2257: 348 | /// 349 | /// - input strings must match `^([0-9]{1,5}(h|m|s|ms)){1,4}$` 350 | /// - durations are parsed the same way that Go's `time.ParseDuration` does 351 | /// 352 | /// If the input string is not valid according to GEP-2257, an error is 353 | /// returned explaining what went wrong. 354 | /// 355 | /// ```rust 356 | /// use gateway_api::Duration; 357 | /// use std::str::FromStr; 358 | /// 359 | /// let duration = Duration::from_str("1h"); 360 | /// # assert!(duration.as_ref().is_ok()); 361 | /// # assert_eq!(format!("{}", duration.as_ref().unwrap()), "1h"); 362 | /// 363 | /// // This should output "Parsed duration: 1h". 364 | /// match duration { 365 | /// Ok(d) => println!("Parsed duration: {}", d), 366 | /// Err(e) => eprintln!("Error: {}", e), 367 | /// } 368 | /// 369 | /// let duration = Duration::from_str("1h30m500ns"); 370 | /// # assert!(duration.as_ref().is_err()); 371 | /// 372 | /// // This should output "Error: Cannot express sub-millisecond 373 | /// // precision in GEP-2257". 374 | /// match duration { 375 | /// Ok(d) => println!("Parsed duration: {}", d), 376 | /// Err(e) => eprintln!("Error: {}", e), 377 | /// } 378 | /// ``` 379 | impl FromStr for Duration { 380 | type Err = String; 381 | 382 | // Parse a GEP-2257-compliant duration string into a 383 | // `gateway_api::Duration`. 384 | fn from_str(duration_str: &str) -> Result { 385 | // GEP-2257 dictates that string values must match GEP2257_PATTERN and 386 | // be parsed the same way that Go's time.ParseDuration parses 387 | // durations. 388 | // 389 | // This Lazy Regex::new should never ever fail, given that the regex 390 | // is a compile-time constant. But just in case..... 391 | static RE: Lazy = Lazy::new(|| { 392 | Regex::new(GEP2257_PATTERN).expect( 393 | format!( 394 | r#"GEP2257 regex "{}" did not compile (this is a bug!)"#, 395 | GEP2257_PATTERN 396 | ) 397 | .as_str(), 398 | ) 399 | }); 400 | 401 | // If the string doesn't match the regex, it's invalid. 402 | if !RE.is_match(duration_str) { 403 | return Err("Invalid duration format".to_string()); 404 | } 405 | 406 | // We use kube::core::Duration to do the heavy lifting of parsing. 407 | match k8sDuration::from_str(duration_str) { 408 | // If the parse fails, return an error immediately... 409 | Err(err) => Err(err.to_string()), 410 | 411 | // ...otherwise, we need to try to turn the k8sDuration into a 412 | // gateway_api::Duration (which will check validity). 413 | Ok(kd) => Duration::try_from(kd), 414 | } 415 | } 416 | } 417 | 418 | /// Formatting a `gateway_api::Duration` for display is defined only for valid 419 | /// durations, and must follow the GEP-2257 rules for formatting: 420 | /// 421 | /// - zero-valued durations must always be formatted as `0s` 422 | /// - non-zero durations must be formatted with only one instance of each 423 | /// applicable unit, greatest unit first. 424 | /// 425 | /// ```rust 426 | /// use gateway_api::Duration; 427 | /// use std::fmt::Display; 428 | /// 429 | /// // Zero-valued durations are always formatted as "0s". 430 | /// let duration = Duration::from_secs(0); 431 | /// # assert!(duration.as_ref().is_ok()); 432 | /// assert_eq!(format!("{}", duration.unwrap()), "0s"); 433 | /// 434 | /// // Non-zero durations are formatted with only one instance of each 435 | /// // applicable unit, greatest unit first. 436 | /// let duration = Duration::from_secs(3600); 437 | /// # assert!(duration.as_ref().is_ok()); 438 | /// assert_eq!(format!("{}", duration.unwrap()), "1h"); 439 | /// 440 | /// let duration = Duration::from_millis(1500); 441 | /// # assert!(duration.as_ref().is_ok()); 442 | /// assert_eq!(format!("{}", duration.unwrap()), "1s500ms"); 443 | /// 444 | /// let duration = Duration::from_millis(9005500); 445 | /// # assert!(duration.as_ref().is_ok()); 446 | /// assert_eq!(format!("{}", duration.unwrap()), "2h30m5s500ms"); 447 | /// ``` 448 | impl fmt::Display for Duration { 449 | /// Format a `gateway_api::Duration` for display, following GEP-2257 rules. 450 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 451 | // Short-circuit if the duration is zero, since "0s" is the special 452 | // case for a zero-valued duration. 453 | if self.is_zero() { 454 | return write!(f, "0s"); 455 | } 456 | 457 | // Unfortunately, we can't rely on kube::core::Duration for 458 | // formatting, since it can happily hand back things like "5400s" 459 | // instead of "1h30m". 460 | // 461 | // So we'll do the formatting ourselves. Start by grabbing the 462 | // milliseconds part of the Duration (remember, the constructors make 463 | // sure that we don't have sub-millisecond precision)... 464 | let ms = self.subsec_nanos() / 1_000_000; 465 | 466 | // ...then after that, do the usual div & mod tree to take seconds and 467 | // get hours, minutes, and seconds from it. 468 | let mut secs = self.as_secs(); 469 | 470 | let hours = secs / 3600; 471 | 472 | if hours > 0 { 473 | secs -= hours * 3600; 474 | write!(f, "{}h", hours)?; 475 | } 476 | 477 | let minutes = secs / 60; 478 | if minutes > 0 { 479 | secs -= minutes * 60; 480 | write!(f, "{}m", minutes)?; 481 | } 482 | 483 | if secs > 0 { 484 | write!(f, "{}s", secs)?; 485 | } 486 | 487 | if ms > 0 { 488 | write!(f, "{}ms", ms)?; 489 | } 490 | 491 | Ok(()) 492 | } 493 | } 494 | 495 | /// Formatting a `gateway_api::Duration` for debug is the same as formatting 496 | /// it for display. 497 | impl fmt::Debug for Duration { 498 | /// Format a `gateway_api::Duration` for debug, following GEP-2257 rules. 499 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 500 | // Yes, we format GEP-2257 Durations the same in debug and display. 501 | fmt::Display::fmt(self, f) 502 | } 503 | } 504 | 505 | #[cfg(test)] 506 | mod tests { 507 | use super::*; 508 | 509 | #[test] 510 | /// Test that the validation logic in `Duration`'s constructor 511 | /// method(s) correctly handles known-good durations. (The tests are 512 | /// ordered to match the from_str test cases.) 513 | fn test_gep2257_from_valid_duration() { 514 | let test_cases = vec![ 515 | Duration::from_secs(0), // 0s / 0h0m0s / 0m0s 516 | Duration::from_secs(3600), // 1h 517 | Duration::from_secs(1800), // 30m 518 | Duration::from_secs(10), // 10s 519 | Duration::from_millis(500), // 500ms 520 | Duration::from_secs(9000), // 2h30m / 150m 521 | Duration::from_secs(5410), // 1h30m10s / 10s30m1h 522 | Duration::new(7200, 600_000_000), // 2h600ms 523 | Duration::new(7200 + 1800, 600_000_000), // 2h30m600ms 524 | Duration::new(7200 + 1800 + 10, 600_000_000), // 2h30m10s600ms 525 | Duration::from_millis(MAX_DURATION_MS as u64), // 99999h59m59s999ms 526 | ]; 527 | 528 | for (idx, duration) in test_cases.iter().enumerate() { 529 | assert!( 530 | duration.is_ok(), 531 | "{:?}: Duration {:?} should be OK", 532 | idx, 533 | duration 534 | ); 535 | } 536 | } 537 | 538 | #[test] 539 | /// Test that the validation logic in `Duration`'s constructor 540 | /// method(s) correctly handles known-bad durations. 541 | fn test_gep2257_from_invalid_duration() { 542 | let test_cases = vec![ 543 | ( 544 | Duration::from_micros(100), 545 | Err("Cannot express sub-millisecond precision in GEP-2257".to_string()), 546 | ), 547 | ( 548 | Duration::from_secs(10000 * 86400), 549 | Err("Duration exceeds GEP-2257 maximum 99999h59m59s999ms".to_string()), 550 | ), 551 | ( 552 | Duration::from_millis((MAX_DURATION_MS + 1) as u64), 553 | Err("Duration exceeds GEP-2257 maximum 99999h59m59s999ms".to_string()), 554 | ), 555 | ]; 556 | 557 | for (idx, (duration, expected)) in test_cases.into_iter().enumerate() { 558 | assert_eq!( 559 | duration, expected, 560 | "{:?}: Duration {:?} should be an error", 561 | idx, duration 562 | ); 563 | } 564 | } 565 | 566 | #[test] 567 | /// Test that the TryFrom implementation for k8sDuration correctly converts 568 | /// to gateway_api::Duration and validates the result. 569 | fn test_gep2257_from_valid_k8s_duration() { 570 | let test_cases = vec![ 571 | ( 572 | k8sDuration::from_str("0s").unwrap(), 573 | Duration::from_secs(0).unwrap(), 574 | ), 575 | ( 576 | k8sDuration::from_str("1h").unwrap(), 577 | Duration::from_secs(3600).unwrap(), 578 | ), 579 | ( 580 | k8sDuration::from_str("500ms").unwrap(), 581 | Duration::from_millis(500).unwrap(), 582 | ), 583 | ( 584 | k8sDuration::from_str("2h600ms").unwrap(), 585 | Duration::new(7200, 600_000_000).unwrap(), 586 | ), 587 | ]; 588 | 589 | for (idx, (k8s_duration, expected)) in test_cases.into_iter().enumerate() { 590 | let duration = Duration::try_from(k8s_duration); 591 | 592 | assert!( 593 | duration.as_ref().is_ok_and(|d| *d == expected), 594 | "{:?}: Duration {:?} should be {:?}", 595 | idx, 596 | duration, 597 | expected 598 | ); 599 | } 600 | } 601 | 602 | #[test] 603 | /// Test that the TryFrom implementation for k8sDuration correctly fails 604 | /// for kube::core::Durations that aren't valid GEP-2257 durations. 605 | fn test_gep2257_from_invalid_k8s_duration() { 606 | let test_cases: Vec<(k8sDuration, Result)> = vec![ 607 | ( 608 | k8sDuration::from_str("100us").unwrap(), 609 | Err("Cannot express sub-millisecond precision in GEP-2257".to_string()), 610 | ), 611 | ( 612 | k8sDuration::from_str("100000h").unwrap(), 613 | Err("Duration exceeds GEP-2257 maximum 99999h59m59s999ms".to_string()), 614 | ), 615 | ( 616 | k8sDuration::from(stdDuration::from_millis((MAX_DURATION_MS + 1) as u64)), 617 | Err("Duration exceeds GEP-2257 maximum 99999h59m59s999ms".to_string()), 618 | ), 619 | ( 620 | k8sDuration::from_str("-5s").unwrap(), 621 | Err("Duration cannot be negative".to_string()), 622 | ), 623 | ]; 624 | 625 | for (idx, (k8s_duration, expected)) in test_cases.into_iter().enumerate() { 626 | assert_eq!( 627 | Duration::try_from(k8s_duration), 628 | expected, 629 | "{:?}: k8sDuration {:?} should be error {:?}", 630 | idx, 631 | k8s_duration, 632 | expected 633 | ); 634 | } 635 | } 636 | 637 | #[test] 638 | fn test_gep2257_from_str() { 639 | // Test vectors are mostly taken directly from GEP-2257, but there are 640 | // some extras thrown in and it's not meaningful to test e.g. "0.5m" 641 | // in Rust. 642 | let test_cases = vec![ 643 | ("0h", Duration::from_secs(0)), 644 | ("0s", Duration::from_secs(0)), 645 | ("0h0m0s", Duration::from_secs(0)), 646 | ("1h", Duration::from_secs(3600)), 647 | ("30m", Duration::from_secs(1800)), 648 | ("10s", Duration::from_secs(10)), 649 | ("500ms", Duration::from_millis(500)), 650 | ("2h30m", Duration::from_secs(9000)), 651 | ("150m", Duration::from_secs(9000)), 652 | ("7230s", Duration::from_secs(7230)), 653 | ("1h30m10s", Duration::from_secs(5410)), 654 | ("10s30m1h", Duration::from_secs(5410)), 655 | ("100ms200ms300ms", Duration::from_millis(600)), 656 | ("100ms200ms300ms", Duration::from_millis(600)), 657 | ( 658 | "99999h59m59s999ms", 659 | Duration::from_millis(MAX_DURATION_MS as u64), 660 | ), 661 | ("1d", Err("Invalid duration format".to_string())), 662 | ("1", Err("Invalid duration format".to_string())), 663 | ("1m1", Err("Invalid duration format".to_string())), 664 | ( 665 | "1h30m10s20ms50h", 666 | Err("Invalid duration format".to_string()), 667 | ), 668 | ("999999h", Err("Invalid duration format".to_string())), 669 | ("1.5h", Err("Invalid duration format".to_string())), 670 | ("-15m", Err("Invalid duration format".to_string())), 671 | ( 672 | "99999h59m59s1000ms", 673 | Err("Duration exceeds GEP-2257 maximum 99999h59m59s999ms".to_string()), 674 | ), 675 | ]; 676 | 677 | for (idx, (duration_str, expected)) in test_cases.into_iter().enumerate() { 678 | assert_eq!( 679 | Duration::from_str(duration_str), 680 | expected, 681 | "{:?}: Duration {:?} should be {:?}", 682 | idx, 683 | duration_str, 684 | expected 685 | ); 686 | } 687 | } 688 | 689 | #[test] 690 | fn test_gep2257_format() { 691 | // Formatting should always succeed for valid durations, and we've 692 | // covered invalid durations in the constructor and parse tests. 693 | let test_cases = vec![ 694 | (Duration::from_secs(0), "0s".to_string()), 695 | (Duration::from_secs(3600), "1h".to_string()), 696 | (Duration::from_secs(1800), "30m".to_string()), 697 | (Duration::from_secs(10), "10s".to_string()), 698 | (Duration::from_millis(500), "500ms".to_string()), 699 | (Duration::from_secs(9000), "2h30m".to_string()), 700 | (Duration::from_secs(5410), "1h30m10s".to_string()), 701 | (Duration::from_millis(600), "600ms".to_string()), 702 | (Duration::new(7200, 600_000_000), "2h600ms".to_string()), 703 | ( 704 | Duration::new(7200 + 1800, 600_000_000), 705 | "2h30m600ms".to_string(), 706 | ), 707 | ( 708 | Duration::new(7200 + 1800 + 10, 600_000_000), 709 | "2h30m10s600ms".to_string(), 710 | ), 711 | ]; 712 | 713 | for (idx, (duration, expected)) in test_cases.into_iter().enumerate() { 714 | assert!( 715 | duration 716 | .as_ref() 717 | .is_ok_and(|d| format!("{}", d) == expected), 718 | "{:?}: Duration {:?} should be {:?}", 719 | idx, 720 | duration, 721 | expected 722 | ); 723 | } 724 | } 725 | } 726 | -------------------------------------------------------------------------------- /gateway-api/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod duration; 2 | pub use duration::Duration; 3 | pub mod apis; 4 | pub use apis::standard::*; 5 | 6 | #[cfg(feature = "experimental")] 7 | pub use apis::experimental; 8 | 9 | #[cfg(test)] 10 | mod tests { 11 | use std::process::Command; 12 | 13 | use anyhow::Error; 14 | use hyper_util::client::legacy::Client as HTTPClient; 15 | use hyper_util::rt::TokioExecutor; 16 | use k8s_openapi::apimachinery::pkg::apis::meta::v1::{Condition, Time}; 17 | use k8s_openapi::chrono::Utc; 18 | use kube::api::{Patch, PatchParams, PostParams}; 19 | use kube::config::{KubeConfigOptions, Kubeconfig}; 20 | use kube::core::ObjectMeta; 21 | use kube::Client as KubeClient; 22 | use kube::{client::ConfigExt, Api, Config, CustomResourceExt}; 23 | use serde_json::json; 24 | use tower::BoxError; 25 | use tower::ServiceBuilder; 26 | use uuid::Uuid; 27 | 28 | use crate::{ 29 | constants::{ 30 | GatewayConditionReason, GatewayConditionType, ListenerConditionReason, 31 | ListenerConditionType, 32 | }, 33 | gatewayclasses::{GatewayClass, GatewayClassSpec}, 34 | gateways::{ 35 | Gateway, GatewaySpec, GatewayStatus, GatewayStatusAddresses, GatewayStatusListeners, 36 | }, 37 | }; 38 | 39 | // ------------------------------------------------------------------------- 40 | // Tests 41 | // ------------------------------------------------------------------------- 42 | 43 | #[ignore] 44 | #[tokio::test] 45 | async fn deploy_gateway() -> Result<(), Error> { 46 | let (client, cluster) = get_client().await?; 47 | let info = client.apiserver_version().await?; 48 | 49 | println!( 50 | "kind cluster {} is running, server version: {}", 51 | cluster.name, info.git_version 52 | ); 53 | 54 | let mut gwc = GatewayClass { 55 | metadata: ObjectMeta::default(), 56 | spec: GatewayClassSpec { 57 | controller_name: "test-controller".to_string(), 58 | description: None, 59 | parameters_ref: None, 60 | }, 61 | status: None, 62 | }; 63 | gwc.metadata.name = Some("test-gateway-class".to_string()); 64 | gwc = Api::all(client.clone()) 65 | .create(&PostParams::default(), &gwc) 66 | .await?; 67 | 68 | assert!(gwc.metadata.name.is_some()); 69 | assert!(gwc.metadata.uid.is_some()); 70 | 71 | let mut gw = Gateway { 72 | metadata: ObjectMeta::default(), 73 | spec: GatewaySpec { 74 | gateway_class_name: gwc 75 | .metadata 76 | .name 77 | .ok_or(Error::msg("could not find GatewayClass name"))?, 78 | ..Default::default() 79 | }, 80 | status: None, 81 | }; 82 | gw.metadata.name = Some("test-gateway".to_string()); 83 | gw = Api::default_namespaced(client.clone()) 84 | .create(&PostParams::default(), &gw) 85 | .await?; 86 | 87 | assert!(gw.metadata.name.is_some()); 88 | assert!(gw.metadata.uid.is_some()); 89 | 90 | let mut gw_status = GatewayStatus::default(); 91 | gw_status.addresses = Some(vec![GatewayStatusAddresses::default()]); 92 | gw_status.listeners = Some(vec![GatewayStatusListeners { 93 | name: "tcp".into(), 94 | attached_routes: 0, 95 | supported_kinds: vec![], 96 | conditions: vec![Condition { 97 | last_transition_time: Time(Utc::now()), 98 | message: "testing gateway".to_string(), 99 | observed_generation: Some(1), 100 | reason: ListenerConditionReason::Programmed.to_string(), 101 | status: "True".to_string(), 102 | type_: ListenerConditionType::Programmed.to_string(), 103 | }], 104 | }]); 105 | gw_status.conditions = Some(vec![Condition { 106 | last_transition_time: Time(Utc::now()), 107 | message: "testing gateway".to_string(), 108 | observed_generation: Some(1), 109 | reason: GatewayConditionReason::Programmed.to_string(), 110 | status: "True".to_string(), 111 | type_: GatewayConditionType::Programmed.to_string(), 112 | }]); 113 | 114 | gw = Api::default_namespaced(client) 115 | .patch_status( 116 | gw.metadata.name.clone().unwrap().as_str(), 117 | &PatchParams::default(), 118 | &Patch::Merge(json!({ 119 | "status": Some(gw_status) 120 | })), 121 | ) 122 | .await?; 123 | 124 | assert!(gw.status.is_some()); 125 | assert!(gw.status.clone().unwrap().addresses.is_some()); 126 | assert!(gw.status.clone().unwrap().listeners.is_some()); 127 | assert!(gw.status.clone().unwrap().conditions.is_some()); 128 | 129 | Ok(()) 130 | } 131 | 132 | // ------------------------------------------------------------------------- 133 | // Test Utilities 134 | // ------------------------------------------------------------------------- 135 | 136 | struct Cluster { 137 | name: String, 138 | } 139 | 140 | impl Drop for Cluster { 141 | fn drop(&mut self) { 142 | match delete_kind_cluster(&self.name) { 143 | Err(err) => panic!("failed to cleanup kind cluster {}: {}", self.name, err), 144 | Ok(()) => {} 145 | } 146 | } 147 | } 148 | 149 | async fn get_client() -> Result<(kube::Client, Cluster), Error> { 150 | let cluster = create_kind_cluster()?; 151 | let kubeconfig_yaml = get_kind_kubeconfig(&cluster.name)?; 152 | let kubeconfig = Kubeconfig::from_yaml(&kubeconfig_yaml)?; 153 | let config = 154 | Config::from_custom_kubeconfig(kubeconfig, &KubeConfigOptions::default()).await?; 155 | 156 | let https = config.rustls_https_connector()?; 157 | let http_client = HTTPClient::builder(TokioExecutor::new()).build(https); 158 | let service = ServiceBuilder::new() 159 | .layer(config.base_uri_layer()) 160 | .option_layer(config.auth_layer()?) 161 | .map_err(BoxError::from) 162 | .service(http_client); 163 | 164 | let client = KubeClient::new(service, config.default_namespace); 165 | 166 | deploy_crds(client.clone()).await?; 167 | 168 | Ok((client, cluster)) 169 | } 170 | 171 | async fn deploy_crds(client: kube::Client) -> Result<(), Error> { 172 | let mut gwc_crd = GatewayClass::crd(); 173 | gwc_crd.metadata.annotations = Some(std::collections::BTreeMap::from([( 174 | "api-approved.kubernetes.io".to_string(), 175 | "https://github.com/kubernetes/enhancements/pull/1111".to_string(), 176 | )])); 177 | 178 | Api::all(client.clone()) 179 | .create(&PostParams::default(), &gwc_crd) 180 | .await?; 181 | 182 | let mut gw_crd = Gateway::crd(); 183 | gw_crd.metadata.annotations = Some(std::collections::BTreeMap::from([( 184 | "api-approved.kubernetes.io".to_string(), 185 | "https://github.com/kubernetes/enhancements/pull/1111".to_string(), 186 | )])); 187 | 188 | Api::all(client.clone()) 189 | .create(&PostParams::default(), &gw_crd) 190 | .await?; 191 | 192 | Ok(()) 193 | } 194 | 195 | fn create_kind_cluster() -> Result { 196 | let cluster_name = Uuid::new_v4().to_string(); 197 | 198 | let output = Command::new("kind") 199 | .arg("create") 200 | .arg("cluster") 201 | .arg("--name") 202 | .arg(&cluster_name) 203 | .output()?; 204 | 205 | if !output.status.success() { 206 | return Err(Error::msg(String::from_utf8(output.stderr)?)); 207 | } 208 | 209 | Ok(Cluster { name: cluster_name }) 210 | } 211 | 212 | fn delete_kind_cluster(cluster_name: &str) -> Result<(), Error> { 213 | let output = Command::new("kind") 214 | .arg("delete") 215 | .arg("cluster") 216 | .arg("--name") 217 | .arg(cluster_name) 218 | .output()?; 219 | 220 | if !output.status.success() { 221 | return Err(Error::msg(String::from_utf8(output.stderr)?)); 222 | } 223 | 224 | Ok(()) 225 | } 226 | 227 | fn get_kind_kubeconfig(cluster_name: &str) -> Result { 228 | let output = Command::new("kind") 229 | .arg("get") 230 | .arg("kubeconfig") 231 | .arg("--name") 232 | .arg(cluster_name) 233 | .output()?; 234 | 235 | if !output.status.success() { 236 | return Err(Error::msg(String::from_utf8(output.stderr)?)); 237 | } 238 | 239 | Ok(String::from_utf8(output.stdout)?) 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ------------------------------------------------------------------------------ 4 | # This script will automatically generate API updates for new Gateway API 5 | # releases. Update the $VERSION to the new release version before executing. 6 | # 7 | # This script requires kopium, which can be installed with: 8 | # 9 | # cargo install kopium 10 | # 11 | # See: https://github.com/kube-rs/kopium 12 | # ------------------------------------------------------------------------------ 13 | 14 | set -eou pipefail 15 | 16 | VERSION="v1.2.1" 17 | 18 | STANDARD_APIS=( 19 | gatewayclasses 20 | gateways 21 | httproutes 22 | referencegrants 23 | grpcroutes 24 | ) 25 | 26 | EXPERIMENTAL_APIS=( 27 | gatewayclasses 28 | gateways 29 | httproutes 30 | referencegrants 31 | grpcroutes 32 | tcproutes 33 | tlsroutes 34 | udproutes 35 | ) 36 | 37 | export APIS_DIR='gateway-api/src/apis' 38 | rm -rf $APIS_DIR/standard/ 39 | rm -rf $APIS_DIR/experimental/ 40 | 41 | cat << EOF > $APIS_DIR/mod.rs 42 | pub mod experimental; 43 | pub mod standard; 44 | EOF 45 | 46 | 47 | mkdir -p $APIS_DIR/standard/ 48 | mkdir -p $APIS_DIR/experimental/ 49 | 50 | echo "// WARNING! generated file do not edit" > $APIS_DIR/standard/mod.rs 51 | 52 | for API in "${STANDARD_APIS[@]}" 53 | do 54 | echo "generating standard api ${API}" 55 | curl -sSL "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${VERSION}/config/crd/standard/gateway.networking.k8s.io_${API}.yaml" | kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - > $APIS_DIR/standard/${API}.rs 56 | echo "pub mod ${API};" >> $APIS_DIR/standard/mod.rs 57 | done 58 | 59 | # Standard API enums that need a Default trait impl along with their respective default variant. 60 | ENUMS=( 61 | HTTPRouteRulesFiltersRequestRedirectPathType=ReplaceFullPath 62 | HTTPRouteRulesFiltersUrlRewritePathType=ReplaceFullPath 63 | HTTPRouteRulesFiltersType=RequestHeaderModifier 64 | HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType=ReplaceFullPath 65 | HTTPRouteRulesBackendRefsFiltersUrlRewritePathType=ReplaceFullPath 66 | HTTPRouteRulesBackendRefsFiltersType=RequestHeaderModifier 67 | GRPCRouteRulesFiltersType=RequestHeaderModifier 68 | GRPCRouteRulesBackendRefsFiltersType=RequestHeaderModifier 69 | ) 70 | 71 | # Create a comma separated string out of $ENUMS. 72 | ENUMS_WITH_DEFAULTS=$(printf ",%s" "${ENUMS[@]}") 73 | ENUMS_WITH_DEFAULTS=${ENUMS_WITH_DEFAULTS:1} 74 | 75 | # The task searches for $GATEWAY_API_ENUMS in the enviornment to get the enum names and their default variants. 76 | GATEWAY_API_ENUMS=${ENUMS_WITH_DEFAULTS} cargo xtask gen_enum_defaults >> $APIS_DIR/standard/enum_defaults.rs 77 | echo "mod enum_defaults;" >> $APIS_DIR/standard/mod.rs 78 | 79 | GATEWAY_CLASS_CONDITION_CONSTANTS="GatewayClassConditionType=Accepted" 80 | GATEWAY_CLASS_REASON_CONSTANTS="GatewayClassConditionReason=Accepted,InvalidParameters,Pending,Unsupported,Waiting" 81 | GATEWAY_CONDITION_CONSTANTS="GatewayConditionType=Programmed,Accepted,Ready" 82 | GATEWAY_REASON_CONSTANTS="GatewayConditionReason=Programmed,Invalid,NoResources,AddressNotAssigned,AddressNotUsable,Accepted,ListenersNotValid,Pending,UnsupportedAddress,InvalidParameters,Ready,ListenersNotReady" 83 | LISTENER_CONDITION_CONSTANTS="ListenerConditionType=Conflicted,Accepted,ResolvedRefs,Programmed,Ready" 84 | LISTENER_REASON_CONSTANTS="ListenerConditionReason=HostnameConflict,ProtocolConflict,NoConflicts,Accepted,PortUnavailable,UnsupportedProtocol,ResolvedRefs,InvalidCertificateRef,InvalidRouteKinds,RefNotPermitted,Programmed,Invalid,Pending,Ready" 85 | 86 | GATEWAY_CLASS_CONDITION_CONSTANTS=${GATEWAY_CLASS_CONDITION_CONSTANTS} GATEWAY_CLASS_REASON_CONSTANTS=${GATEWAY_CLASS_REASON_CONSTANTS} \ 87 | GATEWAY_CONDITION_CONSTANTS=${GATEWAY_CONDITION_CONSTANTS} GATEWAY_REASON_CONSTANTS=${GATEWAY_REASON_CONSTANTS} \ 88 | LISTENER_CONDITION_CONSTANTS=${LISTENER_CONDITION_CONSTANTS} LISTENER_REASON_CONSTANTS=${LISTENER_REASON_CONSTANTS} \ 89 | cargo xtask gen_condition_constants >> $APIS_DIR/standard/constants.rs 90 | echo "pub mod constants;" >> $APIS_DIR/standard/mod.rs 91 | 92 | echo "// WARNING! generated file do not edit" > $APIS_DIR/experimental/mod.rs 93 | 94 | for API in "${EXPERIMENTAL_APIS[@]}" 95 | do 96 | echo "generating experimental api $API" 97 | curl -sSL "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${VERSION}/config/crd/experimental/gateway.networking.k8s.io_${API}.yaml" | kopium --schema=derived --derive=JsonSchema --derive=Default --derive=PartialEq --docs -f - > $APIS_DIR/experimental/${API}.rs 98 | echo "pub mod ${API};" >> $APIS_DIR/experimental/mod.rs 99 | done 100 | 101 | # Experimental API enums that need a Default trait impl along with their respective default variant. 102 | ENUMS=( 103 | HTTPRouteRulesFiltersRequestRedirectPathType=ReplaceFullPath 104 | HTTPRouteRulesFiltersUrlRewritePathType=ReplaceFullPath 105 | HTTPRouteRulesFiltersType=RequestHeaderModifier 106 | HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType=ReplaceFullPath 107 | HTTPRouteRulesBackendRefsFiltersUrlRewritePathType=ReplaceFullPath 108 | HTTPRouteRulesBackendRefsFiltersType=RequestHeaderModifier 109 | GRPCRouteRulesFiltersType=RequestHeaderModifier 110 | GRPCRouteRulesBackendRefsFiltersType=RequestHeaderModifier 111 | ) 112 | 113 | ENUMS_WITH_DEFAULTS=$(printf ",%s" "${ENUMS[@]}") 114 | ENUMS_WITH_DEFAULTS=${ENUMS_WITH_DEFAULTS:1} 115 | GATEWAY_API_ENUMS=${ENUMS_WITH_DEFAULTS} cargo xtask gen_enum_defaults >> $APIS_DIR/experimental/enum_defaults.rs 116 | echo "mod enum_defaults;" >> $APIS_DIR/experimental/mod.rs 117 | 118 | # GatewayClass conditions vary between standard and experimental 119 | GATEWAY_CLASS_CONDITION_CONSTANTS="${GATEWAY_CLASS_CONDITION_CONSTANTS},SupportedVersion" 120 | GATEWAY_CLASS_REASON_CONSTANTS="${GATEWAY_CLASS_REASON_CONSTANTS},SupportedVersion,UnsupportedVersion" 121 | 122 | GATEWAY_CLASS_CONDITION_CONSTANTS=${GATEWAY_CLASS_CONDITION_CONSTANTS} GATEWAY_CLASS_REASON_CONSTANTS=${GATEWAY_CLASS_REASON_CONSTANTS} \ 123 | GATEWAY_CONDITION_CONSTANTS=${GATEWAY_CONDITION_CONSTANTS} GATEWAY_REASON_CONSTANTS=${GATEWAY_REASON_CONSTANTS} \ 124 | LISTENER_CONDITION_CONSTANTS=${LISTENER_CONDITION_CONSTANTS} LISTENER_REASON_CONSTANTS=${LISTENER_REASON_CONSTANTS} \ 125 | cargo xtask gen_condition_constants >> $APIS_DIR/experimental/constants.rs 126 | echo "pub mod constants;" >> $APIS_DIR/experimental/mod.rs 127 | 128 | # Format the code. 129 | cargo fmt 130 | 131 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enum_default_generator" 3 | description = "Codegen tool to generate Default trait impls for Gateway API enums" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | version.workspace = true 9 | 10 | [dependencies] 11 | codegen = "0.2.0" 12 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::BTreeMap, env}; 2 | 3 | use codegen::{Enum, Function, Scope, Variant}; 4 | 5 | fn main() { 6 | let task = env::args().nth(1); 7 | 8 | match task.as_deref() { 9 | Some("gen_enum_defaults") => gen_enum_defaults().unwrap(), 10 | Some("gen_condition_constants") => gen_condition_constants().unwrap(), 11 | _ => print_help(), 12 | } 13 | } 14 | 15 | fn print_help() { 16 | eprintln!( 17 | "Tasks: 18 | 19 | gen_enum_defaults generates Default trait impls for enums 20 | gen_constants generates constants used for Conditions 21 | " 22 | ) 23 | } 24 | 25 | type DynError = Box; 26 | 27 | fn gen_condition_constants() -> Result<(), DynError> { 28 | let gateway_class_condition_types = env::var("GATEWAY_CLASS_CONDITION_CONSTANTS")?; 29 | let gateway_class_reason_types = env::var("GATEWAY_CLASS_REASON_CONSTANTS")?; 30 | let gateway_condition_types = env::var("GATEWAY_CONDITION_CONSTANTS")?; 31 | let gateway_reason_types = env::var("GATEWAY_REASON_CONSTANTS")?; 32 | let listener_condition_types = env::var("LISTENER_CONDITION_CONSTANTS")?; 33 | let listener_reason_types = env::var("LISTENER_REASON_CONSTANTS")?; 34 | 35 | let mut scope = Scope::new(); 36 | gen_const_enums(&mut scope, gateway_class_condition_types); 37 | gen_const_enums(&mut scope, gateway_class_reason_types); 38 | gen_const_enums(&mut scope, gateway_condition_types); 39 | gen_const_enums(&mut scope, gateway_reason_types); 40 | gen_const_enums(&mut scope, listener_condition_types); 41 | gen_const_enums(&mut scope, listener_reason_types); 42 | println!("{}", gen_generated_file_warning()); 43 | println!("{}", scope.to_string()); 44 | Ok(()) 45 | } 46 | 47 | fn gen_const_enums(scope: &mut Scope, constants: String) { 48 | let enum_type_and_variants: Vec<&str> = constants.split('=').collect(); 49 | let enum_type = enum_type_and_variants[0]; 50 | let variants: Vec<&str> = enum_type_and_variants[1].split(',').collect(); 51 | 52 | let mut enumeration = Enum::new(enum_type); 53 | enumeration.derive("Debug"); 54 | enumeration.derive("PartialEq"); 55 | enumeration.derive("Eq"); 56 | enumeration.vis("pub"); 57 | 58 | for variant in variants { 59 | let var = Variant::new(variant); 60 | enumeration.push_variant(var); 61 | } 62 | scope.push_enum(enumeration); 63 | 64 | gen_display_impl(scope, enum_type); 65 | } 66 | 67 | fn gen_display_impl(scope: &mut Scope, ty: &str) { 68 | let mut func = Function::new("fmt".to_string()); 69 | func.arg_ref_self(); 70 | func.arg("f", "&mut std::fmt::Formatter"); 71 | func.ret("std::fmt::Result"); 72 | func.line("write!(f, \"{:?}\", self)"); 73 | scope 74 | .new_impl(ty) 75 | .impl_trait("std::fmt::Display") 76 | .push_fn(func); 77 | } 78 | 79 | fn gen_enum_defaults() -> Result<(), DynError> { 80 | // GATEWAY_API_ENUMS provides the enum names along with their default variant to be used in the 81 | // generated Default impl. For eg: GATEWAY_API_ENUMS=enum1=default1,enum2=default2. 82 | let gw_api_enums = env::var("GATEWAY_API_ENUMS")?; 83 | let enums_with_defaults = get_enums_with_defaults_map(gw_api_enums); 84 | 85 | let mut scope = Scope::new(); 86 | let mut httproute_enums = vec![]; 87 | let mut grpcroute_enums = vec![]; 88 | 89 | for (e, d) in enums_with_defaults { 90 | // The `fn default()` function. 91 | let mut func = Function::new("default".to_string()); 92 | func.ret("Self").line(format!("{}::{}", e, d)); 93 | 94 | // The impl Default for implementation. 95 | scope 96 | .new_impl(e.as_str()) 97 | .impl_trait("Default") 98 | .push_fn(func); 99 | 100 | // Determine which enums belong to the httproute module and which belong to the 101 | // grpcroute module. 102 | if e.starts_with("HTTPRoute") { 103 | httproute_enums.push(e); 104 | } else if e.starts_with("GRPCRoute") { 105 | grpcroute_enums.push(e); 106 | } 107 | } 108 | 109 | println!("{}", gen_generated_file_warning()); 110 | 111 | // Generate use statements for the enums. 112 | if !httproute_enums.is_empty() { 113 | let use_http_stmt = gen_use_stmt(httproute_enums, "httproutes".to_string()); 114 | println!("{}\n", use_http_stmt); 115 | } 116 | if !grpcroute_enums.is_empty() { 117 | let use_grpc_stmt = gen_use_stmt(grpcroute_enums, "grpcroutes".to_string()); 118 | println!("{}\n", use_grpc_stmt); 119 | } 120 | 121 | println!("{}", scope.to_string()); 122 | Ok(()) 123 | } 124 | 125 | fn gen_generated_file_warning() -> String { 126 | "// WARNING: generated file - manual changes will be overriden\n".into() 127 | } 128 | 129 | fn gen_use_stmt(items: Vec, module: String) -> String { 130 | let mut stmt = format!("use super::{}::{{", module); 131 | for item in items { 132 | stmt.push_str(format!("{}, ", item).as_str()); 133 | } 134 | stmt.push_str("};"); 135 | stmt 136 | } 137 | 138 | fn get_enums_with_defaults_map(env_var_val: String) -> BTreeMap { 139 | let mut enums_with_defaults = BTreeMap::new(); 140 | env_var_val.split(',').for_each(|enum_with_default| { 141 | let enum_and_default: Vec<&str> = enum_with_default.split('=').collect(); 142 | enums_with_defaults.insert( 143 | enum_and_default[0].to_string(), 144 | enum_and_default[1].to_string(), 145 | ); 146 | }); 147 | 148 | enums_with_defaults 149 | } 150 | --------------------------------------------------------------------------------