From afdf932615c73a1f0e8cbce65176cbd71cdcf8b5 Mon Sep 17 00:00:00 2001 From: Alex Leong Date: Mon, 10 Jul 2023 11:21:06 -0700 Subject: [PATCH 01/13] Add ResponseHeaderModifier filter (#62) As in https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter and https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go#L615 Signed-off-by: Alex Leong --- src/httproute.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/httproute.rs b/src/httproute.rs index 2337997..f2f36bd 100644 --- a/src/httproute.rs +++ b/src/httproute.rs @@ -361,6 +361,15 @@ pub enum HttpRouteFilter { request_header_modifier: HttpRequestHeaderFilter, }, + /// ResponseHeaderModifier defines a schema for a filter that modifies + /// response headers. + /// + /// Support: Extended + #[serde(rename_all = "camelCase")] + ResponseHeaderModifier { + response_header_modifier: HttpRequestHeaderFilter, + }, + /// RequestMirror defines a schema for a filter that mirrors requests. /// Requests are sent to the specified destination, but responses from /// that destination are ignored. From 2f142ef2209202a147b69633757c9272b1340cdf Mon Sep 17 00:00:00 2001 From: Alex Leong Date: Mon, 10 Jul 2023 13:52:57 -0700 Subject: [PATCH 02/13] Update to v0.12.0 (#63) Change updates the library to `0.12.0` to include the ResponseHeaderModifier filter. Signed-off-by: Alex Leong --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3f5ada7..b6cee87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "integration"] [package] name = "k8s-gateway-api" -version = "0.11.0" +version = "0.12.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/linkerd/k8s-gateway-api" From 2c8e77e7befb20cbfcbffca967085d201fc4dad6 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 15 Aug 2023 14:40:11 -0700 Subject: [PATCH 03/13] update to `kube-rs` v0.85 and `k8s-openapi` v0.19 (#64) This branch updates the dependency on `k8s-openapi` to v0.19 and the dependency on `kube-rs` to v0.85. This release of `k8s-openapi` also adds support for Kubernetes v1.27, so I've updated the CI integration workflow to run tests against that Kubernetes version as well as the existing ones. Since this is a breaking change (these are public API deps), I've gone ahead and bumped the crate version to v0.13.0, as well. --- .github/workflows/integration.yml | 1 + Cargo.toml | 12 ++++++------ integration/Cargo.toml | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 73011c7..c5a7855 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -28,6 +28,7 @@ jobs: - v1.21 - v1.25 - v1.26 + - v1.27 timeout-minutes: 10 runs-on: ubuntu-latest env: diff --git a/Cargo.toml b/Cargo.toml index b6cee87..684480a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "integration"] [package] name = "k8s-gateway-api" -version = "0.12.0" +version = "0.13.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/linkerd/k8s-gateway-api" @@ -16,17 +16,17 @@ default = [] experimental = [] [dependencies] -kube = { version = "0.80", default-features = false, features = ["derive"] } -k8s-openapi = { version = "0.17", features = ["schemars"] } +kube = { version = "0.85", default-features = false, features = ["derive"] } +k8s-openapi = { version = "0.19", features = ["schemars"] } schemars = { version = "0.8", features = ["derive"] } serde = { version = "1", features = ["derive"] } serde_json = "1" [dev-dependencies.k8s-openapi] -version = "0.17" +version = "0.19" default-features = false -features = ["v1_21"] +features = ["v1_27"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] -features = ["experimental", "k8s-openapi/v1_25"] +features = ["experimental", "k8s-openapi/v1_27"] diff --git a/integration/Cargo.toml b/integration/Cargo.toml index e20b36e..4200ef3 100644 --- a/integration/Cargo.toml +++ b/integration/Cargo.toml @@ -6,12 +6,12 @@ license = "Apache-2.0" publish = false [dev-dependencies] -k8s-openapi = { version = "0.17", features = ["v1_21"] } +k8s-openapi = { version = "0.19", features = ["v1_27"] } tokio = { version = "1", features = ["macros", "rt"] } tracing = "0.1" k8s-gateway-api = { path = ".." } [dev-dependencies.kube] -version = "0.80" +version = "0.85" default-features = false features = ["client", "openssl-tls", "runtime", "ws"] From 4f22a786c21b09b28ca1e82dd01cdf2690024365 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:22:34 -0800 Subject: [PATCH 04/13] Bump DavidAnson/markdownlint-cli2-action from 9.0.0 to 13.0.0 (#74) Bumps [DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action) from 9.0.0 to 13.0.0. - [Release notes](https://github.com/davidanson/markdownlint-cli2-action/releases) - [Commits](https://github.com/davidanson/markdownlint-cli2-action/compare/5b7c9f74fec47e6b15667b2cc23c63dff11e449e...ed4dec634fd2ef689c7061d5647371d8248064f1) --- updated-dependencies: - dependency-name: DavidAnson/markdownlint-cli2-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/markdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index 4f36b96..1ace187 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - - uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e + - uses: DavidAnson/markdownlint-cli2-action@ed4dec634fd2ef689c7061d5647371d8248064f1 with: globs: | **/*.md From 38bbd4436399f831adab32fe95f857e1fb161247 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Mon, 11 Dec 2023 07:28:26 -0800 Subject: [PATCH 05/13] Fix repo link in Cargo.toml (#81) Fixes #66 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 684480a..0cc88e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ name = "k8s-gateway-api" version = "0.13.0" edition = "2021" license = "Apache-2.0" -repository = "https://github.com/linkerd/k8s-gateway-api" +repository = "https://github.com/linkerd/k8s-gateway-api-rs" rust-version = "1.60" keywords = ["kubernetes", "gateway"] description = "Rust bindings for the Kubenetes Gateway API" From 9e1b1b3726faaf973ea1d6178ec2b82271c0041d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:29:37 -0800 Subject: [PATCH 06/13] Bump actions/checkout from 3.5.0 to 4.1.1 (#76) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 4.1.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8f4b7f84864484a7bf31766abe9204da3cbe65b3...b4ffde65f46336ab88eb53be808477a3936bae11) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/actions.yml | 4 ++-- .github/workflows/integration.yml | 4 ++-- .github/workflows/lint.yml | 2 +- .github/workflows/markdown.yml | 2 +- .github/workflows/release.yml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 0d76b19..40c8fab 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -14,13 +14,13 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 10 steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: linkerd/dev/actions/setup-tools@v39 - run: just-dev lint-actions devcontainer-versions: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: linkerd/dev/actions/setup-tools@v39 - run: just-dev check-action-images diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index c5a7855..809d62a 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -44,13 +44,13 @@ jobs: - run: just-k3d create - run: kubectl version # Install CRDs - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 with: repository: kubernetes-sigs/gateway-api ref: 4f86f0bd65173b04dadb558f63fbbd53330736d2 # 0.5.0-rc1 path: gateway-api - run: kubectl apply -k gateway-api/config/crd/experimental/ - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Run tests - run: just fetch - run: just test-build --package=integration diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d3f549f..c48e934 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest container: docker://ghcr.io/linkerd/dev:v39-rust steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - run: just fetch - run: just check-fmt - run: just clippy diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index 1ace187..b3a863c 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: DavidAnson/markdownlint-cli2-action@ed4dec634fd2ef689c7061d5647371d8248064f1 with: globs: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d90ba63..5c8a040 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest container: docker://ghcr.io/linkerd/dev:v39-rust steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - run: just fetch - run: just test-build - run: just test @@ -58,7 +58,7 @@ jobs: container: docker://ghcr.io/linkerd/dev:v39-rust steps: - if: needs.meta.outputs.publish - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - if: needs.meta.outputs.publish shell: bash run: | @@ -82,7 +82,7 @@ jobs: runs-on: ubuntu-latest container: docker://ghcr.io/linkerd/dev:v39-rust steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - if: needs.meta.outputs.publish == '' run: just publish --dry-run - if: needs.meta.outputs.publish == 'true' From ec91fdee8346dea851eb8e14eceeff39897ab211 Mon Sep 17 00:00:00 2001 From: Alex Leong Date: Mon, 11 Dec 2023 12:09:10 -0800 Subject: [PATCH 07/13] Update gateway types to v1 (#79) Update gateway API type to v1 as defined at https://github.com/kubernetes-sigs/gateway-api/tree/main/apis/v1 Signed-off-by: Alex Leong --- .github/workflows/integration.yml | 2 +- .github/workflows/lint.yml | 2 +- Cargo.toml | 1 + integration/tests/gateway.rs | 1 + integration/tests/httproute.rs | 2 + src/duration.rs | 354 ++++++++++++++++++++++++++++++ src/gateway.rs | 304 +++++++++++++++++-------- src/gatewayclass.rs | 48 ++-- src/httproute.rs | 296 ++++++++++++++++++++----- src/lib.rs | 1 + src/shared.rs | 229 ++++++++++++++----- 11 files changed, 1013 insertions(+), 227 deletions(-) create mode 100644 src/duration.rs diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 809d62a..8528278 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 with: repository: kubernetes-sigs/gateway-api - ref: 4f86f0bd65173b04dadb558f63fbbd53330736d2 # 0.5.0-rc1 + ref: a0684982eddeb0360e215e0de322c3210ac49bb9 # v1.0.0 path: gateway-api - run: kubectl apply -k gateway-api/config/crd/experimental/ - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c48e934..a024328 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ env: jobs: lint: - timeout-minutes: 5 + timeout-minutes: 20 runs-on: ubuntu-latest container: docker://ghcr.io/linkerd/dev:v39-rust steps: diff --git a/Cargo.toml b/Cargo.toml index 0cc88e3..5728b0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ k8s-openapi = { version = "0.19", features = ["schemars"] } schemars = { version = "0.8", features = ["derive"] } serde = { version = "1", features = ["derive"] } serde_json = "1" +thiserror = "1" [dev-dependencies.k8s-openapi] version = "0.19" diff --git a/integration/tests/gateway.rs b/integration/tests/gateway.rs index 672cfed..418f60a 100644 --- a/integration/tests/gateway.rs +++ b/integration/tests/gateway.rs @@ -19,6 +19,7 @@ async fn round_trip() { }, spec: GatewaySpec { gateway_class_name: "acme-lb".to_string(), + infrastructure: None, listeners: vec![Listener { protocol: "HTTPS".to_string(), port: 443, diff --git a/integration/tests/httproute.rs b/integration/tests/httproute.rs index 8742691..93a3ee1 100644 --- a/integration/tests/httproute.rs +++ b/integration/tests/httproute.rs @@ -41,6 +41,7 @@ async fn round_trip() { ]), filters: None, matches: None, + timeouts: None, }, HttpRouteRule { matches: Some(vec![HttpRouteMatch { @@ -58,6 +59,7 @@ async fn round_trip() { filters: None, }]), filters: None, + timeouts: None, }, ]), ..HttpRouteSpec::default() diff --git a/src/duration.rs b/src/duration.rs new file mode 100644 index 0000000..969ec9e --- /dev/null +++ b/src/duration.rs @@ -0,0 +1,354 @@ +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use std::{fmt, str::FromStr, time::Duration}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct K8sDuration { + duration: Duration, + is_negative: bool, +} + +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +#[non_exhaustive] +pub enum ParseError { + #[error("invalid unit: {}", EXPECTED_UNITS)] + InvalidUnit, + + #[error("missing a unit: {}", EXPECTED_UNITS)] + NoUnit, + + #[error("invalid floating-point number: {}", .0)] + NotANumber(#[from] std::num::ParseFloatError), +} + +const EXPECTED_UNITS: &str = "expected one of 'ns', 'us', '\u{00b5}s', 'ms', 's', 'm', or 'h'"; + +impl From for K8sDuration { + fn from(duration: Duration) -> Self { + Self { + duration, + is_negative: false, + } + } +} + +impl From for Duration { + fn from(K8sDuration { duration, .. }: K8sDuration) -> Self { + duration + } +} + +impl K8sDuration { + #[inline] + #[must_use] + pub fn is_negative(&self) -> bool { + self.is_negative + } +} + +impl fmt::Debug for K8sDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use std::fmt::Write; + if self.is_negative { + f.write_char('-')?; + } + fmt::Debug::fmt(&self.duration, f) + } +} + +impl fmt::Display for K8sDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use std::fmt::Write; + if self.is_negative { + f.write_char('-')?; + } + fmt::Debug::fmt(&self.duration, f) + } +} + +impl FromStr for K8sDuration { + type Err = ParseError; + + fn from_str(mut s: &str) -> Result { + // implements the same format as + // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/time/format.go;l=1589 + + fn duration_from_units(val: f64, unit: &str) -> Result { + const MINUTE: Duration = Duration::from_secs(60); + // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/time/format.go;l=1573 + let base = match unit { + "ns" => Duration::from_nanos(1), + // U+00B5 is the "micro sign" while U+03BC is "Greek letter mu" + "us" | "\u{00b5}s" | "\u{03bc}s" => Duration::from_micros(1), + "ms" => Duration::from_millis(1), + "s" => Duration::from_secs(1), + "m" => MINUTE, + "h" => MINUTE * 60, + _ => return Err(ParseError::InvalidUnit), + }; + Ok(base.mul_f64(val)) + } + + // Go durations are signed. Rust durations aren't. So we need to ignore + // this for now. + let is_negative = s.starts_with('-'); + s = s.trim_start_matches('+').trim_start_matches('-'); + + let mut total = Duration::from_secs(0); + while !s.is_empty() { + if let Some(unit_start) = s.find(|c: char| c.is_alphabetic()) { + let (val, rest) = s.split_at(unit_start); + let val = val.parse::()?; + let unit = if let Some(next_numeric_start) = rest.find(|c: char| !c.is_alphabetic()) + { + let (unit, rest) = rest.split_at(next_numeric_start); + s = rest; + unit + } else { + s = ""; + rest + }; + total += duration_from_units(val, unit)?; + } else if s == "0" { + return Ok(K8sDuration { + duration: Duration::from_secs(0), + is_negative, + }); + } else { + return Err(ParseError::NoUnit); + } + } + + Ok(K8sDuration { + duration: total, + is_negative, + }) + } +} + +impl Serialize for K8sDuration { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> Deserialize<'de> for K8sDuration { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct Visitor; + impl<'de> de::Visitor<'de> for Visitor { + type Value = K8sDuration; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a string in Go `time.Duration.String()` format") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let val = value.parse::().map_err(de::Error::custom)?; + Ok(val) + } + } + deserializer.deserialize_str(Visitor) + } +} + +impl schemars::JsonSchema for K8sDuration { + // see + // https://github.com/kubernetes/apimachinery/blob/756e2227bf3a486098f504af1a0ffb736ad16f4c/pkg/apis/meta/v1/duration.go#L61 + fn schema_name() -> String { + "K8sDuration".to_owned() + } + + fn is_referenceable() -> bool { + false + } + + fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + schemars::schema::SchemaObject { + instance_type: Some(schemars::schema::InstanceType::String.into()), + // the format should *not* be "duration", because "duration" means + // the duration is formatted in ISO 8601, as described here: + // https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-02#section-7.3.1 + format: None, + ..Default::default() + } + .into() + } +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_the_same_as_go() { + const MINUTE: Duration = Duration::from_secs(60); + const HOUR: Duration = Duration::from_secs(60 * 60); + // from Go: + // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/time/time_test.go;l=891-951 + // ``` + // var parseDurationTests = []struct { + // in string + // want Duration + // }{ + let cases: &[(&str, K8sDuration)] = &[ + // // simple + // {"0", 0}, + ("0", Duration::from_secs(0).into()), + // {"5s", 5 * Second}, + ("5s", Duration::from_secs(5).into()), + // {"30s", 30 * Second}, + ("30s", Duration::from_secs(30).into()), + // {"1478s", 1478 * Second}, + ("1478s", Duration::from_secs(1478).into()), + // // sign + // {"-5s", -5 * Second}, + ( + "-5s", + K8sDuration { + duration: Duration::from_secs(5), + is_negative: true, + }, + ), + // {"+5s", 5 * Second}, + ("+5s", Duration::from_secs(5).into()), + // {"-0", 0}, + ( + "-0", + K8sDuration { + duration: Duration::from_secs(0), + is_negative: true, + }, + ), + // {"+0", 0}, + ("+0", Duration::from_secs(0).into()), + // // decimal + // {"5.0s", 5 * Second}, + ("5s", Duration::from_secs(5).into()), + // {"5.6s", 5*Second + 600*Millisecond}, + ( + "5.6s", + (Duration::from_secs(5) + Duration::from_millis(600)).into(), + ), + // {"5.s", 5 * Second}, + ("5.s", Duration::from_secs(5).into()), + // {".5s", 500 * Millisecond}, + (".5s", Duration::from_millis(500).into()), + // {"1.0s", 1 * Second}, + ("1.0s", Duration::from_secs(1).into()), + // {"1.00s", 1 * Second}, + ("1.00s", Duration::from_secs(1).into()), + // {"1.004s", 1*Second + 4*Millisecond}, + ( + "1.004s", + (Duration::from_secs(1) + Duration::from_millis(4)).into(), + ), + // {"1.0040s", 1*Second + 4*Millisecond}, + ( + "1.0040s", + (Duration::from_secs(1) + Duration::from_millis(4)).into(), + ), + // {"100.00100s", 100*Second + 1*Millisecond}, + ( + "100.00100s", + (Duration::from_secs(100) + Duration::from_millis(1)).into(), + ), + // // different units + // {"10ns", 10 * Nanosecond}, + ("10ns", Duration::from_nanos(10).into()), + // {"11us", 11 * Microsecond}, + ("11us", Duration::from_micros(11).into()), + // {"12µs", 12 * Microsecond}, // U+00B5 + ("12µs", Duration::from_micros(12).into()), + // {"12μs", 12 * Microsecond}, // U+03BC + ("12μs", Duration::from_micros(12).into()), + // {"13ms", 13 * Millisecond}, + ("13ms", Duration::from_millis(13).into()), + // {"14s", 14 * Second}, + ("14s", Duration::from_secs(14).into()), + // {"15m", 15 * Minute}, + ("15m", (15 * MINUTE).into()), + // {"16h", 16 * Hour}, + ("16h", (16 * HOUR).into()), + // // composite durations + // {"3h30m", 3*Hour + 30*Minute}, + ("3h30m", (3 * HOUR + 30 * MINUTE).into()), + // {"10.5s4m", 4*Minute + 10*Second + 500*Millisecond}, + ( + "10.5s4m", + (4 * MINUTE + Duration::from_secs(10) + Duration::from_millis(500)).into(), + ), + // {"-2m3.4s", -(2*Minute + 3*Second + 400*Millisecond)}, + ( + "-2m3.4s", + K8sDuration { + duration: 2 * MINUTE + Duration::from_secs(3) + Duration::from_millis(400), + is_negative: true, + }, + ), + // {"1h2m3s4ms5us6ns", 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond}, + ( + "1h2m3s4ms5us6ns", + (1 * HOUR + + 2 * MINUTE + + Duration::from_secs(3) + + Duration::from_millis(4) + + Duration::from_micros(5) + + Duration::from_nanos(6)) + .into(), + ), + // {"39h9m14.425s", 39*Hour + 9*Minute + 14*Second + 425*Millisecond}, + ( + "39h9m14.425s", + (39 * HOUR + 9 * MINUTE + Duration::from_secs(14) + Duration::from_millis(425)) + .into(), + ), + // // large value + // {"52763797000ns", 52763797000 * Nanosecond}, + ("52763797000ns", Duration::from_nanos(52763797000).into()), + // // more than 9 digits after decimal point, see https://golang.org/issue/6617 + // {"0.3333333333333333333h", 20 * Minute}, + ("0.3333333333333333333h", (20 * MINUTE).into()), + // // 9007199254740993 = 1<<53+1 cannot be stored precisely in a float64 + // {"9007199254740993ns", (1<<53 + 1) * Nanosecond}, + ( + "9007199254740993ns", + Duration::from_nanos((1 << 53) + 1).into(), + ), + // Rust Durations can handle larger durations than Go's + // representation, so skip these tests for their precision limits + + // // largest duration that can be represented by int64 in nanoseconds + // {"9223372036854775807ns", (1<<63 - 1) * Nanosecond}, + // ("9223372036854775807ns", Duration::from_nanos((1 << 63) - 1).into()), + // {"9223372036854775.807us", (1<<63 - 1) * Nanosecond}, + // ("9223372036854775.807us", Duration::from_nanos((1 << 63) - 1).into()), + // {"9223372036s854ms775us807ns", (1<<63 - 1) * Nanosecond}, + // {"-9223372036854775808ns", -1 << 63 * Nanosecond}, + // {"-9223372036854775.808us", -1 << 63 * Nanosecond}, + // {"-9223372036s854ms775us808ns", -1 << 63 * Nanosecond}, + // // largest negative value + // {"-9223372036854775808ns", -1 << 63 * Nanosecond}, + // // largest negative round trip value, see https://golang.org/issue/48629 + // {"-2562047h47m16.854775808s", -1 << 63 * Nanosecond}, + + // // huge string; issue 15011. + // {"0.100000000000000000000h", 6 * Minute}, + ("0.100000000000000000000h", (6 * MINUTE).into()), // // This value tests the first overflow check in leadingFraction. + // {"0.830103483285477580700h", 49*Minute + 48*Second + 372539827*Nanosecond}, + // } + // ``` + ]; + + for (input, expected) in cases { + let parsed = dbg!(input).parse::().unwrap(); + assert_eq!(&dbg!(parsed), expected); + } + } +} diff --git a/src/gateway.rs b/src/gateway.rs index 9510a54..204cd31 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -9,7 +9,7 @@ use std::collections::BTreeMap; )] #[kube( group = "gateway.networking.k8s.io", - version = "v1beta1", + version = "v1", kind = "Gateway", status = "GatewayStatus", namespaced @@ -20,42 +20,121 @@ pub struct GatewaySpec { /// GatewayClass resource. pub gateway_class_name: ObjectName, - /// Listeners associated with this Gateway. Listeners define logical - /// endpoints that are bound on this Gateway's addresses. At least one - /// Listener MUST be specified. - /// - /// Each listener in a Gateway must have a unique combination of Hostname, - /// Port, and Protocol. - /// - /// An implementation MAY group Listeners by Port and then collapse each - /// group of Listeners into a single Listener if the implementation - /// determines that the Listeners in the group are "compatible". An - /// implementation MAY also group together and collapse compatible Listeners - /// belonging to different Gateways. - /// - /// For example, an implementation might consider Listeners to be compatible - /// with each other if all of the following conditions are met: - /// - /// 1. Either each Listener within the group specifies the "HTTP" Protocol or - /// each Listener within the group specifies either the "HTTPS" or "TLS" - /// Protocol. - /// - /// 2. Each Listener within the group specifies a Hostname that is unique - /// within the group. - /// - /// 3. As a special case, one Listener within a group may omit Hostname, in - /// which case this Listener matches when no other Listener matches. - /// - /// If the implementation does collapse compatible Listeners, the hostname - /// provided in the incoming client request MUST be matched to a Listener to - /// find the correct set of Routes. The incoming hostname MUST be matched - /// using the Hostname field for each Listener in order of most to least - /// specific. That is, exact matches must be processed before wildcard - /// matches. - /// - /// If this field specifies multiple Listeners that have the same Port value - /// but are not compatible, the implementation must raise a "Conflicted" - /// condition in the Listener status. + /// Listeners associated with this Gateway. Listeners define + /// logical endpoints that are bound on this Gateway's addresses. + /// At least one Listener MUST be specified. + /// + /// Each Listener in a set of Listeners (for example, in a single Gateway) + /// MUST be _distinct_, in that a traffic flow MUST be able to be assigned to + /// exactly one listener. (This section uses "set of Listeners" rather than + /// "Listeners in a single Gateway" because implementations MAY merge configuration + /// from multiple Gateways onto a single data plane, and these rules _also_ + /// apply in that case). + /// + /// Practically, this means that each listener in a set MUST have a unique + /// combination of Port, Protocol, and, if supported by the protocol, Hostname. + /// + /// Some combinations of port, protocol, and TLS settings are considered + /// Core support and MUST be supported by implementations based on their + /// targeted conformance profile: + /// + /// HTTP Profile + /// + /// 1. HTTPRoute, Port: 80, Protocol: HTTP + /// 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided + /// + /// TLS Profile + /// + /// 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough + /// + // /"Distinct" Listeners have the following property: + /// + /// The implementation can match inbound requests to a single distinct + /// Listener. When multiple Listeners share values for fields (for + /// example, two Listeners with the same Port value), the implementation + /// can match requests to only one of the Listeners using other + /// Listener fields. + /// + /// For example, the following Listener scenarios are distinct: + /// + /// 1. Multiple Listeners with the same Port that all use the "HTTP" + /// Protocol that all have unique Hostname values. + /// 2. Multiple Listeners with the same Port that use either the "HTTPS" or + /// "TLS" Protocol that all have unique Hostname values. + /// 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener + /// with the same Protocol has the same Port value. + /// + /// Some fields in the Listener struct have possible values that affect + /// whether the Listener is distinct. Hostname is particularly relevant + /// for HTTP or HTTPS protocols. + /// + /// When using the Hostname value to select between same-Port, same-Protocol + /// Listeners, the Hostname value must be different on each Listener for the + /// Listener to be distinct. + /// + /// When the Listeners are distinct based on Hostname, inbound request + /// hostnames MUST match from the most specific to least specific Hostname + /// values to choose the correct Listener and its associated set of Routes. + /// + /// Exact matches must be processed before wildcard matches, and wildcard + /// matches must be processed before fallback (empty Hostname value) + /// matches. For example, `"foo.example.com"` takes precedence over + /// `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. + /// + /// Additionally, if there are multiple wildcard entries, more specific + /// wildcard entries must be processed before less specific wildcard entries. + /// For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. + /// The precise definition here is that the higher the number of dots in the + /// hostname to the right of the wildcard character, the higher the precedence. + /// + /// The wildcard character will match any number of characters _and dots_ to + /// the left, however, so `"*.example.com"` will match both + /// `"foo.bar.example.com"` _and_ `"bar.example.com"`. + /// + /// If a set of Listeners contains Listeners that are not distinct, then those + /// Listeners are Conflicted, and the implementation MUST set the "Conflicted" + /// condition in the Listener Status to "True". + /// + /// Implementations MAY choose to accept a Gateway with some Conflicted + /// Listeners only if they only accept the partial Listener set that contains + /// no Conflicted Listeners. To put this another way, implementations may + /// accept a partial Listener set only if they throw out *all* the conflicting + /// Listeners. No picking one of the conflicting listeners as the winner. + /// This also means that the Gateway must have at least one non-conflicting + /// Listener in this case, otherwise it violates the requirement that at + /// least one Listener must be present. + /// + /// The implementation MUST set a "ListenersNotValid" condition on the + /// Gateway Status when the Gateway contains Conflicted Listeners whether or + /// not they accept the Gateway. That Condition SHOULD clearly + /// indicate in the Message which Listeners are conflicted, and which are + /// Accepted. Additionally, the Listener status for those listeners SHOULD + /// indicate which Listeners are conflicted and not Accepted. + /// + /// A Gateway's Listeners are considered "compatible" if: + /// + /// 1. They are distinct. + /// 2. The implementation can serve them in compliance with the Addresses + /// requirement that all Listeners are available on all assigned + /// addresses. + /// + /// Compatible combinations in Extended support are expected to vary across + /// implementations. A combination that is compatible for one implementation + /// may not be compatible for another. + /// + /// For example, an implementation that cannot serve both TCP and UDP listeners + /// on the same address, or cannot mix HTTPS and generic TLS listens on the same port + /// would not consider those cases compatible, even though they are distinct. + /// + /// Note that requests SHOULD match at most one Listener. For example, if + /// Listeners are defined for "foo.example.com" and "*.example.com", a + /// request to "foo.example.com" SHOULD only be routed using routes attached + /// to the "foo.example.com" Listener (and not the "*.example.com" Listener). + /// This concept is known as "Listener Isolation". Implementations that do + /// not support Listener Isolation MUST clearly document this. + /// + /// Implementations MAY merge separate Gateways onto a single set of + /// Addresses if all Listeners across all Gateways are compatible. /// /// Support: Core pub listeners: Vec, @@ -71,9 +150,6 @@ pub struct GatewaySpec { /// other networking infrastructure, or some other address that traffic will /// be sent to. /// - /// The .listener.hostname field is used to route traffic that has already - /// arrived at the Gateway to the correct in-cluster destination. - /// /// If no Addresses are specified, the implementation MAY schedule the /// Gateway in an implementation-specific manner, assigning an appropriate /// set of Addresses. @@ -84,6 +160,11 @@ pub struct GatewaySpec { /// /// Support: Extended pub addresses: Option>, + + /// Infrastructure defines infrastructure level attributes about this Gateway instance. + /// + /// Support: Core + pub infrastructure: Option, } /// Listener embodies the concept of a logical endpoint where a Gateway accepts @@ -105,12 +186,12 @@ pub struct Listener { /// Implementations MUST apply Hostname matching appropriately for each of /// the following protocols: /// - /// * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener - /// Hostname MUST match the Host header of the request. * HTTPS: The - /// Listener Hostname SHOULD match at both the TLS and HTTP protocol layers - /// as described above. If an implementation does not ensure that both the - /// SNI and Host header match the Listener hostname, it MUST clearly document - /// that. + /// * TLS: The Listener Hostname MUST match the SNI. + /// * HTTP: The Listener Hostname MUST match the Host header of the request. + /// * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + /// protocol layers as described above. If an implementation does not + /// ensure that both the SNI and Host header match the Listener hostname, + /// it MUST clearly document that. /// /// For HTTPRoute and TLSRoute resources, there is an interaction with the /// `spec.hostnames` array. When both listener and route specify hostnames, @@ -118,6 +199,10 @@ pub struct Listener { /// accepted. For more information, refer to the Route specific Hostnames /// documentation. /// + /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + /// as a suffix match. That means that a match for `*.example.com` would match + /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + /// /// Support: Core pub hostname: Option, @@ -137,8 +222,8 @@ pub struct Listener { /// The association of SNIs to Certificate defined in GatewayTLSConfig is /// defined based on the Hostname field for this listener. /// - /// The GatewayClass MUST use the longest matching SNI out of all available - /// certificates for any TLS handshake. + /// The GatewayClass MUST use the longest matching SNI out of all + /// available certificates for any TLS handshake. /// /// Support: Core pub tls: Option, @@ -152,16 +237,16 @@ pub struct Listener { /// determined in order of the following criteria: /// /// * The most specific match as defined by the Route type. - /// * The oldest Route based on creation timestamp. For example, a Route - /// with a creation timestamp of "2020-09-08 01:02:03" is given precedence - /// over a Route with a creation timestamp of "2020-09-08 01:02:04". + /// * The oldest Route based on creation timestamp. For example, a Route with + /// a creation timestamp of "2020-09-08 01:02:03" is given precedence over + /// a Route with a creation timestamp of "2020-09-08 01:02:04". /// * If everything else is equivalent, the Route appearing first in /// alphabetical order (namespace/name) should be given precedence. For /// example, foo/bar is given precedence over foo/baz. /// /// All valid rules within a Route attached to this Listener should be - /// implemented. Invalid Route rules can be ignored (sometimes that will - /// mean the full Route). If a Route rule transitions from valid to invalid, + /// implemented. Invalid Route rules can be ignored (sometimes that will mean + /// the full Route). If a Route rule transitions from valid to invalid, /// support for that Route rule should be dropped to ensure consistency. For /// example, even if a filter specified by a Route rule is invalid, the rest /// of the rules within that Route should still be supported. @@ -171,18 +256,18 @@ pub struct Listener { } /// ProtocolType defines the application protocol accepted by a Listener. -/// Implementations are not required to accept all the defined protocols. -/// If an implementation does not support a specified protocol, it -/// should raise a "Detached" condition for the affected Listener with -/// a reason of "UnsupportedProtocol". +/// Implementations are not required to accept all the defined protocols. If an +/// implementation does not support a specified protocol, it MUST set the +/// "Accepted" condition to False for the affected Listener with a reason of +/// "UnsupportedProtocol". /// /// Core ProtocolType values are listed in the table below. /// /// Implementations can define their own protocols if a core ProtocolType does not /// exist. Such definitions must use prefixed name, such as /// `mycompany.com/my-custom-protocol`. Un-prefixed names are reserved for core -/// protocols. Any protocol defined by implementations will fall under custom -/// conformance. +/// protocols. Any protocol defined by implementations will fall under +/// Implementation-specific conformance. /// /// Valid values include: /// @@ -193,7 +278,6 @@ pub struct Listener { /// /// * "example.com" - must include path if domain is used /// * "foo.example.com" - must include path if domain is used -/// pub type ProtocolType = String; /// GatewayTLSConfig describes a TLS configuration. @@ -202,34 +286,34 @@ pub type ProtocolType = String; )] #[serde(rename_all = "camelCase")] pub struct GatewayTlsConfig { - /// Mode defines the TLS behavior for the TLS session initiated by the - /// client. There are two possible modes: + /// Mode defines the TLS behavior for the TLS session initiated by the client. + /// There are two possible modes: /// - /// - Terminate: The TLS session between the downstream client and the - /// Gateway is terminated at the Gateway. This mode requires + /// - Terminate: The TLS session between the downstream client + /// and the Gateway is terminated at the Gateway. This mode requires /// certificateRefs to be set and contain at least one element. /// - Passthrough: The TLS session is NOT terminated by the Gateway. This - /// implies that the Gateway can't decipher the TLS stream except for the - /// ClientHello message of the TLS protocol. CertificateRefs field is - /// ignored in this mode. + /// implies that the Gateway can't decipher the TLS stream except for + /// the ClientHello message of the TLS protocol. + /// CertificateRefs field is ignored in this mode. /// /// Support: Core pub mode: Option, - /// CertificateRefs contains a series of references to Kubernetes objects - /// that contains TLS certificates and private keys. These certificates are - /// used to establish a TLS handshake for requests that match the hostname - /// of the associated listener. + /// CertificateRefs contains a series of references to Kubernetes objects that + /// contains TLS certificates and private keys. These certificates are used to + /// establish a TLS handshake for requests that match the hostname of the + /// associated listener. /// /// A single CertificateRef to a Kubernetes Secret has "Core" support. /// Implementations MAY choose to support attaching multiple certificates to /// a Listener, but this behavior is implementation-specific. /// /// References to a resource in different namespace are invalid UNLESS there - /// is a ReferencePolicy in the target namespace that allows the certificate - /// to be attached. If a ReferencePolicy does not allow this reference, the + /// is a ReferenceGrant in the target namespace that allows the certificate + /// to be attached. If a ReferenceGrant does not allow this reference, the /// "ResolvedRefs" condition MUST be set to False for this listener with the - /// "InvalidCertificateRef" reason. + /// "RefNotPermitted" reason. /// /// This field is required to have at least one element when the mode is set /// to "Terminate" (default) and is optional otherwise. @@ -237,11 +321,9 @@ pub struct GatewayTlsConfig { /// CertificateRefs can reference to standard Kubernetes resources, i.e. /// Secret, or implementation-specific custom resources. /// - /// Support: Core - A single reference to a Kubernetes Secret of type - /// kubernetes.io/tls + /// Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls /// - /// Support: Implementation-specific (More than one reference or other - /// resource types) + /// Support: Implementation-specific (More than one reference or other resource types) pub certificate_refs: Option>, /// Options are a list of key/value pairs to enable extended TLS @@ -340,28 +422,61 @@ pub struct GatewayAddress { /// GatewayStatus defines the observed state of Gateway. #[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct GatewayStatus { - /// Addresses lists the IP addresses that have actually been bound to the - /// Gateway. These addresses may differ from the addresses in the Spec, e.g. - /// if the Gateway automatically assigns an address from a reserved pool. + /// Addresses lists the network addresses that have been bound to the + /// Gateway. + /// + /// This list may differ from the addresses provided in the spec under some + /// conditions: + /// + /// * no addresses are specified, all addresses are dynamically assigned + /// * a combination of specified and dynamic addresses are assigned + /// * a specified address was unusable (e.g. already in use) pub addresses: Option>, /// Conditions describe the current conditions of the Gateway. /// - /// Implementations should prefer to express Gateway conditions using the - /// `GatewayConditionType` and `GatewayConditionReason` constants so that - /// operators and tools can converge on a common vocabulary to describe - /// Gateway state. + /// Implementations should prefer to express Gateway conditions + /// using the `GatewayConditionType` and `GatewayConditionReason` + /// constants so that operators and tools can converge on a common + /// vocabulary to describe Gateway state. /// /// Known condition types are: /// - /// * "Scheduled" + /// * "Accepted" + /// * "Programmed" /// * "Ready" pub conditions: Option>, - /// Routes is a list of routes bound to the Gateway. + /// Listeners provide status for each unique listener port defined in the Spec. pub listeners: Option>, } +/// GatewayInfrastructure defines infrastructure level attributes about a Gateway instance. +#[derive( + Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, +)] +pub struct GatewayInfrastructure { + /// Labels that SHOULD be applied to any resources created in response to this Gateway. + /// + /// For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. + /// For other implementations, this refers to any relevant (implementation specific) "labels" concepts. + /// + /// An implementation may chose to add additional implementation-specific labels as they see fit. + /// + /// Support: Extended + pub labels: Option>, + + /// Annotations that SHOULD be applied to any resources created in response to this Gateway. + /// + /// For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. + /// For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. + /// + /// An implementation may chose to add additional implementation-specific annotations as they see fit. + /// + /// Support: Extended + pub annotations: Option>, +} + /// GatewayConditionType is a type of condition associated with a /// Gateway. This type should be used with the GatewayStatus.Conditions /// field. @@ -390,6 +505,21 @@ pub struct ListenerStatus { /// AttachedRoutes represents the total number of Routes that have been /// successfully attached to this Listener. + /// + /// Successful attachment of a Route to a Listener is based solely on the + /// combination of the AllowedRoutes field on the corresponding Listener + /// and the Route's ParentRefs field. A Route is successfully attached to + /// a Listener when it is selected by the Listener's AllowedRoutes field + /// AND the Route has a valid ParentRef selecting the whole Gateway + /// resource or a specific Listener as a parent resource (more detail on + /// attachment semantics can be found in the documentation on the various + /// Route kinds ParentRefs fields). Listener or Route status does not impact + /// successful attachment, i.e. the AttachedRoutes field count MUST be set + /// for Listeners with condition Accepted: false and MUST count successfully + /// attached Routes that may themselves have Accepted: false conditions. + /// + /// Uses for this field include troubleshooting Route attachment and + /// measuring blast radius/impact of changes to a Listener. pub attached_routes: u16, /// Conditions describe the current condition of this listener. diff --git a/src/gatewayclass.rs b/src/gatewayclass.rs index a17d43e..af8ee06 100644 --- a/src/gatewayclass.rs +++ b/src/gatewayclass.rs @@ -1,29 +1,29 @@ use crate::*; use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1; -// GatewayClass describes a class of Gateways available to the user for creating -// Gateway resources. -// -// It is recommended that this resource be used as a template for Gateways. This -// means that a Gateway is based on the state of the GatewayClass at the time it -// was created and changes to the GatewayClass or associated parameters are not -// propagated down to existing Gateways. This recommendation is intended to -// limit the blast radius of changes to GatewayClass or associated parameters. -// If implementations choose to propagate GatewayClass changes to existing -// Gateways, that MUST be clearly documented by the implementation. -// -// Whenever one or more Gateways are using a GatewayClass, implementations MUST -// add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the -// associated GatewayClass. This ensures that a GatewayClass associated with a -// Gateway is not deleted while in use. -// -// GatewayClass is a Cluster level resource. +/// GatewayClass describes a class of Gateways available to the user for creating +/// Gateway resources. +/// +/// It is recommended that this resource be used as a template for Gateways. This +/// means that a Gateway is based on the state of the GatewayClass at the time it +/// was created and changes to the GatewayClass or associated parameters are not +/// propagated down to existing Gateways. This recommendation is intended to +/// limit the blast radius of changes to GatewayClass or associated parameters. +/// If implementations choose to propagate GatewayClass changes to existing +/// Gateways, that MUST be clearly documented by the implementation. +/// +/// Whenever one or more Gateways are using a GatewayClass, implementations SHOULD +/// add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the +/// associated GatewayClass. This ensures that a GatewayClass associated with a +/// Gateway is not deleted while in use. +/// +/// GatewayClass is a Cluster level resource. #[derive( Clone, Debug, kube::CustomResource, serde::Deserialize, serde::Serialize, schemars::JsonSchema, )] #[kube( group = "gateway.networking.k8s.io", - version = "v1beta1", + version = "v1", kind = "GatewayClass", status = "GatewayClassStatus" )] @@ -35,6 +35,8 @@ pub struct GatewayClassSpec { /// Example: "example.net/gateway-controller". /// /// This field is not mutable and cannot be empty. + /// + /// Support: Core pub controller_name: GatewayController, /// ParametersRef is a reference to a resource that contains the @@ -49,7 +51,7 @@ pub struct GatewayClassSpec { /// If the referent cannot be found, the GatewayClass's "InvalidParameters" /// status condition will be true. /// - /// Support: Custom + /// Support: Implementation-specific pub paramters_ref: Option, /// Description helps describe a GatewayClass with more details. @@ -96,4 +98,12 @@ pub struct GatewayClassStatus { /// Controllers should prefer to publish conditions using values of /// GatewayClassConditionType for the type of each Condition. pub conditions: Option>, + + /// SupportedFeatures is the set of features the GatewayClass support. + /// It MUST be sorted in ascending alphabetical order. + pub supported_features: Option>, } + +/// SupportedFeature is used to describe distinct features that are covered by +/// conformance tests. +pub type SupportedFeature = String; diff --git a/src/httproute.rs b/src/httproute.rs index f2f36bd..d4bd44c 100644 --- a/src/httproute.rs +++ b/src/httproute.rs @@ -15,7 +15,7 @@ use crate::*; )] #[kube( group = "gateway.networking.k8s.io", - version = "v1beta1", + version = "v1", kind = "HTTPRoute", struct = "HttpRoute", status = "HttpRouteStatus", @@ -26,9 +26,14 @@ pub struct HttpRouteSpec { #[serde(flatten)] pub inner: CommonRouteSpec, - /// Hostnames defines a set of hostname that should match against the HTTP - /// Host header to select a HTTPRoute to process the request. This matches - /// the RFC 1123 definition of a hostname with 2 notable exceptions: + /// Hostnames defines a set of hostnames that should match against the HTTP Host + /// header to select a HTTPRoute used to process the request. Implementations + /// MUST ignore any port value specified in the HTTP Host header while + /// performing a match and (absent of any applicable header modification + /// configuration) MUST forward this header unmodified to the backend. + /// + /// Valid values for Hostnames are determined by RFC 1123 definition of a + /// hostname with 2 notable exceptions: /// /// 1. IPs are not allowed. /// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard @@ -44,8 +49,13 @@ pub struct HttpRouteSpec { /// * A Listener with `*.example.com` as the hostname matches HTTPRoutes /// that have either not specified any hostnames or have specified at least /// one hostname that matches the Listener hostname. For example, - /// `test.example.com` and `*.example.com` would both match. On the other - /// hand, `example.com` and `test.example.net` would not match. + /// `*.example.com`, `test.example.com`, and `foo.test.example.com` would + /// all match. On the other hand, `example.com` and `test.example.net` would + /// not match. + /// + /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + /// as a suffix match. That means that a match for `*.example.com` would match + /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. /// /// If both the Listener and HTTPRoute have specified hostnames, any /// HTTPRoute hostnames that do not match the Listener hostname MUST be @@ -58,6 +68,16 @@ pub struct HttpRouteSpec { /// implementation must raise an 'Accepted' Condition with a status of /// `False` in the corresponding RouteParentStatus. /// + /// In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + /// overlapping wildcard matching and exact matching hostnames), precedence must + /// be given to rules from the HTTPRoute with the largest number of: + /// + /// * Characters in a matching non-wildcard hostname. + /// * Characters in a matching hostname. + /// + /// If ties exist across multiple Routes, the matching precedence rules for + /// HTTPRouteMatches takes over. + /// /// Support: Core pub hostnames: Option>, @@ -104,15 +124,17 @@ pub struct HttpRouteRule { /// HTTP request. /// /// Proxy or Load Balancer routing configuration generated from HTTPRoutes - /// MUST prioritize rules based on the following criteria, continuing on - /// ties. Precedence must be given to the the Rule with the largest number - /// of: + /// MUST prioritize matches based on the following criteria, continuing on + /// ties. Across all rules specified on applicable Routes, precedence must be + /// given to the match having: /// - /// * Characters in a matching non-wildcard hostname. - /// * Characters in a matching hostname. - /// * Characters in a matching path. - /// * Header matches. - /// * Query param matches. + /// * "Exact" path match. + /// * "Prefix" path match with largest number of characters. + /// * Method match. + /// * Largest number of header matches. + /// * Largest number of query param matches. + /// + /// Note: The precedence of RegularExpression path matches are implementation-specific. /// /// If ties still exist across multiple Routes, matching precedence MUST be /// determined in order of the following criteria, continuing on ties: @@ -121,30 +143,37 @@ pub struct HttpRouteRule { /// * The Route appearing first in alphabetical order by /// "{namespace}/{name}". /// - /// If ties still exist within the Route that has been given precedence, - /// matching precedence MUST be granted to the first matching rule meeting - /// the above criteria. + /// If ties still exist within an HTTPRoute, matching precedence MUST be granted + /// to the FIRST matching rule (in list order) with a match meeting the above + /// criteria. /// /// When no rules matching a request have been successfully attached to the /// parent a request is coming from, a HTTP 404 status code MUST be returned. pub matches: Option>, - /// Filters define the filters that are applied to requests that match this - /// rule. + /// Filters define the filters that are applied to requests that match + /// this rule. /// /// The effects of ordering of multiple behaviors are currently unspecified. /// This can change in the future based on feedback during the alpha stage. /// - /// Conformance-levels at this level are defined based on the type of - /// filter: + /// Conformance-levels at this level are defined based on the type of filter: /// /// - ALL core filters MUST be supported by all implementations. /// - Implementers are encouraged to support extended filters. /// - Implementation-specific custom filters have no API guarantees across /// implementations. /// - /// Specifying a core filter multiple times has unspecified or custom - /// conformance. + /// Specifying the same filter multiple times is not supported unless explicitly + /// indicated in the filter. + /// + /// All filters are expected to be compatible with each other except for the + /// URLRewrite and RequestRedirect filters, which may not be combined. If an + /// implementation can not support other combinations of filters, they must clearly + /// document that limitation. In cases where incompatible or unsupported + /// filters are specified and cause the `Accepted` condition to be set to status + /// `False`, implementations may use the `IncompatibleFilters` reason to specify + /// this configuration error. /// /// Support: Core pub filters: Option>, @@ -152,30 +181,80 @@ pub struct HttpRouteRule { /// BackendRefs defines the backend(s) where matching requests should be /// sent. /// - /// A 500 status code MUST be returned if there are no BackendRefs or - /// filters specified that would result in a response being sent. + /// Failure behavior here depends on how many BackendRefs are specified and + /// how many are invalid. /// - /// A BackendRef is considered invalid when it refers to: + /// If *all* entries in BackendRefs are invalid, and there are also no filters + /// specified in this route rule, *all* traffic which matches this rule MUST + /// receive a 500 status code. /// - /// * an unknown or unsupported kind of resource - /// * a resource that does not exist - /// * a resource in another namespace when the reference has not been - /// explicitly allowed by a ReferencePolicy (or equivalent concept). + /// See the HTTPBackendRef definition for the rules about what makes a single + /// HTTPBackendRef invalid. /// - /// When a BackendRef is invalid, 500 status codes MUST be returned for + /// When a HTTPBackendRef is invalid, 500 status codes MUST be returned for /// requests that would have otherwise been routed to an invalid backend. If /// multiple backends are specified, and some are invalid, the proportion of /// requests that would otherwise have been routed to an invalid backend /// MUST receive a 500 status code. /// - /// When a BackendRef refers to a Service that has no ready endpoints, it is - /// recommended to return a 503 status code. + /// For example, if two backends are specified with equal weights, and one is + /// invalid, 50 percent of traffic must receive a 500. Implementations may + /// choose how that 50 percent is determined. /// /// Support: Core for Kubernetes Service - /// Support: Custom for any other resource + /// + /// Support: Extended for Kubernetes ServiceImport + /// + /// Support: Implementation-specific for any other resource /// /// Support for weight: Core pub backend_refs: Option>, + + // Timeouts defines the timeouts that can be configured for an HTTP request. + // + // Support: Extended + pub timeouts: Option, +} + +/// HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute. +/// Timeout values are represented with Gateway API Duration formatting. +/// Specifying a zero value such as "0s" is interpreted as no timeout. +#[derive( + Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, +)] +#[serde(rename_all = "camelCase")] +pub struct HttpRouteTimeouts { + /// Request specifies the maximum duration for a gateway to respond to an HTTP request. + /// If the gateway has not been able to respond before this deadline is met, the gateway + /// MUST return a timeout error. + /// + /// For example, setting the `rules.timeouts.request` field to the value `10s` in an + /// `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds + /// to complete. + /// + /// This timeout is intended to cover as close to the whole request-response transaction + /// as possible although an implementation MAY choose to start the timeout after the entire + /// request stream has been received instead of immediately after the transaction is + /// initiated by the client. + /// + /// When this field is unspecified, request timeout behavior is implementation-specific. + /// + /// Support: Extended + pub request: Option, + + /// BackendRequest specifies a timeout for an individual request from the gateway + /// to a backend. This covers the time from when the request first starts being + /// sent from the gateway to when the full response has been received from the backend. + /// + /// An entire client HTTP transaction with a gateway, covered by the Request timeout, + /// may result in more than one call from the gateway to the destination backend, + /// for example, if automatic retries are supported. + /// + /// Because the Request timeout encompasses the BackendRequest timeout, the value of + /// BackendRequest must be <= the value of Request timeout. + /// + /// Support: Extended + pub backend_request: Option, } /// HTTPRouteMatch defines the predicate used to match requests to a given @@ -210,10 +289,11 @@ pub struct HttpRouteMatch { /// QueryParams specifies HTTP query parameter matchers. Multiple match /// values are ANDed together, meaning, a request must match all the /// specified query parameters to select the route. + /// + /// Support: Extended pub query_params: Option>, /// Method specifies HTTP method matcher. - /// /// When specified, this route will be matched only if the request has the /// specified method. /// @@ -318,8 +398,8 @@ pub type HttpMethod = String; /// HTTPRouteFilter defines processing steps that must be completed during the /// request or response lifecycle. HTTPRouteFilters are meant as an extension -/// point to express processing that may be done in Gateway implementations. -/// Some examples include request or response modification, implementing +/// point to express processing that may be done in Gateway implementations. Some +/// examples include request or response modification, implementing /// authentication strategies, rate-limiting, and traffic shaping. API /// guarantee/conformance is defined based on the type of the filter. /// @@ -334,7 +414,8 @@ pub type HttpMethod = String; /// "Support: Extended" in this package, e.g. "RequestMirror". Implementers /// are encouraged to support extended filters. /// -/// - Custom: Filters that are defined and supported by specific vendors. +/// - Implementation-specific: Filters that are defined and supported by +/// specific vendors. /// In the future, filters showing convergence in behavior across multiple /// implementations will be considered for inclusion in extended or core /// conformance levels. Filter-specific configuration for such filters @@ -374,6 +455,10 @@ pub enum HttpRouteFilter { /// Requests are sent to the specified destination, but responses from /// that destination are ignored. /// + /// This filter can be used multiple times within the same rule. Note that + /// not all implementations will be able to support mirroring to multiple + /// backends. + /// /// Support: Extended #[serde(rename_all = "camelCase")] RequestMirror { @@ -400,6 +485,8 @@ pub enum HttpRouteFilter { /// "networking.example.net"). ExtensionRef MUST NOT be used for core and /// extended filters. /// + /// This filter can be used multiple times within the same rule. + /// /// Support: Implementation-specific #[serde(rename_all = "camelCase")] ExtensionRef { extension_ref: LocalObjectReference }, @@ -501,7 +588,32 @@ pub enum HttpPathModifier { /// ReplacePrefixMatch specifies the value with which to replace the prefix /// match of a request during a rewrite or redirect. For example, a request - /// to "/foo/bar" with a prefix match of "/foo" would be modified to "/bar". + /// to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + /// of "/xyz" would be modified to "/xyz/bar". + /// + /// Note that this matches the behavior of the PathPrefix match type. This + /// matches full path elements. A path element refers to the list of labels + /// in the path split by the `/` separator. When specified, a trailing `/` is + /// ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + /// match the prefix `/abc`, but the path `/abcd` would not. + /// + /// ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + /// Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + /// the implementation setting the Accepted Condition for the Route to `status: False`. + /// + /// Request Path | Prefix Match | Replace Prefix | Modified Path + /// -------------|--------------|----------------|---------- + /// /foo/bar | /foo | /xyz | /xyz/bar + /// /foo/bar | /foo | /xyz/ | /xyz/bar + /// /foo/bar | /foo/ | /xyz | /xyz/bar + /// /foo/bar | /foo/ | /xyz/ | /xyz/bar + /// /foo | /foo | /xyz | /xyz + /// /foo/ | /foo | /xyz | /xyz/ + /// /foo/bar | /foo | | /bar + /// /foo/ | /foo | | / + /// /foo | /foo | | / + /// /foo/ | /foo | / | / + /// /foo | /foo | / | / #[serde(rename_all = "camelCase")] ReplacePrefixMatch { replace_prefix_match: String }, } @@ -517,13 +629,15 @@ pub struct HttpRequestRedirectFilter { /// header in the response. /// When empty, the scheme of the request is used. /// + /// Scheme redirects can affect the port of the redirect, for more information, + /// refer to the documentation for the port field of this filter. + /// /// Support: Extended pub scheme: Option, /// Hostname is the hostname to be used in the value of the `Location` /// header in the response. - /// - /// When empty, the hostname of the request is used. + /// When empty, the hostname in the `Host` header of the request is used. /// /// Support: Core pub hostname: Option, @@ -537,7 +651,24 @@ pub struct HttpRequestRedirectFilter { /// Port is the port to be used in the value of the `Location` /// header in the response. - /// When empty, port (if specified) of the request is used. + /// + /// If no port is specified, the redirect port MUST be derived using the + /// following rules: + /// + /// * If redirect scheme is not-empty, the redirect port MUST be the well-known + /// port associated with the redirect scheme. Specifically "http" to port 80 + /// and "https" to port 443. If the redirect scheme does not have a + /// well-known port, the listener port of the Gateway SHOULD be used. + /// * If redirect scheme is empty, the redirect port MUST be the Gateway + /// Listener port. + /// + /// Implementations SHOULD NOT add the port number in the 'Location' + /// header in the following cases: + /// + /// * A Location header that will use HTTP (whether that is determined via + /// the Listener protocol or the Scheme field) _and_ use port 80. + /// * A Location header that will use HTTPS (whether that is determined via + /// the Listener protocol or the Scheme field) _and_ use port 443. /// /// Support: Extended pub port: Option, @@ -578,13 +709,17 @@ pub struct HttpUrlRewriteFilter { pub struct HttpRequestMirrorFilter { /// BackendRef references a resource where mirrored requests are sent. /// + /// Mirrored requests must be sent only to a single destination endpoint + /// within this BackendRef, irrespective of how many endpoints are present + /// within this BackendRef. + /// /// If the referent cannot be found, this BackendRef is invalid and must be /// dropped from the Gateway. The controller must ensure the "ResolvedRefs" /// condition on the Route status is set to `status: False` and not configure /// this backend in the underlying implementation. /// /// If there is a cross-namespace reference to an *existing* object - /// that is not allowed by a ReferencePolicy, the controller must ensure the + /// that is not allowed by a ReferenceGrant, the controller must ensure the /// "ResolvedRefs" condition on the Route is set to `status: False`, /// with the "RefNotPermitted" reason and not configure this backend in the /// underlying implementation. @@ -593,11 +728,31 @@ pub struct HttpRequestMirrorFilter { /// should be used to provide more detail about the problem. /// /// Support: Extended for Kubernetes Service - /// Support: Custom for any other resource + /// + /// Support: Implementation-specific for any other resource pub backend_ref: BackendObjectReference, } -/// HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. +/// HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. +/// +/// Note that when a namespace different than the local namespace is specified, a +/// ReferenceGrant object is required in the referent namespace to allow that +/// namespace's owner to accept the reference. See the ReferenceGrant +/// documentation for details. +/// +/// When the BackendRef points to a Kubernetes Service, implementations SHOULD +/// honor the appProtocol field if it is set for the target Service Port. +/// +/// Implementations supporting appProtocol SHOULD recognize the Kubernetes +/// Standard Application Protocols defined in KEP-3726. +/// +/// If a Service appProtocol isn't specified, an implementation MAY infer the +/// backend protocol through its own means. Implementations MAY infer the +/// protocol from the Route type referring to the backend Service. +/// +/// If a Route is not able to send traffic to the backend using the specified +/// protocol then the backend is considered invalid. Implementations MUST set the +/// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. #[derive( Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, )] @@ -605,29 +760,52 @@ pub struct HttpRequestMirrorFilter { pub struct HttpBackendRef { /// BackendRef is a reference to a backend to forward matched requests to. /// - /// If the referent cannot be found, this HTTPBackendRef is invalid and must - /// be dropped from the Gateway. The controller must ensure the - /// "ResolvedRefs" condition on the Route is set to `status: False` and not - /// configure this backend in the underlying implementation. + /// A BackendRef can be invalid for the following reasons. In all cases, the + /// implementation MUST ensure the `ResolvedRefs` Condition on the Route + /// is set to `status: False`, with a Reason and Message that indicate + /// what is the cause of the error. /// - /// If there is a cross-namespace reference to an *existing* object - /// that is not covered by a ReferencePolicy, the controller must ensure the - /// "ResolvedRefs" condition on the Route is set to `status: False`, - /// with the "RefNotPermitted" reason and not configure this backend in the - /// underlying implementation. + /// A BackendRef is invalid if: /// - /// In either error case, the Message of the `ResolvedRefs` Condition - /// should be used to provide more detail about the problem. + /// * It refers to an unknown or unsupported kind of resource. In this + /// case, the Reason must be set to `InvalidKind` and Message of the + /// Condition must explain which kind of resource is unknown or unsupported. + /// + /// * It refers to a resource that does not exist. In this case, the Reason must + /// be set to `BackendNotFound` and the Message of the Condition must explain + /// which resource does not exist. + /// + /// * It refers a resource in another namespace when the reference has not been + /// explicitly allowed by a ReferenceGrant (or equivalent concept). In this + /// case, the Reason must be set to `RefNotPermitted` and the Message of the + /// Condition must explain which cross-namespace reference is not allowed. + /// + /// * It refers to a Kubernetes Service that has an incompatible appProtocol + /// for the given Route type + /// + /// * The BackendTLSPolicy object is installed in the cluster, a BackendTLSPolicy + /// is present that refers to the Service, and the implementation is unable + /// to meet the requirement. At the time of writing, BackendTLSPolicy is + /// experimental, but once it becomes standard, this will become a MUST + /// requirement. + /// + /// Support: Core for Kubernetes Service + /// + /// Support: Implementation-specific for any other resource + /// + /// Support for weight: Core + /// + /// Support for Kubernetes Service appProtocol: Extended /// - /// Support: Custom + /// Support for BackendTLSPolicy: Experimental and ImplementationSpecific #[serde(flatten)] pub backend_ref: Option, /// Filters defined at this level should be executed if and only if the /// request is being forwarded to the backend defined here. /// - /// Support: Custom (For broader support of filters, use the Filters field - /// in HTTPRouteRule.) + /// Support: Implementation-specific (For broader support of filters, use the + /// Filters field in HTTPRouteRule.) pub filters: Option>, } diff --git a/src/lib.rs b/src/lib.rs index 80d3d8b..775baf4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ // TODO(ver): We should deny missing_docs, but this doesn't play with // CustomResource derivations. +pub mod duration; mod gateway; mod gatewayclass; mod httproute; diff --git a/src/shared.rs b/src/shared.rs index 555daf3..c94cec3 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -3,9 +3,14 @@ use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1; use crate::BackendObjectReference; /// ParentReference identifies an API object (usually a Gateway) that can be considered -/// a parent of this resource (usually a route). The only kind of parent resource -/// with "Core" support is Gateway. This API may be extended in the future to -/// support additional kinds of parent resources, such as HTTPRoute. +/// a parent of this resource (usually a route). There are two kinds of parent resources +/// with "Core" support: +/// +/// * Gateway (Gateway conformance profile) +/// * Service (Mesh conformance profile, experimental, ClusterIP Services only) +/// +/// This API may be extended in the future to support additional kinds of parent +/// resources. /// /// The API object must be valid in the cluster; the Group and Kind must /// be registered in the cluster for this reference to be valid. @@ -15,18 +20,41 @@ use crate::BackendObjectReference; #[serde(rename_all = "camelCase")] pub struct ParentReference { /// Group is the group of the referent. + /// When unspecified, "gateway.networking.k8s.io" is inferred. + /// To set the core API group (such as for a "Service" kind referent), + /// Group must be explicitly set to "" (empty string). /// /// Support: Core pub group: Option, /// Kind is kind of the referent. /// - /// Support: Core (Gateway) - /// Support: Custom (Other Resources) + /// There are two kinds of parent resources with "Core" support: + /// + /// * Gateway (Gateway conformance profile) + /// * Service (Mesh conformance profile, experimental, ClusterIP Services only) + /// + /// Support for other resources is Implementation-Specific. pub kind: Option, - /// Namespace is the namespace of the referent. When unspecified (or empty - /// string), this refers to the local namespace of the Route. + /// Namespace is the namespace of the referent. When unspecified, this refers + /// to the local namespace of the Route. + /// + /// Note that there are specific rules for ParentRefs which cross namespace + /// boundaries. Cross-namespace references are only valid if they are explicitly + /// allowed by something in the namespace they are referring to. For example: + /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a + /// generic way to enable any other kind of cross-namespace reference. + /// + /// ParentRefs from a Route to a Service in the same namespace are "producer" + /// routes, which apply default routing rules to inbound connections from + /// any namespace to the Service. + /// + /// ParentRefs from a Route to a Service in a different namespace are + /// "consumer" routes, and these routing rules are only applied to outbound + /// connections originating from the same namespace as the Route, for which + /// the intended destination of the connections are a Service targeted as a + /// ParentRef of the Route. /// /// Support: Core pub namespace: Option, @@ -42,33 +70,42 @@ pub struct ParentReference { /// * Gateway: Listener Name. When both Port (experimental) and SectionName /// are specified, the name and port of the selected listener must match /// both specified values. + /// * Service: Port Name. When both Port (experimental) and SectionName + /// are specified, the name and port of the selected listener must match + /// both specified values. Note that attaching Routes to Services as Parents + /// is part of experimental Mesh support and is not supported for any other + /// purpose. /// - /// Implementations MAY choose to support attaching Routes to other - /// resources. If that is the case, they MUST clearly document how - /// SectionName is interpreted. + /// Implementations MAY choose to support attaching Routes to other resources. + /// If that is the case, they MUST clearly document how SectionName is + /// interpreted. /// - /// When unspecified (empty string), this will reference the entire - /// resource. For the purpose of status, an attachment is considered - /// successful if at least one section in the parent resource accepts it. - /// For example, Gateway listeners can restrict which Routes can attach to - /// them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners - /// accept attachment from the referencing Route, the Route MUST be - /// considered successfully attached. If no Gateway listeners accept - /// attachment from this Route, the Route MUST be considered detached from - /// the Gateway. + /// When unspecified (empty string), this will reference the entire resource. + /// For the purpose of status, an attachment is considered successful if at + /// least one section in the parent resource accepts it. For example, Gateway + /// listeners can restrict which Routes can attach to them by Route kind, + /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + /// the referencing Route, the Route MUST be considered successfully + /// attached. If no Gateway listeners accept attachment from this Route, the + /// Route MUST be considered detached from the Gateway. /// /// Support: Core pub section_name: Option, /// Port is the network port this Route targets. It can be interpreted - /// differently based on the type of parent resource: + /// differently based on the type of parent resource. /// - /// * Gateway: All listeners listening on the specified port that also - /// support this kind of Route(and select this Route). It's not recommended - /// to set `Port` unless the networking behaviors specified in a Route must - /// apply to a specific port as opposed to a listener(s) whose port(s) may - /// be changed. When both Port and SectionName are specified, the name and - /// port of the selected listener must match both specified values. + /// When the parent resource is a Gateway, this targets all listeners + /// listening on the specified port that also support this kind of Route(and + /// select this Route). It's not recommended to set `Port` unless the + /// networking behaviors specified in a Route must apply to a specific port + /// as opposed to a listener(s) whose port(s) may be changed. When both Port + /// and SectionName are specified, the name and port of the selected listener + /// must match both specified values. + /// + /// When the parent resource is a Service, this targets a specific port in the + /// Service spec. When both Port (experimental) and SectionName are specified, + /// the name and port of the selected port must match both specified values. /// /// Implementations MAY choose to support other parent resources. /// Implementations supporting other types of parent resources MUST clearly @@ -79,8 +116,8 @@ pub struct ParentReference { /// listeners can restrict which Routes can attach to them by Route kind, /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment /// from the referencing Route, the Route MUST be considered successfully - /// attached. If no Gateway listeners accept attachment from this Route, the - /// Route MUST be considered detached from the Gateway. + /// attached. If no Gateway listeners accept attachment from this Route, + /// the Route MUST be considered detached from the Gateway. /// /// Support: Extended pub port: Option, @@ -93,25 +130,65 @@ pub struct ParentReference { )] #[serde(rename_all = "camelCase")] pub struct CommonRouteSpec { - /// ParentRefs references the resources (usually Gateways) that a Route - /// wants to be attached to. Note that the referenced parent resource needs - /// to allow this for the attachment to be complete. For Gateways, that - /// means the Gateway needs to allow attachment from Routes of this kind and - /// namespace. - /// - /// The only kind of parent resource with "Core" support is Gateway. This - /// API may be extended in the future to support additional kinds of parent - /// resources such as one of the route kinds. - /// - /// It is invalid to reference an identical parent more than once. It is - /// valid to reference multiple distinct sections within the same parent - /// resource, such as 2 Listeners within a Gateway. - /// - /// It is possible to separately reference multiple distinct objects that - /// may be collapsed by an implementation. For example, some implementations - /// may choose to merge compatible Gateway Listeners together. If that is - /// the case, the list of routes attached to those resources should also be + /// ParentRefs references the resources (usually Gateways) that a Route wants + /// to be attached to. Note that the referenced parent resource needs to + /// allow this for the attachment to be complete. For Gateways, that means + /// the Gateway needs to allow attachment from Routes of this kind and + /// namespace. For Services, that means the Service must either be in the same + /// namespace for a "producer" route, or the mesh implementation must support + /// and allow "consumer" routes for the referenced Service. ReferenceGrant is + /// not applicable for governing ParentRefs to Services - it is not possible to + /// create a "producer" route for a Service in a different namespace from the + /// Route. + /// + /// There are two kinds of parent resources with "Core" support: + /// + /// * Gateway (Gateway conformance profile) + /// * Service (Mesh conformance profile, experimental, ClusterIP Services only) + /// This API may be extended in the future to support additional kinds of parent + /// resources. + /// + /// ParentRefs must be _distinct_. This means either that: + /// + /// * They select different objects. If this is the case, then parentRef + /// entries are distinct. In terms of fields, this means that the + /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must + /// be unique across all parentRef entries in the Route. + /// * They do not select different objects, but for each optional field used, + /// each ParentRef that selects the same object must set the same set of + /// optional fields to different values. If one ParentRef sets a + /// combination of optional fields, all must set the same combination. + /// + /// Some examples: + /// + /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the + /// same object must also set `sectionName`. + /// * If one ParentRef sets `port`, all ParentRefs referencing the same + /// object must also set `port`. + /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs + /// referencing the same object must also set `sectionName` and `port`. + /// + /// It is possible to separately reference multiple distinct objects that may + /// be collapsed by an implementation. For example, some implementations may + /// choose to merge compatible Gateway Listeners together. If that is the + /// case, the list of routes attached to those resources should also be /// merged. + /// + /// Note that for ParentRefs that cross namespace boundaries, there are specific + /// rules. Cross-namespace references are only valid if they are explicitly + /// allowed by something in the namespace they are referring to. For example, + /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a + /// generic way to enable other kinds of cross-namespace reference. + /// + /// ParentRefs from a Route to a Service in the same namespace are "producer" + /// routes, which apply default routing rules to inbound connections from + /// any namespace to the Service. + /// + /// ParentRefs from a Route to a Service in a different namespace are + /// "consumer" routes, and these routing rules are only applied to outbound + /// connections originating from the same namespace as the Route, for which + /// the intended destination of the connections are a Service targeted as a + /// ParentRef of the Route. pub parent_refs: Option>, } @@ -121,9 +198,28 @@ pub type PortNumber = u16; /// BackendRef defines how a Route should forward a request to a Kubernetes /// resource. /// -/// Note that when a namespace is specified, a ReferencePolicy object is -/// required in the referent namespace to allow that namespace's owner to accept -/// the reference. See the ReferencePolicy documentation for details. +/// Note that when a namespace different than the local namespace is specified, a +/// ReferenceGrant object is required in the referent namespace to allow that +/// namespace's owner to accept the reference. See the ReferenceGrant +/// documentation for details. +/// +/// When the BackendRef points to a Kubernetes Service, implementations SHOULD +/// honor the appProtocol field if it is set for the target Service Port. +/// +/// Implementations supporting appProtocol SHOULD recognize the Kubernetes +/// Standard Application Protocols defined in KEP-3726. +/// +/// If a Service appProtocol isn't specified, an implementation MAY infer the +/// backend protocol through its own means. Implementations MAY infer the +/// protocol from the Route type referring to the backend Service. +/// +/// If a Route is not able to send traffic to the backend using the specified +/// protocol then the backend is considered invalid. Implementations MUST set the +/// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. +/// +/// Note that when the BackendTLSPolicy object is enabled by the implementation, +/// there are some extra rules about validity to consider here. See the fields +/// where this struct is used for more information about the exact behavior. #[derive( Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, )] @@ -135,9 +231,9 @@ pub struct BackendRef { /// implementation supports. Weight is not a percentage and the sum of /// weights does not need to equal 100. /// - /// If only one backend is specified and it has a weight greater than 0, - /// 100% of the traffic is forwarded to that backend. If weight is set to 0, - /// no traffic should be forwarded for this entry. If unspecified, weight + /// If only one backend is specified and it has a weight greater than 0, 100% + /// of the traffic is forwarded to that backend. If weight is set to 0, no + /// traffic should be forwarded for this entry. If unspecified, weight /// defaults to 1. /// /// Support for this field varies based on the context where used. @@ -179,25 +275,25 @@ pub struct RouteParentStatus { /// [names]: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names pub controller_name: GatewayController, - /// Conditions describes the status of the route with respect to the - /// Gateway. Note that the route's availability is also subject to the - /// Gateway's own status conditions and listener status. + /// Conditions describes the status of the route with respect to the Gateway. + /// Note that the route's availability is also subject to the Gateway's own + /// status conditions and listener status. /// /// If the Route's ParentRef specifies an existing Gateway that supports /// Routes of this kind AND that Gateway's controller has sufficient access, /// then that Gateway's controller MUST set the "Accepted" condition on the - /// Route, to indicate whether the route has been accepted or rejected by - /// the Gateway, and why. + /// Route, to indicate whether the route has been accepted or rejected by the + /// Gateway, and why. /// /// A Route MUST be considered "Accepted" if at least one of the Route's /// rules is implemented by the Gateway. /// - /// There are a number of cases where the "Accepted" condition may not be - /// set due to lack of controller visibility, that includes when: + /// There are a number of cases where the "Accepted" condition may not be set + /// due to lack of controller visibility, that includes when: /// /// * The Route refers to a non-existent parent. /// * The Route is of a type that the controller does not support. - /// * The Route is in a namespace the the controller does not have access to. + /// * The Route is in a namespace the controller does not have access to. pub conditions: Vec, } @@ -352,4 +448,17 @@ pub type AnnotationKey = String; pub type AnnotationValue = String; /// AddressType defines how a network address is represented as a text string. +/// This may take two possible forms: +/// +/// * A predefined CamelCase string identifier (currently limited to `IPAddress` or `Hostname`) +/// * A domain-prefixed string identifier (like `acme.io/CustomAddressType`) +/// +/// Values `IPAddress` and `Hostname` have Extended support. +/// +/// The `NamedAddress` value has been deprecated in favor of implementation +/// specific domain-prefixed strings. +/// +/// All other values, including domain-prefixed values have Implementation-specific support, +/// which are used in implementation-specific behaviors. Support for additional +/// predefined CamelCase identifiers may be added in future releases. pub type AddressType = String; From 69ffbb8e92bc75ee89ae991befc716bac0b370e7 Mon Sep 17 00:00:00 2001 From: juicyenc Date: Tue, 12 Dec 2023 04:17:30 +0800 Subject: [PATCH 08/13] fix ListenerStatus is not renamed to camelCase (#71) Signed-off-by: juicyenc --- src/gateway.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gateway.rs b/src/gateway.rs index 204cd31..8bd6db6 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -488,6 +488,7 @@ pub type GatewayConditionReason = String; /// ListenerStatus is the status associated with a Listener. #[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +#[serde(rename_all = "camelCase")] pub struct ListenerStatus { /// Name is the name of the Listener that this status corresponds to. pub name: SectionName, From a4be132c03c81a70a2c39c7c91fe06f280cfd799 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Mon, 11 Dec 2023 14:08:06 -0800 Subject: [PATCH 09/13] deps: Update to kube v0.87 and k8s-openapi v0.20 (#80) * deps: Update to kube v0.87 and k8s-openapi v0.20 This update features changes to the kube::CustomResource interface. * Test k8s 1.22 and 1.28 --- .github/workflows/integration.yml | 6 ++---- Cargo.toml | 10 +++++----- integration/Cargo.toml | 4 ++-- src/exp/tcproute.rs | 2 +- src/exp/tlsroute.rs | 2 +- src/exp/udproute.rs | 2 +- src/httproute.rs | 2 +- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 8528278..d74a554 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -25,10 +25,8 @@ jobs: strategy: matrix: k8s: - - v1.21 - - v1.25 - - v1.26 - - v1.27 + - v1.22 + - v1.28 timeout-minutes: 10 runs-on: ubuntu-latest env: diff --git a/Cargo.toml b/Cargo.toml index 5728b0d..bbdbc7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,18 +16,18 @@ default = [] experimental = [] [dependencies] -kube = { version = "0.85", default-features = false, features = ["derive"] } -k8s-openapi = { version = "0.19", features = ["schemars"] } +kube = { version = "0.87", default-features = false, features = ["derive"] } +k8s-openapi = { version = "0.20", features = ["schemars"] } schemars = { version = "0.8", features = ["derive"] } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" [dev-dependencies.k8s-openapi] -version = "0.19" +version = "0.20" default-features = false -features = ["v1_27"] +features = ["latest"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] -features = ["experimental", "k8s-openapi/v1_27"] +features = ["experimental", "k8s-openapi/latest"] diff --git a/integration/Cargo.toml b/integration/Cargo.toml index 4200ef3..ad98692 100644 --- a/integration/Cargo.toml +++ b/integration/Cargo.toml @@ -6,12 +6,12 @@ license = "Apache-2.0" publish = false [dev-dependencies] -k8s-openapi = { version = "0.19", features = ["v1_27"] } +k8s-openapi = { version = "0.20", features = ["latest"] } tokio = { version = "1", features = ["macros", "rt"] } tracing = "0.1" k8s-gateway-api = { path = ".." } [dev-dependencies.kube] -version = "0.85" +version = "0.87" default-features = false features = ["client", "openssl-tls", "runtime", "ws"] diff --git a/src/exp/tcproute.rs b/src/exp/tcproute.rs index 7abdded..f8ead10 100644 --- a/src/exp/tcproute.rs +++ b/src/exp/tcproute.rs @@ -10,7 +10,7 @@ use crate::*; group = "gateway.networking.k8s.io", version = "v1alpha2", kind = "TCPRoute", - struct = "TcpRoute", + root = "TcpRoute", status = "TcpRouteStatus", namespaced )] diff --git a/src/exp/tlsroute.rs b/src/exp/tlsroute.rs index 8b5829f..8a3de6d 100644 --- a/src/exp/tlsroute.rs +++ b/src/exp/tlsroute.rs @@ -13,7 +13,7 @@ use crate::*; group = "gateway.networking.k8s.io", version = "v1alpha2", kind = "TLSRoute", - struct = "TlsRoute", + root = "TlsRoute", status = "TlsRouteStatus", namespaced )] diff --git a/src/exp/udproute.rs b/src/exp/udproute.rs index b60657f..fde21ba 100644 --- a/src/exp/udproute.rs +++ b/src/exp/udproute.rs @@ -7,7 +7,7 @@ use crate::*; group = "gateway.networking.k8s.io", version = "v1alpha2", kind = "UDPRoute", - struct = "UdpRoute", + root = "UdpRoute", status = "UdpRouteStatus", namespaced )] diff --git a/src/httproute.rs b/src/httproute.rs index d4bd44c..720b875 100644 --- a/src/httproute.rs +++ b/src/httproute.rs @@ -17,7 +17,7 @@ use crate::*; group = "gateway.networking.k8s.io", version = "v1", kind = "HTTPRoute", - struct = "HttpRoute", + root = "HttpRoute", status = "HttpRouteStatus", namespaced )] From 75a76f6630213b2fcd4ec3b421dda9af09b40ad8 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Mon, 11 Dec 2023 14:42:41 -0800 Subject: [PATCH 10/13] v0.14.0 (#82) Updates dev to v42. --- .devcontainer/devcontainer.json | 4 ++-- .github/workflows/actions.yml | 4 ++-- .github/workflows/integration.yml | 4 ++-- .github/workflows/lint.yml | 2 +- .github/workflows/release.yml | 6 +++--- Cargo.toml | 4 ++-- README.md | 4 ++-- justfile | 4 ++-- src/httproute.rs | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ab1c732..1923053 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "k8s-gateway-api", - "image": "ghcr.io/linkerd/dev:v39", + "image": "ghcr.io/linkerd/dev:v42", "extensions": [ "DavidAnson.vscode-markdownlint", "kokakiwi.vscode-just", @@ -26,4 +26,4 @@ "type": "bind" } ] -} \ No newline at end of file +} diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 40c8fab..16eb80c 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -15,12 +15,12 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: linkerd/dev/actions/setup-tools@v39 + - uses: linkerd/dev/actions/setup-tools@v42 - run: just-dev lint-actions devcontainer-versions: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: linkerd/dev/actions/setup-tools@v39 + - uses: linkerd/dev/actions/setup-tools@v42 - run: just-dev check-action-images diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index d74a554..bb3d444 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -33,9 +33,9 @@ jobs: K8S_CHANNEL: ${{ matrix.k8s }} steps: # Install just* tooling - - uses: linkerd/dev/actions/setup-tools@v39 + - uses: linkerd/dev/actions/setup-tools@v42 # Configure the default Rust toolchain - - uses: linkerd/dev/actions/setup-rust@v39 + - uses: linkerd/dev/actions/setup-rust@v42 # Setup a cluster - run: curl --proto =https --tlsv1.3 -fLsSv "https://raw.githubusercontent.com/k3d-io/k3d/${K3D_VERSION}/install.sh" | bash - run: k3d --version diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a024328..7508d99 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: lint: timeout-minutes: 20 runs-on: ubuntu-latest - container: docker://ghcr.io/linkerd/dev:v39-rust + container: docker://ghcr.io/linkerd/dev:v42-rust steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - run: just fetch diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c8a040..6a81716 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: test: timeout-minutes: 5 runs-on: ubuntu-latest - container: docker://ghcr.io/linkerd/dev:v39-rust + container: docker://ghcr.io/linkerd/dev:v42-rust steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - run: just fetch @@ -55,7 +55,7 @@ jobs: contents: write timeout-minutes: 5 runs-on: ubuntu-latest - container: docker://ghcr.io/linkerd/dev:v39-rust + container: docker://ghcr.io/linkerd/dev:v42-rust steps: - if: needs.meta.outputs.publish uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 @@ -80,7 +80,7 @@ jobs: needs: [meta, release] timeout-minutes: 10 runs-on: ubuntu-latest - container: docker://ghcr.io/linkerd/dev:v39-rust + container: docker://ghcr.io/linkerd/dev:v42-rust steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - if: needs.meta.outputs.publish == '' diff --git a/Cargo.toml b/Cargo.toml index bbdbc7f..43f4d9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,11 @@ members = [".", "integration"] [package] name = "k8s-gateway-api" -version = "0.13.0" +version = "0.14.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/linkerd/k8s-gateway-api-rs" -rust-version = "1.60" +rust-version = "1.65" keywords = ["kubernetes", "gateway"] description = "Rust bindings for the Kubenetes Gateway API" diff --git a/README.md b/README.md index b77d750..8dfcd70 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ (Unofficial) Rust bindings for the [Kubernetes Gateway API][site]. -Based on [gateway-api-v0.5.0-rc1]. +Based on [gateway-api-v1.0.0]. [![Crates.io][crate-badge]][crate-url] [![Documentation][docs-badge]][docs-url] @@ -20,7 +20,7 @@ the *v1alpha2* types when the `experimental` feature is enabled. * Express validation constraints * Rustify/Linkify documentation -[gateway-api-v0.5.0-rc1]: https://github.com/kubernetes-sigs/gateway-api/tree/4f86f0bd65173b04dadb558f63fbbd53330736d2 +[gateway-api-v1.0.0]: https://github.com/kubernetes-sigs/gateway-api/tree/a0684982eddeb0360e215e0de322c3210ac49bb9 [site]: https://gateway-api.sigs.k8s.io/ [crate-badge]: https://img.shields.io/crates/v/k8s-gateway-api.svg [crate-url]: https://crates.io/crates/k8s-gateway-api diff --git a/justfile b/justfile index f3b8a70..b3fcce6 100644 --- a/justfile +++ b/justfile @@ -23,7 +23,7 @@ deny: cargo-deny --all-features check docs: - just-cargo doc --frozen --no-deps --features=k8s-openapi/v1_25 + just-cargo doc --frozen --no-deps --features=k8s-openapi/latest test-build *flags: just-cargo test-build --frozen {{ flags }} @@ -32,7 +32,7 @@ test *flags: just-cargo test --frozen {{ flags }} publish *flags: - cargo publish --features=k8s-openapi/v1_25 {{ flags }} + cargo publish --features=k8s-openapi/latest {{ flags }} action-lint: just-dev lint-actions diff --git a/src/httproute.rs b/src/httproute.rs index 720b875..973367d 100644 --- a/src/httproute.rs +++ b/src/httproute.rs @@ -609,9 +609,9 @@ pub enum HttpPathModifier { /// /foo/bar | /foo/ | /xyz/ | /xyz/bar /// /foo | /foo | /xyz | /xyz /// /foo/ | /foo | /xyz | /xyz/ - /// /foo/bar | /foo | | /bar - /// /foo/ | /foo | | / - /// /foo | /foo | | / + /// /foo/bar | /foo | _empty string_ | /bar + /// /foo/ | /foo | _empty string_ | / + /// /foo | /foo | _empty string_ | / /// /foo/ | /foo | / | / /// /foo | /foo | / | / #[serde(rename_all = "camelCase")] From 9253b82147bb5d043626fd24964aced18964411e Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Wed, 3 Jan 2024 10:17:49 -0800 Subject: [PATCH 11/13] Revert "Update gateway types to v1 (#79)" (#84) * Revert "Update gateway types to v1 (#79)" This reverts commit ec91fdee8346dea851eb8e14eceeff39897ab211. * Revert README change --- .github/workflows/integration.yml | 2 +- .github/workflows/lint.yml | 2 +- Cargo.toml | 1 - README.md | 4 +- integration/tests/gateway.rs | 1 - integration/tests/httproute.rs | 2 - src/duration.rs | 354 ------------------------------ src/gateway.rs | 304 ++++++++----------------- src/gatewayclass.rs | 48 ++-- src/httproute.rs | 296 +++++-------------------- src/lib.rs | 1 - src/shared.rs | 229 +++++-------------- 12 files changed, 229 insertions(+), 1015 deletions(-) delete mode 100644 src/duration.rs diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index bb3d444..9e38f8a 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 with: repository: kubernetes-sigs/gateway-api - ref: a0684982eddeb0360e215e0de322c3210ac49bb9 # v1.0.0 + ref: 4f86f0bd65173b04dadb558f63fbbd53330736d2 # 0.5.0-rc1 path: gateway-api - run: kubectl apply -k gateway-api/config/crd/experimental/ - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7508d99..6750417 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ env: jobs: lint: - timeout-minutes: 20 + timeout-minutes: 5 runs-on: ubuntu-latest container: docker://ghcr.io/linkerd/dev:v42-rust steps: diff --git a/Cargo.toml b/Cargo.toml index 43f4d9f..8324df3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ k8s-openapi = { version = "0.20", features = ["schemars"] } schemars = { version = "0.8", features = ["derive"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -thiserror = "1" [dev-dependencies.k8s-openapi] version = "0.20" diff --git a/README.md b/README.md index 8dfcd70..b77d750 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ (Unofficial) Rust bindings for the [Kubernetes Gateway API][site]. -Based on [gateway-api-v1.0.0]. +Based on [gateway-api-v0.5.0-rc1]. [![Crates.io][crate-badge]][crate-url] [![Documentation][docs-badge]][docs-url] @@ -20,7 +20,7 @@ the *v1alpha2* types when the `experimental` feature is enabled. * Express validation constraints * Rustify/Linkify documentation -[gateway-api-v1.0.0]: https://github.com/kubernetes-sigs/gateway-api/tree/a0684982eddeb0360e215e0de322c3210ac49bb9 +[gateway-api-v0.5.0-rc1]: https://github.com/kubernetes-sigs/gateway-api/tree/4f86f0bd65173b04dadb558f63fbbd53330736d2 [site]: https://gateway-api.sigs.k8s.io/ [crate-badge]: https://img.shields.io/crates/v/k8s-gateway-api.svg [crate-url]: https://crates.io/crates/k8s-gateway-api diff --git a/integration/tests/gateway.rs b/integration/tests/gateway.rs index 418f60a..672cfed 100644 --- a/integration/tests/gateway.rs +++ b/integration/tests/gateway.rs @@ -19,7 +19,6 @@ async fn round_trip() { }, spec: GatewaySpec { gateway_class_name: "acme-lb".to_string(), - infrastructure: None, listeners: vec![Listener { protocol: "HTTPS".to_string(), port: 443, diff --git a/integration/tests/httproute.rs b/integration/tests/httproute.rs index 93a3ee1..8742691 100644 --- a/integration/tests/httproute.rs +++ b/integration/tests/httproute.rs @@ -41,7 +41,6 @@ async fn round_trip() { ]), filters: None, matches: None, - timeouts: None, }, HttpRouteRule { matches: Some(vec![HttpRouteMatch { @@ -59,7 +58,6 @@ async fn round_trip() { filters: None, }]), filters: None, - timeouts: None, }, ]), ..HttpRouteSpec::default() diff --git a/src/duration.rs b/src/duration.rs deleted file mode 100644 index 969ec9e..0000000 --- a/src/duration.rs +++ /dev/null @@ -1,354 +0,0 @@ -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use std::{fmt, str::FromStr, time::Duration}; - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct K8sDuration { - duration: Duration, - is_negative: bool, -} - -#[derive(Debug, thiserror::Error, Eq, PartialEq)] -#[non_exhaustive] -pub enum ParseError { - #[error("invalid unit: {}", EXPECTED_UNITS)] - InvalidUnit, - - #[error("missing a unit: {}", EXPECTED_UNITS)] - NoUnit, - - #[error("invalid floating-point number: {}", .0)] - NotANumber(#[from] std::num::ParseFloatError), -} - -const EXPECTED_UNITS: &str = "expected one of 'ns', 'us', '\u{00b5}s', 'ms', 's', 'm', or 'h'"; - -impl From for K8sDuration { - fn from(duration: Duration) -> Self { - Self { - duration, - is_negative: false, - } - } -} - -impl From for Duration { - fn from(K8sDuration { duration, .. }: K8sDuration) -> Self { - duration - } -} - -impl K8sDuration { - #[inline] - #[must_use] - pub fn is_negative(&self) -> bool { - self.is_negative - } -} - -impl fmt::Debug for K8sDuration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use std::fmt::Write; - if self.is_negative { - f.write_char('-')?; - } - fmt::Debug::fmt(&self.duration, f) - } -} - -impl fmt::Display for K8sDuration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use std::fmt::Write; - if self.is_negative { - f.write_char('-')?; - } - fmt::Debug::fmt(&self.duration, f) - } -} - -impl FromStr for K8sDuration { - type Err = ParseError; - - fn from_str(mut s: &str) -> Result { - // implements the same format as - // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/time/format.go;l=1589 - - fn duration_from_units(val: f64, unit: &str) -> Result { - const MINUTE: Duration = Duration::from_secs(60); - // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/time/format.go;l=1573 - let base = match unit { - "ns" => Duration::from_nanos(1), - // U+00B5 is the "micro sign" while U+03BC is "Greek letter mu" - "us" | "\u{00b5}s" | "\u{03bc}s" => Duration::from_micros(1), - "ms" => Duration::from_millis(1), - "s" => Duration::from_secs(1), - "m" => MINUTE, - "h" => MINUTE * 60, - _ => return Err(ParseError::InvalidUnit), - }; - Ok(base.mul_f64(val)) - } - - // Go durations are signed. Rust durations aren't. So we need to ignore - // this for now. - let is_negative = s.starts_with('-'); - s = s.trim_start_matches('+').trim_start_matches('-'); - - let mut total = Duration::from_secs(0); - while !s.is_empty() { - if let Some(unit_start) = s.find(|c: char| c.is_alphabetic()) { - let (val, rest) = s.split_at(unit_start); - let val = val.parse::()?; - let unit = if let Some(next_numeric_start) = rest.find(|c: char| !c.is_alphabetic()) - { - let (unit, rest) = rest.split_at(next_numeric_start); - s = rest; - unit - } else { - s = ""; - rest - }; - total += duration_from_units(val, unit)?; - } else if s == "0" { - return Ok(K8sDuration { - duration: Duration::from_secs(0), - is_negative, - }); - } else { - return Err(ParseError::NoUnit); - } - } - - Ok(K8sDuration { - duration: total, - is_negative, - }) - } -} - -impl Serialize for K8sDuration { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_str(self) - } -} - -impl<'de> Deserialize<'de> for K8sDuration { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = K8sDuration; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("a string in Go `time.Duration.String()` format") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - let val = value.parse::().map_err(de::Error::custom)?; - Ok(val) - } - } - deserializer.deserialize_str(Visitor) - } -} - -impl schemars::JsonSchema for K8sDuration { - // see - // https://github.com/kubernetes/apimachinery/blob/756e2227bf3a486098f504af1a0ffb736ad16f4c/pkg/apis/meta/v1/duration.go#L61 - fn schema_name() -> String { - "K8sDuration".to_owned() - } - - fn is_referenceable() -> bool { - false - } - - fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - schemars::schema::SchemaObject { - instance_type: Some(schemars::schema::InstanceType::String.into()), - // the format should *not* be "duration", because "duration" means - // the duration is formatted in ISO 8601, as described here: - // https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-02#section-7.3.1 - format: None, - ..Default::default() - } - .into() - } -} -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parses_the_same_as_go() { - const MINUTE: Duration = Duration::from_secs(60); - const HOUR: Duration = Duration::from_secs(60 * 60); - // from Go: - // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/time/time_test.go;l=891-951 - // ``` - // var parseDurationTests = []struct { - // in string - // want Duration - // }{ - let cases: &[(&str, K8sDuration)] = &[ - // // simple - // {"0", 0}, - ("0", Duration::from_secs(0).into()), - // {"5s", 5 * Second}, - ("5s", Duration::from_secs(5).into()), - // {"30s", 30 * Second}, - ("30s", Duration::from_secs(30).into()), - // {"1478s", 1478 * Second}, - ("1478s", Duration::from_secs(1478).into()), - // // sign - // {"-5s", -5 * Second}, - ( - "-5s", - K8sDuration { - duration: Duration::from_secs(5), - is_negative: true, - }, - ), - // {"+5s", 5 * Second}, - ("+5s", Duration::from_secs(5).into()), - // {"-0", 0}, - ( - "-0", - K8sDuration { - duration: Duration::from_secs(0), - is_negative: true, - }, - ), - // {"+0", 0}, - ("+0", Duration::from_secs(0).into()), - // // decimal - // {"5.0s", 5 * Second}, - ("5s", Duration::from_secs(5).into()), - // {"5.6s", 5*Second + 600*Millisecond}, - ( - "5.6s", - (Duration::from_secs(5) + Duration::from_millis(600)).into(), - ), - // {"5.s", 5 * Second}, - ("5.s", Duration::from_secs(5).into()), - // {".5s", 500 * Millisecond}, - (".5s", Duration::from_millis(500).into()), - // {"1.0s", 1 * Second}, - ("1.0s", Duration::from_secs(1).into()), - // {"1.00s", 1 * Second}, - ("1.00s", Duration::from_secs(1).into()), - // {"1.004s", 1*Second + 4*Millisecond}, - ( - "1.004s", - (Duration::from_secs(1) + Duration::from_millis(4)).into(), - ), - // {"1.0040s", 1*Second + 4*Millisecond}, - ( - "1.0040s", - (Duration::from_secs(1) + Duration::from_millis(4)).into(), - ), - // {"100.00100s", 100*Second + 1*Millisecond}, - ( - "100.00100s", - (Duration::from_secs(100) + Duration::from_millis(1)).into(), - ), - // // different units - // {"10ns", 10 * Nanosecond}, - ("10ns", Duration::from_nanos(10).into()), - // {"11us", 11 * Microsecond}, - ("11us", Duration::from_micros(11).into()), - // {"12µs", 12 * Microsecond}, // U+00B5 - ("12µs", Duration::from_micros(12).into()), - // {"12μs", 12 * Microsecond}, // U+03BC - ("12μs", Duration::from_micros(12).into()), - // {"13ms", 13 * Millisecond}, - ("13ms", Duration::from_millis(13).into()), - // {"14s", 14 * Second}, - ("14s", Duration::from_secs(14).into()), - // {"15m", 15 * Minute}, - ("15m", (15 * MINUTE).into()), - // {"16h", 16 * Hour}, - ("16h", (16 * HOUR).into()), - // // composite durations - // {"3h30m", 3*Hour + 30*Minute}, - ("3h30m", (3 * HOUR + 30 * MINUTE).into()), - // {"10.5s4m", 4*Minute + 10*Second + 500*Millisecond}, - ( - "10.5s4m", - (4 * MINUTE + Duration::from_secs(10) + Duration::from_millis(500)).into(), - ), - // {"-2m3.4s", -(2*Minute + 3*Second + 400*Millisecond)}, - ( - "-2m3.4s", - K8sDuration { - duration: 2 * MINUTE + Duration::from_secs(3) + Duration::from_millis(400), - is_negative: true, - }, - ), - // {"1h2m3s4ms5us6ns", 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond}, - ( - "1h2m3s4ms5us6ns", - (1 * HOUR - + 2 * MINUTE - + Duration::from_secs(3) - + Duration::from_millis(4) - + Duration::from_micros(5) - + Duration::from_nanos(6)) - .into(), - ), - // {"39h9m14.425s", 39*Hour + 9*Minute + 14*Second + 425*Millisecond}, - ( - "39h9m14.425s", - (39 * HOUR + 9 * MINUTE + Duration::from_secs(14) + Duration::from_millis(425)) - .into(), - ), - // // large value - // {"52763797000ns", 52763797000 * Nanosecond}, - ("52763797000ns", Duration::from_nanos(52763797000).into()), - // // more than 9 digits after decimal point, see https://golang.org/issue/6617 - // {"0.3333333333333333333h", 20 * Minute}, - ("0.3333333333333333333h", (20 * MINUTE).into()), - // // 9007199254740993 = 1<<53+1 cannot be stored precisely in a float64 - // {"9007199254740993ns", (1<<53 + 1) * Nanosecond}, - ( - "9007199254740993ns", - Duration::from_nanos((1 << 53) + 1).into(), - ), - // Rust Durations can handle larger durations than Go's - // representation, so skip these tests for their precision limits - - // // largest duration that can be represented by int64 in nanoseconds - // {"9223372036854775807ns", (1<<63 - 1) * Nanosecond}, - // ("9223372036854775807ns", Duration::from_nanos((1 << 63) - 1).into()), - // {"9223372036854775.807us", (1<<63 - 1) * Nanosecond}, - // ("9223372036854775.807us", Duration::from_nanos((1 << 63) - 1).into()), - // {"9223372036s854ms775us807ns", (1<<63 - 1) * Nanosecond}, - // {"-9223372036854775808ns", -1 << 63 * Nanosecond}, - // {"-9223372036854775.808us", -1 << 63 * Nanosecond}, - // {"-9223372036s854ms775us808ns", -1 << 63 * Nanosecond}, - // // largest negative value - // {"-9223372036854775808ns", -1 << 63 * Nanosecond}, - // // largest negative round trip value, see https://golang.org/issue/48629 - // {"-2562047h47m16.854775808s", -1 << 63 * Nanosecond}, - - // // huge string; issue 15011. - // {"0.100000000000000000000h", 6 * Minute}, - ("0.100000000000000000000h", (6 * MINUTE).into()), // // This value tests the first overflow check in leadingFraction. - // {"0.830103483285477580700h", 49*Minute + 48*Second + 372539827*Nanosecond}, - // } - // ``` - ]; - - for (input, expected) in cases { - let parsed = dbg!(input).parse::().unwrap(); - assert_eq!(&dbg!(parsed), expected); - } - } -} diff --git a/src/gateway.rs b/src/gateway.rs index 8bd6db6..d19c525 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -9,7 +9,7 @@ use std::collections::BTreeMap; )] #[kube( group = "gateway.networking.k8s.io", - version = "v1", + version = "v1beta1", kind = "Gateway", status = "GatewayStatus", namespaced @@ -20,121 +20,42 @@ pub struct GatewaySpec { /// GatewayClass resource. pub gateway_class_name: ObjectName, - /// Listeners associated with this Gateway. Listeners define - /// logical endpoints that are bound on this Gateway's addresses. - /// At least one Listener MUST be specified. - /// - /// Each Listener in a set of Listeners (for example, in a single Gateway) - /// MUST be _distinct_, in that a traffic flow MUST be able to be assigned to - /// exactly one listener. (This section uses "set of Listeners" rather than - /// "Listeners in a single Gateway" because implementations MAY merge configuration - /// from multiple Gateways onto a single data plane, and these rules _also_ - /// apply in that case). - /// - /// Practically, this means that each listener in a set MUST have a unique - /// combination of Port, Protocol, and, if supported by the protocol, Hostname. - /// - /// Some combinations of port, protocol, and TLS settings are considered - /// Core support and MUST be supported by implementations based on their - /// targeted conformance profile: - /// - /// HTTP Profile - /// - /// 1. HTTPRoute, Port: 80, Protocol: HTTP - /// 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided - /// - /// TLS Profile - /// - /// 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough - /// - // /"Distinct" Listeners have the following property: - /// - /// The implementation can match inbound requests to a single distinct - /// Listener. When multiple Listeners share values for fields (for - /// example, two Listeners with the same Port value), the implementation - /// can match requests to only one of the Listeners using other - /// Listener fields. - /// - /// For example, the following Listener scenarios are distinct: - /// - /// 1. Multiple Listeners with the same Port that all use the "HTTP" - /// Protocol that all have unique Hostname values. - /// 2. Multiple Listeners with the same Port that use either the "HTTPS" or - /// "TLS" Protocol that all have unique Hostname values. - /// 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener - /// with the same Protocol has the same Port value. - /// - /// Some fields in the Listener struct have possible values that affect - /// whether the Listener is distinct. Hostname is particularly relevant - /// for HTTP or HTTPS protocols. - /// - /// When using the Hostname value to select between same-Port, same-Protocol - /// Listeners, the Hostname value must be different on each Listener for the - /// Listener to be distinct. - /// - /// When the Listeners are distinct based on Hostname, inbound request - /// hostnames MUST match from the most specific to least specific Hostname - /// values to choose the correct Listener and its associated set of Routes. - /// - /// Exact matches must be processed before wildcard matches, and wildcard - /// matches must be processed before fallback (empty Hostname value) - /// matches. For example, `"foo.example.com"` takes precedence over - /// `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. - /// - /// Additionally, if there are multiple wildcard entries, more specific - /// wildcard entries must be processed before less specific wildcard entries. - /// For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. - /// The precise definition here is that the higher the number of dots in the - /// hostname to the right of the wildcard character, the higher the precedence. - /// - /// The wildcard character will match any number of characters _and dots_ to - /// the left, however, so `"*.example.com"` will match both - /// `"foo.bar.example.com"` _and_ `"bar.example.com"`. - /// - /// If a set of Listeners contains Listeners that are not distinct, then those - /// Listeners are Conflicted, and the implementation MUST set the "Conflicted" - /// condition in the Listener Status to "True". - /// - /// Implementations MAY choose to accept a Gateway with some Conflicted - /// Listeners only if they only accept the partial Listener set that contains - /// no Conflicted Listeners. To put this another way, implementations may - /// accept a partial Listener set only if they throw out *all* the conflicting - /// Listeners. No picking one of the conflicting listeners as the winner. - /// This also means that the Gateway must have at least one non-conflicting - /// Listener in this case, otherwise it violates the requirement that at - /// least one Listener must be present. - /// - /// The implementation MUST set a "ListenersNotValid" condition on the - /// Gateway Status when the Gateway contains Conflicted Listeners whether or - /// not they accept the Gateway. That Condition SHOULD clearly - /// indicate in the Message which Listeners are conflicted, and which are - /// Accepted. Additionally, the Listener status for those listeners SHOULD - /// indicate which Listeners are conflicted and not Accepted. - /// - /// A Gateway's Listeners are considered "compatible" if: - /// - /// 1. They are distinct. - /// 2. The implementation can serve them in compliance with the Addresses - /// requirement that all Listeners are available on all assigned - /// addresses. - /// - /// Compatible combinations in Extended support are expected to vary across - /// implementations. A combination that is compatible for one implementation - /// may not be compatible for another. - /// - /// For example, an implementation that cannot serve both TCP and UDP listeners - /// on the same address, or cannot mix HTTPS and generic TLS listens on the same port - /// would not consider those cases compatible, even though they are distinct. - /// - /// Note that requests SHOULD match at most one Listener. For example, if - /// Listeners are defined for "foo.example.com" and "*.example.com", a - /// request to "foo.example.com" SHOULD only be routed using routes attached - /// to the "foo.example.com" Listener (and not the "*.example.com" Listener). - /// This concept is known as "Listener Isolation". Implementations that do - /// not support Listener Isolation MUST clearly document this. - /// - /// Implementations MAY merge separate Gateways onto a single set of - /// Addresses if all Listeners across all Gateways are compatible. + /// Listeners associated with this Gateway. Listeners define logical + /// endpoints that are bound on this Gateway's addresses. At least one + /// Listener MUST be specified. + /// + /// Each listener in a Gateway must have a unique combination of Hostname, + /// Port, and Protocol. + /// + /// An implementation MAY group Listeners by Port and then collapse each + /// group of Listeners into a single Listener if the implementation + /// determines that the Listeners in the group are "compatible". An + /// implementation MAY also group together and collapse compatible Listeners + /// belonging to different Gateways. + /// + /// For example, an implementation might consider Listeners to be compatible + /// with each other if all of the following conditions are met: + /// + /// 1. Either each Listener within the group specifies the "HTTP" Protocol or + /// each Listener within the group specifies either the "HTTPS" or "TLS" + /// Protocol. + /// + /// 2. Each Listener within the group specifies a Hostname that is unique + /// within the group. + /// + /// 3. As a special case, one Listener within a group may omit Hostname, in + /// which case this Listener matches when no other Listener matches. + /// + /// If the implementation does collapse compatible Listeners, the hostname + /// provided in the incoming client request MUST be matched to a Listener to + /// find the correct set of Routes. The incoming hostname MUST be matched + /// using the Hostname field for each Listener in order of most to least + /// specific. That is, exact matches must be processed before wildcard + /// matches. + /// + /// If this field specifies multiple Listeners that have the same Port value + /// but are not compatible, the implementation must raise a "Conflicted" + /// condition in the Listener status. /// /// Support: Core pub listeners: Vec, @@ -150,6 +71,9 @@ pub struct GatewaySpec { /// other networking infrastructure, or some other address that traffic will /// be sent to. /// + /// The .listener.hostname field is used to route traffic that has already + /// arrived at the Gateway to the correct in-cluster destination. + /// /// If no Addresses are specified, the implementation MAY schedule the /// Gateway in an implementation-specific manner, assigning an appropriate /// set of Addresses. @@ -160,11 +84,6 @@ pub struct GatewaySpec { /// /// Support: Extended pub addresses: Option>, - - /// Infrastructure defines infrastructure level attributes about this Gateway instance. - /// - /// Support: Core - pub infrastructure: Option, } /// Listener embodies the concept of a logical endpoint where a Gateway accepts @@ -186,12 +105,12 @@ pub struct Listener { /// Implementations MUST apply Hostname matching appropriately for each of /// the following protocols: /// - /// * TLS: The Listener Hostname MUST match the SNI. - /// * HTTP: The Listener Hostname MUST match the Host header of the request. - /// * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP - /// protocol layers as described above. If an implementation does not - /// ensure that both the SNI and Host header match the Listener hostname, - /// it MUST clearly document that. + /// * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener + /// Hostname MUST match the Host header of the request. * HTTPS: The + /// Listener Hostname SHOULD match at both the TLS and HTTP protocol layers + /// as described above. If an implementation does not ensure that both the + /// SNI and Host header match the Listener hostname, it MUST clearly document + /// that. /// /// For HTTPRoute and TLSRoute resources, there is an interaction with the /// `spec.hostnames` array. When both listener and route specify hostnames, @@ -199,10 +118,6 @@ pub struct Listener { /// accepted. For more information, refer to the Route specific Hostnames /// documentation. /// - /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted - /// as a suffix match. That means that a match for `*.example.com` would match - /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. - /// /// Support: Core pub hostname: Option, @@ -222,8 +137,8 @@ pub struct Listener { /// The association of SNIs to Certificate defined in GatewayTLSConfig is /// defined based on the Hostname field for this listener. /// - /// The GatewayClass MUST use the longest matching SNI out of all - /// available certificates for any TLS handshake. + /// The GatewayClass MUST use the longest matching SNI out of all available + /// certificates for any TLS handshake. /// /// Support: Core pub tls: Option, @@ -237,16 +152,16 @@ pub struct Listener { /// determined in order of the following criteria: /// /// * The most specific match as defined by the Route type. - /// * The oldest Route based on creation timestamp. For example, a Route with - /// a creation timestamp of "2020-09-08 01:02:03" is given precedence over - /// a Route with a creation timestamp of "2020-09-08 01:02:04". + /// * The oldest Route based on creation timestamp. For example, a Route + /// with a creation timestamp of "2020-09-08 01:02:03" is given precedence + /// over a Route with a creation timestamp of "2020-09-08 01:02:04". /// * If everything else is equivalent, the Route appearing first in /// alphabetical order (namespace/name) should be given precedence. For /// example, foo/bar is given precedence over foo/baz. /// /// All valid rules within a Route attached to this Listener should be - /// implemented. Invalid Route rules can be ignored (sometimes that will mean - /// the full Route). If a Route rule transitions from valid to invalid, + /// implemented. Invalid Route rules can be ignored (sometimes that will + /// mean the full Route). If a Route rule transitions from valid to invalid, /// support for that Route rule should be dropped to ensure consistency. For /// example, even if a filter specified by a Route rule is invalid, the rest /// of the rules within that Route should still be supported. @@ -256,18 +171,18 @@ pub struct Listener { } /// ProtocolType defines the application protocol accepted by a Listener. -/// Implementations are not required to accept all the defined protocols. If an -/// implementation does not support a specified protocol, it MUST set the -/// "Accepted" condition to False for the affected Listener with a reason of -/// "UnsupportedProtocol". +/// Implementations are not required to accept all the defined protocols. +/// If an implementation does not support a specified protocol, it +/// should raise a "Detached" condition for the affected Listener with +/// a reason of "UnsupportedProtocol". /// /// Core ProtocolType values are listed in the table below. /// /// Implementations can define their own protocols if a core ProtocolType does not /// exist. Such definitions must use prefixed name, such as /// `mycompany.com/my-custom-protocol`. Un-prefixed names are reserved for core -/// protocols. Any protocol defined by implementations will fall under -/// Implementation-specific conformance. +/// protocols. Any protocol defined by implementations will fall under custom +/// conformance. /// /// Valid values include: /// @@ -278,6 +193,7 @@ pub struct Listener { /// /// * "example.com" - must include path if domain is used /// * "foo.example.com" - must include path if domain is used +/// pub type ProtocolType = String; /// GatewayTLSConfig describes a TLS configuration. @@ -286,34 +202,34 @@ pub type ProtocolType = String; )] #[serde(rename_all = "camelCase")] pub struct GatewayTlsConfig { - /// Mode defines the TLS behavior for the TLS session initiated by the client. - /// There are two possible modes: + /// Mode defines the TLS behavior for the TLS session initiated by the + /// client. There are two possible modes: /// - /// - Terminate: The TLS session between the downstream client - /// and the Gateway is terminated at the Gateway. This mode requires + /// - Terminate: The TLS session between the downstream client and the + /// Gateway is terminated at the Gateway. This mode requires /// certificateRefs to be set and contain at least one element. /// - Passthrough: The TLS session is NOT terminated by the Gateway. This - /// implies that the Gateway can't decipher the TLS stream except for - /// the ClientHello message of the TLS protocol. - /// CertificateRefs field is ignored in this mode. + /// implies that the Gateway can't decipher the TLS stream except for the + /// ClientHello message of the TLS protocol. CertificateRefs field is + /// ignored in this mode. /// /// Support: Core pub mode: Option, - /// CertificateRefs contains a series of references to Kubernetes objects that - /// contains TLS certificates and private keys. These certificates are used to - /// establish a TLS handshake for requests that match the hostname of the - /// associated listener. + /// CertificateRefs contains a series of references to Kubernetes objects + /// that contains TLS certificates and private keys. These certificates are + /// used to establish a TLS handshake for requests that match the hostname + /// of the associated listener. /// /// A single CertificateRef to a Kubernetes Secret has "Core" support. /// Implementations MAY choose to support attaching multiple certificates to /// a Listener, but this behavior is implementation-specific. /// /// References to a resource in different namespace are invalid UNLESS there - /// is a ReferenceGrant in the target namespace that allows the certificate - /// to be attached. If a ReferenceGrant does not allow this reference, the + /// is a ReferencePolicy in the target namespace that allows the certificate + /// to be attached. If a ReferencePolicy does not allow this reference, the /// "ResolvedRefs" condition MUST be set to False for this listener with the - /// "RefNotPermitted" reason. + /// "InvalidCertificateRef" reason. /// /// This field is required to have at least one element when the mode is set /// to "Terminate" (default) and is optional otherwise. @@ -321,9 +237,11 @@ pub struct GatewayTlsConfig { /// CertificateRefs can reference to standard Kubernetes resources, i.e. /// Secret, or implementation-specific custom resources. /// - /// Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls + /// Support: Core - A single reference to a Kubernetes Secret of type + /// kubernetes.io/tls /// - /// Support: Implementation-specific (More than one reference or other resource types) + /// Support: Implementation-specific (More than one reference or other + /// resource types) pub certificate_refs: Option>, /// Options are a list of key/value pairs to enable extended TLS @@ -422,61 +340,28 @@ pub struct GatewayAddress { /// GatewayStatus defines the observed state of Gateway. #[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct GatewayStatus { - /// Addresses lists the network addresses that have been bound to the - /// Gateway. - /// - /// This list may differ from the addresses provided in the spec under some - /// conditions: - /// - /// * no addresses are specified, all addresses are dynamically assigned - /// * a combination of specified and dynamic addresses are assigned - /// * a specified address was unusable (e.g. already in use) + /// Addresses lists the IP addresses that have actually been bound to the + /// Gateway. These addresses may differ from the addresses in the Spec, e.g. + /// if the Gateway automatically assigns an address from a reserved pool. pub addresses: Option>, /// Conditions describe the current conditions of the Gateway. /// - /// Implementations should prefer to express Gateway conditions - /// using the `GatewayConditionType` and `GatewayConditionReason` - /// constants so that operators and tools can converge on a common - /// vocabulary to describe Gateway state. + /// Implementations should prefer to express Gateway conditions using the + /// `GatewayConditionType` and `GatewayConditionReason` constants so that + /// operators and tools can converge on a common vocabulary to describe + /// Gateway state. /// /// Known condition types are: /// - /// * "Accepted" - /// * "Programmed" + /// * "Scheduled" /// * "Ready" pub conditions: Option>, - /// Listeners provide status for each unique listener port defined in the Spec. + /// Routes is a list of routes bound to the Gateway. pub listeners: Option>, } -/// GatewayInfrastructure defines infrastructure level attributes about a Gateway instance. -#[derive( - Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, -)] -pub struct GatewayInfrastructure { - /// Labels that SHOULD be applied to any resources created in response to this Gateway. - /// - /// For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. - /// For other implementations, this refers to any relevant (implementation specific) "labels" concepts. - /// - /// An implementation may chose to add additional implementation-specific labels as they see fit. - /// - /// Support: Extended - pub labels: Option>, - - /// Annotations that SHOULD be applied to any resources created in response to this Gateway. - /// - /// For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. - /// For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. - /// - /// An implementation may chose to add additional implementation-specific annotations as they see fit. - /// - /// Support: Extended - pub annotations: Option>, -} - /// GatewayConditionType is a type of condition associated with a /// Gateway. This type should be used with the GatewayStatus.Conditions /// field. @@ -506,21 +391,6 @@ pub struct ListenerStatus { /// AttachedRoutes represents the total number of Routes that have been /// successfully attached to this Listener. - /// - /// Successful attachment of a Route to a Listener is based solely on the - /// combination of the AllowedRoutes field on the corresponding Listener - /// and the Route's ParentRefs field. A Route is successfully attached to - /// a Listener when it is selected by the Listener's AllowedRoutes field - /// AND the Route has a valid ParentRef selecting the whole Gateway - /// resource or a specific Listener as a parent resource (more detail on - /// attachment semantics can be found in the documentation on the various - /// Route kinds ParentRefs fields). Listener or Route status does not impact - /// successful attachment, i.e. the AttachedRoutes field count MUST be set - /// for Listeners with condition Accepted: false and MUST count successfully - /// attached Routes that may themselves have Accepted: false conditions. - /// - /// Uses for this field include troubleshooting Route attachment and - /// measuring blast radius/impact of changes to a Listener. pub attached_routes: u16, /// Conditions describe the current condition of this listener. diff --git a/src/gatewayclass.rs b/src/gatewayclass.rs index af8ee06..a17d43e 100644 --- a/src/gatewayclass.rs +++ b/src/gatewayclass.rs @@ -1,29 +1,29 @@ use crate::*; use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1; -/// GatewayClass describes a class of Gateways available to the user for creating -/// Gateway resources. -/// -/// It is recommended that this resource be used as a template for Gateways. This -/// means that a Gateway is based on the state of the GatewayClass at the time it -/// was created and changes to the GatewayClass or associated parameters are not -/// propagated down to existing Gateways. This recommendation is intended to -/// limit the blast radius of changes to GatewayClass or associated parameters. -/// If implementations choose to propagate GatewayClass changes to existing -/// Gateways, that MUST be clearly documented by the implementation. -/// -/// Whenever one or more Gateways are using a GatewayClass, implementations SHOULD -/// add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the -/// associated GatewayClass. This ensures that a GatewayClass associated with a -/// Gateway is not deleted while in use. -/// -/// GatewayClass is a Cluster level resource. +// GatewayClass describes a class of Gateways available to the user for creating +// Gateway resources. +// +// It is recommended that this resource be used as a template for Gateways. This +// means that a Gateway is based on the state of the GatewayClass at the time it +// was created and changes to the GatewayClass or associated parameters are not +// propagated down to existing Gateways. This recommendation is intended to +// limit the blast radius of changes to GatewayClass or associated parameters. +// If implementations choose to propagate GatewayClass changes to existing +// Gateways, that MUST be clearly documented by the implementation. +// +// Whenever one or more Gateways are using a GatewayClass, implementations MUST +// add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the +// associated GatewayClass. This ensures that a GatewayClass associated with a +// Gateway is not deleted while in use. +// +// GatewayClass is a Cluster level resource. #[derive( Clone, Debug, kube::CustomResource, serde::Deserialize, serde::Serialize, schemars::JsonSchema, )] #[kube( group = "gateway.networking.k8s.io", - version = "v1", + version = "v1beta1", kind = "GatewayClass", status = "GatewayClassStatus" )] @@ -35,8 +35,6 @@ pub struct GatewayClassSpec { /// Example: "example.net/gateway-controller". /// /// This field is not mutable and cannot be empty. - /// - /// Support: Core pub controller_name: GatewayController, /// ParametersRef is a reference to a resource that contains the @@ -51,7 +49,7 @@ pub struct GatewayClassSpec { /// If the referent cannot be found, the GatewayClass's "InvalidParameters" /// status condition will be true. /// - /// Support: Implementation-specific + /// Support: Custom pub paramters_ref: Option, /// Description helps describe a GatewayClass with more details. @@ -98,12 +96,4 @@ pub struct GatewayClassStatus { /// Controllers should prefer to publish conditions using values of /// GatewayClassConditionType for the type of each Condition. pub conditions: Option>, - - /// SupportedFeatures is the set of features the GatewayClass support. - /// It MUST be sorted in ascending alphabetical order. - pub supported_features: Option>, } - -/// SupportedFeature is used to describe distinct features that are covered by -/// conformance tests. -pub type SupportedFeature = String; diff --git a/src/httproute.rs b/src/httproute.rs index 973367d..2ee7ac3 100644 --- a/src/httproute.rs +++ b/src/httproute.rs @@ -15,7 +15,7 @@ use crate::*; )] #[kube( group = "gateway.networking.k8s.io", - version = "v1", + version = "v1beta1", kind = "HTTPRoute", root = "HttpRoute", status = "HttpRouteStatus", @@ -26,14 +26,9 @@ pub struct HttpRouteSpec { #[serde(flatten)] pub inner: CommonRouteSpec, - /// Hostnames defines a set of hostnames that should match against the HTTP Host - /// header to select a HTTPRoute used to process the request. Implementations - /// MUST ignore any port value specified in the HTTP Host header while - /// performing a match and (absent of any applicable header modification - /// configuration) MUST forward this header unmodified to the backend. - /// - /// Valid values for Hostnames are determined by RFC 1123 definition of a - /// hostname with 2 notable exceptions: + /// Hostnames defines a set of hostname that should match against the HTTP + /// Host header to select a HTTPRoute to process the request. This matches + /// the RFC 1123 definition of a hostname with 2 notable exceptions: /// /// 1. IPs are not allowed. /// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard @@ -49,13 +44,8 @@ pub struct HttpRouteSpec { /// * A Listener with `*.example.com` as the hostname matches HTTPRoutes /// that have either not specified any hostnames or have specified at least /// one hostname that matches the Listener hostname. For example, - /// `*.example.com`, `test.example.com`, and `foo.test.example.com` would - /// all match. On the other hand, `example.com` and `test.example.net` would - /// not match. - /// - /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted - /// as a suffix match. That means that a match for `*.example.com` would match - /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + /// `test.example.com` and `*.example.com` would both match. On the other + /// hand, `example.com` and `test.example.net` would not match. /// /// If both the Listener and HTTPRoute have specified hostnames, any /// HTTPRoute hostnames that do not match the Listener hostname MUST be @@ -68,16 +58,6 @@ pub struct HttpRouteSpec { /// implementation must raise an 'Accepted' Condition with a status of /// `False` in the corresponding RouteParentStatus. /// - /// In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. - /// overlapping wildcard matching and exact matching hostnames), precedence must - /// be given to rules from the HTTPRoute with the largest number of: - /// - /// * Characters in a matching non-wildcard hostname. - /// * Characters in a matching hostname. - /// - /// If ties exist across multiple Routes, the matching precedence rules for - /// HTTPRouteMatches takes over. - /// /// Support: Core pub hostnames: Option>, @@ -124,17 +104,15 @@ pub struct HttpRouteRule { /// HTTP request. /// /// Proxy or Load Balancer routing configuration generated from HTTPRoutes - /// MUST prioritize matches based on the following criteria, continuing on - /// ties. Across all rules specified on applicable Routes, precedence must be - /// given to the match having: - /// - /// * "Exact" path match. - /// * "Prefix" path match with largest number of characters. - /// * Method match. - /// * Largest number of header matches. - /// * Largest number of query param matches. + /// MUST prioritize rules based on the following criteria, continuing on + /// ties. Precedence must be given to the the Rule with the largest number + /// of: /// - /// Note: The precedence of RegularExpression path matches are implementation-specific. + /// * Characters in a matching non-wildcard hostname. + /// * Characters in a matching hostname. + /// * Characters in a matching path. + /// * Header matches. + /// * Query param matches. /// /// If ties still exist across multiple Routes, matching precedence MUST be /// determined in order of the following criteria, continuing on ties: @@ -143,37 +121,30 @@ pub struct HttpRouteRule { /// * The Route appearing first in alphabetical order by /// "{namespace}/{name}". /// - /// If ties still exist within an HTTPRoute, matching precedence MUST be granted - /// to the FIRST matching rule (in list order) with a match meeting the above - /// criteria. + /// If ties still exist within the Route that has been given precedence, + /// matching precedence MUST be granted to the first matching rule meeting + /// the above criteria. /// /// When no rules matching a request have been successfully attached to the /// parent a request is coming from, a HTTP 404 status code MUST be returned. pub matches: Option>, - /// Filters define the filters that are applied to requests that match - /// this rule. + /// Filters define the filters that are applied to requests that match this + /// rule. /// /// The effects of ordering of multiple behaviors are currently unspecified. /// This can change in the future based on feedback during the alpha stage. /// - /// Conformance-levels at this level are defined based on the type of filter: + /// Conformance-levels at this level are defined based on the type of + /// filter: /// /// - ALL core filters MUST be supported by all implementations. /// - Implementers are encouraged to support extended filters. /// - Implementation-specific custom filters have no API guarantees across /// implementations. /// - /// Specifying the same filter multiple times is not supported unless explicitly - /// indicated in the filter. - /// - /// All filters are expected to be compatible with each other except for the - /// URLRewrite and RequestRedirect filters, which may not be combined. If an - /// implementation can not support other combinations of filters, they must clearly - /// document that limitation. In cases where incompatible or unsupported - /// filters are specified and cause the `Accepted` condition to be set to status - /// `False`, implementations may use the `IncompatibleFilters` reason to specify - /// this configuration error. + /// Specifying a core filter multiple times has unspecified or custom + /// conformance. /// /// Support: Core pub filters: Option>, @@ -181,80 +152,30 @@ pub struct HttpRouteRule { /// BackendRefs defines the backend(s) where matching requests should be /// sent. /// - /// Failure behavior here depends on how many BackendRefs are specified and - /// how many are invalid. + /// A 500 status code MUST be returned if there are no BackendRefs or + /// filters specified that would result in a response being sent. /// - /// If *all* entries in BackendRefs are invalid, and there are also no filters - /// specified in this route rule, *all* traffic which matches this rule MUST - /// receive a 500 status code. + /// A BackendRef is considered invalid when it refers to: /// - /// See the HTTPBackendRef definition for the rules about what makes a single - /// HTTPBackendRef invalid. + /// * an unknown or unsupported kind of resource + /// * a resource that does not exist + /// * a resource in another namespace when the reference has not been + /// explicitly allowed by a ReferencePolicy (or equivalent concept). /// - /// When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + /// When a BackendRef is invalid, 500 status codes MUST be returned for /// requests that would have otherwise been routed to an invalid backend. If /// multiple backends are specified, and some are invalid, the proportion of /// requests that would otherwise have been routed to an invalid backend /// MUST receive a 500 status code. /// - /// For example, if two backends are specified with equal weights, and one is - /// invalid, 50 percent of traffic must receive a 500. Implementations may - /// choose how that 50 percent is determined. + /// When a BackendRef refers to a Service that has no ready endpoints, it is + /// recommended to return a 503 status code. /// /// Support: Core for Kubernetes Service - /// - /// Support: Extended for Kubernetes ServiceImport - /// - /// Support: Implementation-specific for any other resource + /// Support: Custom for any other resource /// /// Support for weight: Core pub backend_refs: Option>, - - // Timeouts defines the timeouts that can be configured for an HTTP request. - // - // Support: Extended - pub timeouts: Option, -} - -/// HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute. -/// Timeout values are represented with Gateway API Duration formatting. -/// Specifying a zero value such as "0s" is interpreted as no timeout. -#[derive( - Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, -)] -#[serde(rename_all = "camelCase")] -pub struct HttpRouteTimeouts { - /// Request specifies the maximum duration for a gateway to respond to an HTTP request. - /// If the gateway has not been able to respond before this deadline is met, the gateway - /// MUST return a timeout error. - /// - /// For example, setting the `rules.timeouts.request` field to the value `10s` in an - /// `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds - /// to complete. - /// - /// This timeout is intended to cover as close to the whole request-response transaction - /// as possible although an implementation MAY choose to start the timeout after the entire - /// request stream has been received instead of immediately after the transaction is - /// initiated by the client. - /// - /// When this field is unspecified, request timeout behavior is implementation-specific. - /// - /// Support: Extended - pub request: Option, - - /// BackendRequest specifies a timeout for an individual request from the gateway - /// to a backend. This covers the time from when the request first starts being - /// sent from the gateway to when the full response has been received from the backend. - /// - /// An entire client HTTP transaction with a gateway, covered by the Request timeout, - /// may result in more than one call from the gateway to the destination backend, - /// for example, if automatic retries are supported. - /// - /// Because the Request timeout encompasses the BackendRequest timeout, the value of - /// BackendRequest must be <= the value of Request timeout. - /// - /// Support: Extended - pub backend_request: Option, } /// HTTPRouteMatch defines the predicate used to match requests to a given @@ -289,11 +210,10 @@ pub struct HttpRouteMatch { /// QueryParams specifies HTTP query parameter matchers. Multiple match /// values are ANDed together, meaning, a request must match all the /// specified query parameters to select the route. - /// - /// Support: Extended pub query_params: Option>, /// Method specifies HTTP method matcher. + /// /// When specified, this route will be matched only if the request has the /// specified method. /// @@ -398,8 +318,8 @@ pub type HttpMethod = String; /// HTTPRouteFilter defines processing steps that must be completed during the /// request or response lifecycle. HTTPRouteFilters are meant as an extension -/// point to express processing that may be done in Gateway implementations. Some -/// examples include request or response modification, implementing +/// point to express processing that may be done in Gateway implementations. +/// Some examples include request or response modification, implementing /// authentication strategies, rate-limiting, and traffic shaping. API /// guarantee/conformance is defined based on the type of the filter. /// @@ -414,8 +334,7 @@ pub type HttpMethod = String; /// "Support: Extended" in this package, e.g. "RequestMirror". Implementers /// are encouraged to support extended filters. /// -/// - Implementation-specific: Filters that are defined and supported by -/// specific vendors. +/// - Custom: Filters that are defined and supported by specific vendors. /// In the future, filters showing convergence in behavior across multiple /// implementations will be considered for inclusion in extended or core /// conformance levels. Filter-specific configuration for such filters @@ -455,10 +374,6 @@ pub enum HttpRouteFilter { /// Requests are sent to the specified destination, but responses from /// that destination are ignored. /// - /// This filter can be used multiple times within the same rule. Note that - /// not all implementations will be able to support mirroring to multiple - /// backends. - /// /// Support: Extended #[serde(rename_all = "camelCase")] RequestMirror { @@ -485,8 +400,6 @@ pub enum HttpRouteFilter { /// "networking.example.net"). ExtensionRef MUST NOT be used for core and /// extended filters. /// - /// This filter can be used multiple times within the same rule. - /// /// Support: Implementation-specific #[serde(rename_all = "camelCase")] ExtensionRef { extension_ref: LocalObjectReference }, @@ -588,32 +501,7 @@ pub enum HttpPathModifier { /// ReplacePrefixMatch specifies the value with which to replace the prefix /// match of a request during a rewrite or redirect. For example, a request - /// to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - /// of "/xyz" would be modified to "/xyz/bar". - /// - /// Note that this matches the behavior of the PathPrefix match type. This - /// matches full path elements. A path element refers to the list of labels - /// in the path split by the `/` separator. When specified, a trailing `/` is - /// ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - /// match the prefix `/abc`, but the path `/abcd` would not. - /// - /// ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - /// Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - /// the implementation setting the Accepted Condition for the Route to `status: False`. - /// - /// Request Path | Prefix Match | Replace Prefix | Modified Path - /// -------------|--------------|----------------|---------- - /// /foo/bar | /foo | /xyz | /xyz/bar - /// /foo/bar | /foo | /xyz/ | /xyz/bar - /// /foo/bar | /foo/ | /xyz | /xyz/bar - /// /foo/bar | /foo/ | /xyz/ | /xyz/bar - /// /foo | /foo | /xyz | /xyz - /// /foo/ | /foo | /xyz | /xyz/ - /// /foo/bar | /foo | _empty string_ | /bar - /// /foo/ | /foo | _empty string_ | / - /// /foo | /foo | _empty string_ | / - /// /foo/ | /foo | / | / - /// /foo | /foo | / | / + /// to "/foo/bar" with a prefix match of "/foo" would be modified to "/bar". #[serde(rename_all = "camelCase")] ReplacePrefixMatch { replace_prefix_match: String }, } @@ -629,15 +517,13 @@ pub struct HttpRequestRedirectFilter { /// header in the response. /// When empty, the scheme of the request is used. /// - /// Scheme redirects can affect the port of the redirect, for more information, - /// refer to the documentation for the port field of this filter. - /// /// Support: Extended pub scheme: Option, /// Hostname is the hostname to be used in the value of the `Location` /// header in the response. - /// When empty, the hostname in the `Host` header of the request is used. + /// + /// When empty, the hostname of the request is used. /// /// Support: Core pub hostname: Option, @@ -651,24 +537,7 @@ pub struct HttpRequestRedirectFilter { /// Port is the port to be used in the value of the `Location` /// header in the response. - /// - /// If no port is specified, the redirect port MUST be derived using the - /// following rules: - /// - /// * If redirect scheme is not-empty, the redirect port MUST be the well-known - /// port associated with the redirect scheme. Specifically "http" to port 80 - /// and "https" to port 443. If the redirect scheme does not have a - /// well-known port, the listener port of the Gateway SHOULD be used. - /// * If redirect scheme is empty, the redirect port MUST be the Gateway - /// Listener port. - /// - /// Implementations SHOULD NOT add the port number in the 'Location' - /// header in the following cases: - /// - /// * A Location header that will use HTTP (whether that is determined via - /// the Listener protocol or the Scheme field) _and_ use port 80. - /// * A Location header that will use HTTPS (whether that is determined via - /// the Listener protocol or the Scheme field) _and_ use port 443. + /// When empty, port (if specified) of the request is used. /// /// Support: Extended pub port: Option, @@ -709,17 +578,13 @@ pub struct HttpUrlRewriteFilter { pub struct HttpRequestMirrorFilter { /// BackendRef references a resource where mirrored requests are sent. /// - /// Mirrored requests must be sent only to a single destination endpoint - /// within this BackendRef, irrespective of how many endpoints are present - /// within this BackendRef. - /// /// If the referent cannot be found, this BackendRef is invalid and must be /// dropped from the Gateway. The controller must ensure the "ResolvedRefs" /// condition on the Route status is set to `status: False` and not configure /// this backend in the underlying implementation. /// /// If there is a cross-namespace reference to an *existing* object - /// that is not allowed by a ReferenceGrant, the controller must ensure the + /// that is not allowed by a ReferencePolicy, the controller must ensure the /// "ResolvedRefs" condition on the Route is set to `status: False`, /// with the "RefNotPermitted" reason and not configure this backend in the /// underlying implementation. @@ -728,31 +593,11 @@ pub struct HttpRequestMirrorFilter { /// should be used to provide more detail about the problem. /// /// Support: Extended for Kubernetes Service - /// - /// Support: Implementation-specific for any other resource + /// Support: Custom for any other resource pub backend_ref: BackendObjectReference, } -/// HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. -/// -/// Note that when a namespace different than the local namespace is specified, a -/// ReferenceGrant object is required in the referent namespace to allow that -/// namespace's owner to accept the reference. See the ReferenceGrant -/// documentation for details. -/// -/// When the BackendRef points to a Kubernetes Service, implementations SHOULD -/// honor the appProtocol field if it is set for the target Service Port. -/// -/// Implementations supporting appProtocol SHOULD recognize the Kubernetes -/// Standard Application Protocols defined in KEP-3726. -/// -/// If a Service appProtocol isn't specified, an implementation MAY infer the -/// backend protocol through its own means. Implementations MAY infer the -/// protocol from the Route type referring to the backend Service. -/// -/// If a Route is not able to send traffic to the backend using the specified -/// protocol then the backend is considered invalid. Implementations MUST set the -/// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. +/// HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. #[derive( Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, )] @@ -760,52 +605,29 @@ pub struct HttpRequestMirrorFilter { pub struct HttpBackendRef { /// BackendRef is a reference to a backend to forward matched requests to. /// - /// A BackendRef can be invalid for the following reasons. In all cases, the - /// implementation MUST ensure the `ResolvedRefs` Condition on the Route - /// is set to `status: False`, with a Reason and Message that indicate - /// what is the cause of the error. - /// - /// A BackendRef is invalid if: + /// If the referent cannot be found, this HTTPBackendRef is invalid and must + /// be dropped from the Gateway. The controller must ensure the + /// "ResolvedRefs" condition on the Route is set to `status: False` and not + /// configure this backend in the underlying implementation. /// - /// * It refers to an unknown or unsupported kind of resource. In this - /// case, the Reason must be set to `InvalidKind` and Message of the - /// Condition must explain which kind of resource is unknown or unsupported. - /// - /// * It refers to a resource that does not exist. In this case, the Reason must - /// be set to `BackendNotFound` and the Message of the Condition must explain - /// which resource does not exist. - /// - /// * It refers a resource in another namespace when the reference has not been - /// explicitly allowed by a ReferenceGrant (or equivalent concept). In this - /// case, the Reason must be set to `RefNotPermitted` and the Message of the - /// Condition must explain which cross-namespace reference is not allowed. - /// - /// * It refers to a Kubernetes Service that has an incompatible appProtocol - /// for the given Route type - /// - /// * The BackendTLSPolicy object is installed in the cluster, a BackendTLSPolicy - /// is present that refers to the Service, and the implementation is unable - /// to meet the requirement. At the time of writing, BackendTLSPolicy is - /// experimental, but once it becomes standard, this will become a MUST - /// requirement. - /// - /// Support: Core for Kubernetes Service - /// - /// Support: Implementation-specific for any other resource - /// - /// Support for weight: Core + /// If there is a cross-namespace reference to an *existing* object + /// that is not covered by a ReferencePolicy, the controller must ensure the + /// "ResolvedRefs" condition on the Route is set to `status: False`, + /// with the "RefNotPermitted" reason and not configure this backend in the + /// underlying implementation. /// - /// Support for Kubernetes Service appProtocol: Extended + /// In either error case, the Message of the `ResolvedRefs` Condition + /// should be used to provide more detail about the problem. /// - /// Support for BackendTLSPolicy: Experimental and ImplementationSpecific + /// Support: Custom #[serde(flatten)] pub backend_ref: Option, /// Filters defined at this level should be executed if and only if the /// request is being forwarded to the backend defined here. /// - /// Support: Implementation-specific (For broader support of filters, use the - /// Filters field in HTTPRouteRule.) + /// Support: Custom (For broader support of filters, use the Filters field + /// in HTTPRouteRule.) pub filters: Option>, } diff --git a/src/lib.rs b/src/lib.rs index 775baf4..80d3d8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ // TODO(ver): We should deny missing_docs, but this doesn't play with // CustomResource derivations. -pub mod duration; mod gateway; mod gatewayclass; mod httproute; diff --git a/src/shared.rs b/src/shared.rs index c94cec3..555daf3 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -3,14 +3,9 @@ use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1; use crate::BackendObjectReference; /// ParentReference identifies an API object (usually a Gateway) that can be considered -/// a parent of this resource (usually a route). There are two kinds of parent resources -/// with "Core" support: -/// -/// * Gateway (Gateway conformance profile) -/// * Service (Mesh conformance profile, experimental, ClusterIP Services only) -/// -/// This API may be extended in the future to support additional kinds of parent -/// resources. +/// a parent of this resource (usually a route). The only kind of parent resource +/// with "Core" support is Gateway. This API may be extended in the future to +/// support additional kinds of parent resources, such as HTTPRoute. /// /// The API object must be valid in the cluster; the Group and Kind must /// be registered in the cluster for this reference to be valid. @@ -20,41 +15,18 @@ use crate::BackendObjectReference; #[serde(rename_all = "camelCase")] pub struct ParentReference { /// Group is the group of the referent. - /// When unspecified, "gateway.networking.k8s.io" is inferred. - /// To set the core API group (such as for a "Service" kind referent), - /// Group must be explicitly set to "" (empty string). /// /// Support: Core pub group: Option, /// Kind is kind of the referent. /// - /// There are two kinds of parent resources with "Core" support: - /// - /// * Gateway (Gateway conformance profile) - /// * Service (Mesh conformance profile, experimental, ClusterIP Services only) - /// - /// Support for other resources is Implementation-Specific. + /// Support: Core (Gateway) + /// Support: Custom (Other Resources) pub kind: Option, - /// Namespace is the namespace of the referent. When unspecified, this refers - /// to the local namespace of the Route. - /// - /// Note that there are specific rules for ParentRefs which cross namespace - /// boundaries. Cross-namespace references are only valid if they are explicitly - /// allowed by something in the namespace they are referring to. For example: - /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a - /// generic way to enable any other kind of cross-namespace reference. - /// - /// ParentRefs from a Route to a Service in the same namespace are "producer" - /// routes, which apply default routing rules to inbound connections from - /// any namespace to the Service. - /// - /// ParentRefs from a Route to a Service in a different namespace are - /// "consumer" routes, and these routing rules are only applied to outbound - /// connections originating from the same namespace as the Route, for which - /// the intended destination of the connections are a Service targeted as a - /// ParentRef of the Route. + /// Namespace is the namespace of the referent. When unspecified (or empty + /// string), this refers to the local namespace of the Route. /// /// Support: Core pub namespace: Option, @@ -70,42 +42,33 @@ pub struct ParentReference { /// * Gateway: Listener Name. When both Port (experimental) and SectionName /// are specified, the name and port of the selected listener must match /// both specified values. - /// * Service: Port Name. When both Port (experimental) and SectionName - /// are specified, the name and port of the selected listener must match - /// both specified values. Note that attaching Routes to Services as Parents - /// is part of experimental Mesh support and is not supported for any other - /// purpose. /// - /// Implementations MAY choose to support attaching Routes to other resources. - /// If that is the case, they MUST clearly document how SectionName is - /// interpreted. + /// Implementations MAY choose to support attaching Routes to other + /// resources. If that is the case, they MUST clearly document how + /// SectionName is interpreted. /// - /// When unspecified (empty string), this will reference the entire resource. - /// For the purpose of status, an attachment is considered successful if at - /// least one section in the parent resource accepts it. For example, Gateway - /// listeners can restrict which Routes can attach to them by Route kind, - /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - /// the referencing Route, the Route MUST be considered successfully - /// attached. If no Gateway listeners accept attachment from this Route, the - /// Route MUST be considered detached from the Gateway. + /// When unspecified (empty string), this will reference the entire + /// resource. For the purpose of status, an attachment is considered + /// successful if at least one section in the parent resource accepts it. + /// For example, Gateway listeners can restrict which Routes can attach to + /// them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners + /// accept attachment from the referencing Route, the Route MUST be + /// considered successfully attached. If no Gateway listeners accept + /// attachment from this Route, the Route MUST be considered detached from + /// the Gateway. /// /// Support: Core pub section_name: Option, /// Port is the network port this Route targets. It can be interpreted - /// differently based on the type of parent resource. + /// differently based on the type of parent resource: /// - /// When the parent resource is a Gateway, this targets all listeners - /// listening on the specified port that also support this kind of Route(and - /// select this Route). It's not recommended to set `Port` unless the - /// networking behaviors specified in a Route must apply to a specific port - /// as opposed to a listener(s) whose port(s) may be changed. When both Port - /// and SectionName are specified, the name and port of the selected listener - /// must match both specified values. - /// - /// When the parent resource is a Service, this targets a specific port in the - /// Service spec. When both Port (experimental) and SectionName are specified, - /// the name and port of the selected port must match both specified values. + /// * Gateway: All listeners listening on the specified port that also + /// support this kind of Route(and select this Route). It's not recommended + /// to set `Port` unless the networking behaviors specified in a Route must + /// apply to a specific port as opposed to a listener(s) whose port(s) may + /// be changed. When both Port and SectionName are specified, the name and + /// port of the selected listener must match both specified values. /// /// Implementations MAY choose to support other parent resources. /// Implementations supporting other types of parent resources MUST clearly @@ -116,8 +79,8 @@ pub struct ParentReference { /// listeners can restrict which Routes can attach to them by Route kind, /// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment /// from the referencing Route, the Route MUST be considered successfully - /// attached. If no Gateway listeners accept attachment from this Route, - /// the Route MUST be considered detached from the Gateway. + /// attached. If no Gateway listeners accept attachment from this Route, the + /// Route MUST be considered detached from the Gateway. /// /// Support: Extended pub port: Option, @@ -130,65 +93,25 @@ pub struct ParentReference { )] #[serde(rename_all = "camelCase")] pub struct CommonRouteSpec { - /// ParentRefs references the resources (usually Gateways) that a Route wants - /// to be attached to. Note that the referenced parent resource needs to - /// allow this for the attachment to be complete. For Gateways, that means - /// the Gateway needs to allow attachment from Routes of this kind and - /// namespace. For Services, that means the Service must either be in the same - /// namespace for a "producer" route, or the mesh implementation must support - /// and allow "consumer" routes for the referenced Service. ReferenceGrant is - /// not applicable for governing ParentRefs to Services - it is not possible to - /// create a "producer" route for a Service in a different namespace from the - /// Route. - /// - /// There are two kinds of parent resources with "Core" support: - /// - /// * Gateway (Gateway conformance profile) - /// * Service (Mesh conformance profile, experimental, ClusterIP Services only) - /// This API may be extended in the future to support additional kinds of parent - /// resources. - /// - /// ParentRefs must be _distinct_. This means either that: - /// - /// * They select different objects. If this is the case, then parentRef - /// entries are distinct. In terms of fields, this means that the - /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must - /// be unique across all parentRef entries in the Route. - /// * They do not select different objects, but for each optional field used, - /// each ParentRef that selects the same object must set the same set of - /// optional fields to different values. If one ParentRef sets a - /// combination of optional fields, all must set the same combination. - /// - /// Some examples: - /// - /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the - /// same object must also set `sectionName`. - /// * If one ParentRef sets `port`, all ParentRefs referencing the same - /// object must also set `port`. - /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs - /// referencing the same object must also set `sectionName` and `port`. - /// - /// It is possible to separately reference multiple distinct objects that may - /// be collapsed by an implementation. For example, some implementations may - /// choose to merge compatible Gateway Listeners together. If that is the - /// case, the list of routes attached to those resources should also be + /// ParentRefs references the resources (usually Gateways) that a Route + /// wants to be attached to. Note that the referenced parent resource needs + /// to allow this for the attachment to be complete. For Gateways, that + /// means the Gateway needs to allow attachment from Routes of this kind and + /// namespace. + /// + /// The only kind of parent resource with "Core" support is Gateway. This + /// API may be extended in the future to support additional kinds of parent + /// resources such as one of the route kinds. + /// + /// It is invalid to reference an identical parent more than once. It is + /// valid to reference multiple distinct sections within the same parent + /// resource, such as 2 Listeners within a Gateway. + /// + /// It is possible to separately reference multiple distinct objects that + /// may be collapsed by an implementation. For example, some implementations + /// may choose to merge compatible Gateway Listeners together. If that is + /// the case, the list of routes attached to those resources should also be /// merged. - /// - /// Note that for ParentRefs that cross namespace boundaries, there are specific - /// rules. Cross-namespace references are only valid if they are explicitly - /// allowed by something in the namespace they are referring to. For example, - /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a - /// generic way to enable other kinds of cross-namespace reference. - /// - /// ParentRefs from a Route to a Service in the same namespace are "producer" - /// routes, which apply default routing rules to inbound connections from - /// any namespace to the Service. - /// - /// ParentRefs from a Route to a Service in a different namespace are - /// "consumer" routes, and these routing rules are only applied to outbound - /// connections originating from the same namespace as the Route, for which - /// the intended destination of the connections are a Service targeted as a - /// ParentRef of the Route. pub parent_refs: Option>, } @@ -198,28 +121,9 @@ pub type PortNumber = u16; /// BackendRef defines how a Route should forward a request to a Kubernetes /// resource. /// -/// Note that when a namespace different than the local namespace is specified, a -/// ReferenceGrant object is required in the referent namespace to allow that -/// namespace's owner to accept the reference. See the ReferenceGrant -/// documentation for details. -/// -/// When the BackendRef points to a Kubernetes Service, implementations SHOULD -/// honor the appProtocol field if it is set for the target Service Port. -/// -/// Implementations supporting appProtocol SHOULD recognize the Kubernetes -/// Standard Application Protocols defined in KEP-3726. -/// -/// If a Service appProtocol isn't specified, an implementation MAY infer the -/// backend protocol through its own means. Implementations MAY infer the -/// protocol from the Route type referring to the backend Service. -/// -/// If a Route is not able to send traffic to the backend using the specified -/// protocol then the backend is considered invalid. Implementations MUST set the -/// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. -/// -/// Note that when the BackendTLSPolicy object is enabled by the implementation, -/// there are some extra rules about validity to consider here. See the fields -/// where this struct is used for more information about the exact behavior. +/// Note that when a namespace is specified, a ReferencePolicy object is +/// required in the referent namespace to allow that namespace's owner to accept +/// the reference. See the ReferencePolicy documentation for details. #[derive( Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema, )] @@ -231,9 +135,9 @@ pub struct BackendRef { /// implementation supports. Weight is not a percentage and the sum of /// weights does not need to equal 100. /// - /// If only one backend is specified and it has a weight greater than 0, 100% - /// of the traffic is forwarded to that backend. If weight is set to 0, no - /// traffic should be forwarded for this entry. If unspecified, weight + /// If only one backend is specified and it has a weight greater than 0, + /// 100% of the traffic is forwarded to that backend. If weight is set to 0, + /// no traffic should be forwarded for this entry. If unspecified, weight /// defaults to 1. /// /// Support for this field varies based on the context where used. @@ -275,25 +179,25 @@ pub struct RouteParentStatus { /// [names]: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names pub controller_name: GatewayController, - /// Conditions describes the status of the route with respect to the Gateway. - /// Note that the route's availability is also subject to the Gateway's own - /// status conditions and listener status. + /// Conditions describes the status of the route with respect to the + /// Gateway. Note that the route's availability is also subject to the + /// Gateway's own status conditions and listener status. /// /// If the Route's ParentRef specifies an existing Gateway that supports /// Routes of this kind AND that Gateway's controller has sufficient access, /// then that Gateway's controller MUST set the "Accepted" condition on the - /// Route, to indicate whether the route has been accepted or rejected by the - /// Gateway, and why. + /// Route, to indicate whether the route has been accepted or rejected by + /// the Gateway, and why. /// /// A Route MUST be considered "Accepted" if at least one of the Route's /// rules is implemented by the Gateway. /// - /// There are a number of cases where the "Accepted" condition may not be set - /// due to lack of controller visibility, that includes when: + /// There are a number of cases where the "Accepted" condition may not be + /// set due to lack of controller visibility, that includes when: /// /// * The Route refers to a non-existent parent. /// * The Route is of a type that the controller does not support. - /// * The Route is in a namespace the controller does not have access to. + /// * The Route is in a namespace the the controller does not have access to. pub conditions: Vec, } @@ -448,17 +352,4 @@ pub type AnnotationKey = String; pub type AnnotationValue = String; /// AddressType defines how a network address is represented as a text string. -/// This may take two possible forms: -/// -/// * A predefined CamelCase string identifier (currently limited to `IPAddress` or `Hostname`) -/// * A domain-prefixed string identifier (like `acme.io/CustomAddressType`) -/// -/// Values `IPAddress` and `Hostname` have Extended support. -/// -/// The `NamedAddress` value has been deprecated in favor of implementation -/// specific domain-prefixed strings. -/// -/// All other values, including domain-prefixed values have Implementation-specific support, -/// which are used in implementation-specific behaviors. Support for additional -/// predefined CamelCase identifiers may be added in future releases. pub type AddressType = String; From c071a50d5ac990f63bc761b417bc3bdca11c4bc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 10:17:57 -0800 Subject: [PATCH 12/13] Bump DavidAnson/markdownlint-cli2-action from 13.0.0 to 14.0.0 (#83) Bumps [DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action) from 13.0.0 to 14.0.0. - [Release notes](https://github.com/davidanson/markdownlint-cli2-action/releases) - [Commits](https://github.com/davidanson/markdownlint-cli2-action/compare/ed4dec634fd2ef689c7061d5647371d8248064f1...455b6612a7b7a80f28be9e019b70abdd11696e4e) --- updated-dependencies: - dependency-name: DavidAnson/markdownlint-cli2-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/markdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index b3a863c..71a828a 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: DavidAnson/markdownlint-cli2-action@ed4dec634fd2ef689c7061d5647371d8248064f1 + - uses: DavidAnson/markdownlint-cli2-action@455b6612a7b7a80f28be9e019b70abdd11696e4e with: globs: | **/*.md From 50f8089904d97abe6eb4391e245114b1aa0bd636 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Wed, 3 Jan 2024 18:18:52 +0000 Subject: [PATCH 13/13] v0.15.0 BREAKING CHANGE: This release reverts the Gateway API version to v0.5.0, as the 1.0 CRD version is not yet available in GKE clusters. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8324df3..d0a724b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "integration"] [package] name = "k8s-gateway-api" -version = "0.14.0" +version = "0.15.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/linkerd/k8s-gateway-api-rs"