diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5d810c13b..ed0f2ba10 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/rust:1.70.0-bullseye +FROM docker.io/rust:1.72.0-bullseye ENV DEBIAN_FRONTEND=noninteractive RUN apt update && apt upgrade -y diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c28eeb294..55261bc73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,7 +142,7 @@ jobs: fail-fast: false matrix: # Run these tests against older clusters as well - k8s: [v1.23, latest] + k8s: [v1.24, latest] steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 @@ -216,7 +216,7 @@ jobs: - uses: nolar/setup-k3d-k3s@v1 with: - version: v1.23 + version: v1.24 # k3d-kube k3d-name: kube # Used to avoid rate limits when fetching the releases from k3s repo. @@ -247,7 +247,7 @@ jobs: tls: [openssl, rustls] steps: - uses: actions/checkout@v4 - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: | ~/.cargo/registry/index diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 00f85dcb6..1545c32f7 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -28,7 +28,7 @@ jobs: cluster-name: "test-cluster-1" args: >- --agents 1 - --image docker.io/rancher/k3s:v1.23.4-k3s1 + --image docker.io/rancher/k3s:v1.24.4-k3s1 --k3s-arg "--no-deploy=traefik,servicelb,metrics-server@server:*" - name: Run cargo-tarpaulin uses: actions-rs/tarpaulin@v0.1 diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 280a9b6f6..474e52cdc 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -26,7 +26,7 @@ jobs: - run: rustfmt +nightly --edition 2018 $(find . -type f -iname *.rs) - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: commit-message: rustfmt signoff: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0937673a2..9a7cbcf21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,31 @@ UNRELEASED =================== - * see https://github.com/kube-rs/kube/compare/0.87.2...main + * see https://github.com/kube-rs/kube/compare/0.88.1...main + +[0.88.1](https://github.com/kube-rs/kube/releases/tag/0.88.1) / 2024-01-26 +=================== + + +## What's Changed +This is a bug fix release for a deserialization issue introduced in 0.88.0. + +### Fixed +* Minor fixes to `ObjectList` by @flavio in https://github.com/kube-rs/kube/pull/1398 + +[0.88.0](https://github.com/kube-rs/kube/releases/tag/0.88.0) / 2024-01-21 +=================== + + +## Kubernetes `v1_29` support via `k8s-openapi` [0.21](https://github.com/Arnavion/k8s-openapi/releases/tag/v0.21.0) +Please [upgrade k8s-openapi along with kube](https://kube.rs/upgrading/) to avoid conflicts. + +## What's Changed +### Added +* Add type meta data for list types by @Danil-Grigorev in https://github.com/kube-rs/kube/pull/1380 +### Changed +* Bump MSRV to 1.70 by @clux in https://github.com/kube-rs/kube/pull/1384 +* Upgrade `k8s-openapi` for Kubernetes `v1_29` support by @clux in https://github.com/kube-rs/kube/pull/1394 [0.87.2](https://github.com/kube-rs/kube/releases/tag/0.87.2) / 2023-12-22 =================== diff --git a/README.md b/README.md index 013be8d34..7f6608846 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # kube-rs [![Crates.io](https://img.shields.io/crates/v/kube.svg)](https://crates.io/crates/kube) -[![Rust 1.70](https://img.shields.io/badge/MSRV-1.70-dea584.svg)](https://github.com/rust-lang/rust/releases/tag/1.70.0) -[![Tested against Kubernetes v1_23 and above](https://img.shields.io/badge/MK8SV-v1_23-326ce5.svg)](https://kube.rs/kubernetes-version) +[![Rust 1.72](https://img.shields.io/badge/MSRV-1.72-dea584.svg)](https://github.com/rust-lang/rust/releases/tag/1.72.0) +[![Tested against Kubernetes v1_24 and above](https://img.shields.io/badge/MK8SV-v1_24-326ce5.svg)](https://kube.rs/kubernetes-version) [![Best Practices](https://bestpractices.coreinfrastructure.org/projects/5413/badge)](https://bestpractices.coreinfrastructure.org/projects/5413) [![Discord chat](https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=plastic)](https://discord.gg/tokio) @@ -16,8 +16,8 @@ Select a version of `kube` along with the generated [k8s-openapi](https://github ```toml [dependencies] -kube = { version = "0.87.2", features = ["runtime", "derive"] } -k8s-openapi = { version = "0.20.0", features = ["latest"] } +kube = { version = "0.88.1", features = ["runtime", "derive"] } +k8s-openapi = { version = "0.21.0", features = ["latest"] } ``` [Features are available](https://github.com/kube-rs/kube/blob/main/kube/Cargo.toml#L18). @@ -152,8 +152,8 @@ By default [rustls](https://github.com/ctz/rustls) is used for TLS, but `openssl ```toml [dependencies] -kube = { version = "0.87.2", default-features = false, features = ["client", "openssl-tls"] } -k8s-openapi = { version = "0.20.0", features = ["latest"] } +kube = { version = "0.88.1", default-features = false, features = ["client", "openssl-tls"] } +k8s-openapi = { version = "0.21.0", features = ["latest"] } ``` This will pull in `openssl` and `hyper-openssl`. If `default-features` is left enabled, you will pull in two TLS stacks, and the default will remain as `rustls`. diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index 4f2e9186b..484c362dd 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -19,7 +19,7 @@ path = "boot.rs" [features] latest = ["k8s-openapi/latest"] -mk8sv = ["k8s-openapi/v1_23"] +mk8sv = ["k8s-openapi/v1_24"] rustls = ["kube/rustls-tls"] openssl = ["kube/openssl-tls"] @@ -28,7 +28,7 @@ anyhow = "1.0.44" tracing = "0.1.36" tracing-subscriber = "0.3.3" futures = "0.3.17" -kube = { path = "../kube", version = "^0.87.2", default-features = false, features = ["client", "runtime", "ws", "admission", "gzip"] } -k8s-openapi = { version = "0.20.0", default-features = false } +kube = { path = "../kube", version = "^0.88.1", default-features = false, features = ["client", "runtime", "ws", "admission", "gzip"] } +k8s-openapi = { version = "0.21.0", default-features = false } serde_json = "1.0.68" tokio = { version = "1.14.0", features = ["full"] } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c770cff75..760003f2a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -27,13 +27,13 @@ latest = ["k8s-openapi/latest"] [dev-dependencies] tokio-util = "0.7.0" assert-json-diff = "2.0.1" -garde = { version = "0.16.1", default-features = false, features = ["derive"] } +garde = { version = "0.18.0", default-features = false, features = ["derive"] } anyhow = "1.0.44" futures = "0.3.17" -jsonpath-rust = "0.3.4" -kube = { path = "../kube", version = "^0.87.2", default-features = false, features = ["admission"] } -kube-derive = { path = "../kube-derive", version = "^0.87.2", default-features = false } # only needed to opt out of schema -k8s-openapi = { version = "0.20.0", default-features = false } +jsonpath-rust = "0.4.0" +kube = { path = "../kube", version = "^0.88.1", default-features = false, features = ["admission"] } +kube-derive = { path = "../kube-derive", version = "^0.88.1", default-features = false } # only needed to opt out of schema +k8s-openapi = { version = "0.21.0", default-features = false } serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.68" serde_yaml = "0.9.19" diff --git a/kube-client/Cargo.toml b/kube-client/Cargo.toml index 10e2f39c7..922b4948a 100644 --- a/kube-client/Cargo.toml +++ b/kube-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kube-client" -version = "0.87.2" +version = "0.88.1" description = "Kubernetes client" authors = [ "clux ", @@ -12,7 +12,7 @@ repository = "https://github.com/kube-rs/kube" readme = "../README.md" keywords = ["kubernetes", "client",] categories = ["web-programming::http-client", "configuration", "network-programming", "api-bindings"] -rust-version = "1.70.0" +rust-version = "1.72.0" edition = "2021" [features] @@ -56,8 +56,8 @@ rustls = { version = "0.21.4", features = ["dangerous_configuration"], optional rustls-pemfile = { version = "1.0.0", optional = true } bytes = { version = "1.1.0", optional = true } tokio = { version = "1.14.0", features = ["time", "signal", "sync"], optional = true } -kube-core = { path = "../kube-core", version = "=0.87.2" } -jsonpath-rust = { version = "0.3.4", optional = true } +kube-core = { path = "../kube-core", version = "=0.88.1" } +jsonpath-rust = { version = "0.4.0", optional = true } tokio-util = { version = "0.7.0", optional = true, features = ["io", "codec"] } hyper = { version = "0.14.13", optional = true, features = ["client", "http1", "stream", "tcp"] } hyper-rustls = { version = "0.24.0", optional = true } @@ -75,7 +75,7 @@ hyper-openssl = { version = "0.9.2", optional = true } form_urlencoded = { version = "1.2.0", optional = true } [dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false features = [] @@ -88,6 +88,6 @@ tokio-test = "0.4.0" tower-test = "0.4.0" [dev-dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false features = ["latest"] diff --git a/kube-client/src/api/remote_command.rs b/kube-client/src/api/remote_command.rs index 299dffff9..a0a78572a 100644 --- a/kube-client/src/api/remote_command.rs +++ b/kube-client/src/api/remote_command.rs @@ -28,6 +28,7 @@ type TerminalSizeSender = mpsc::Sender; /// TerminalSize define the size of a terminal #[derive(Debug, Serialize, Deserialize)] #[cfg_attr(docsrs, doc(cfg(feature = "ws")))] +#[serde(rename_all = "PascalCase")] pub struct TerminalSize { /// width of the terminal pub width: u16, diff --git a/kube-client/src/api/subresource.rs b/kube-client/src/api/subresource.rs index 08f6a3078..996f0ea26 100644 --- a/kube-client/src/api/subresource.rs +++ b/kube-client/src/api/subresource.rs @@ -545,14 +545,9 @@ where K: Clone + DeserializeOwned + Execute, { /// Execute a command in a pod - pub async fn exec( - &self, - name: &str, - command: I, - ap: &AttachParams, - ) -> Result + pub async fn exec(&self, name: &str, command: I, ap: &AttachParams) -> Result where - I: IntoIterator, + I: IntoIterator + Debug, T: Into, { let mut req = self diff --git a/kube-core/Cargo.toml b/kube-core/Cargo.toml index af89352b4..eec4cb89c 100644 --- a/kube-core/Cargo.toml +++ b/kube-core/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "kube-core" description = "Kube shared types, traits and client-less behavior" -version = "0.87.2" +version = "0.88.1" authors = [ "clux ", "kazk ", ] edition = "2021" -rust-version = "1.70.0" +rust-version = "1.72.0" license = "Apache-2.0" keywords = ["kubernetes", "apimachinery"] categories = ["api-bindings", "encoding", "parser-implementations"] @@ -36,12 +36,12 @@ chrono = { version = "0.4.19", default-features = false, features = ["clock"] } schemars = { version = "0.8.6", optional = true } [dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false features = [] [dev-dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false features = ["latest"] diff --git a/kube-core/src/lib.rs b/kube-core/src/lib.rs index 20a4dbff6..a0371c169 100644 --- a/kube-core/src/lib.rs +++ b/kube-core/src/lib.rs @@ -43,8 +43,8 @@ pub use request::Request; mod resource; pub use resource::{ - ClusterResourceScope, DynamicResourceScope, NamespaceResourceScope, Resource, ResourceExt, ResourceScope, - SubResourceScope, + api_version_from_group_version, ClusterResourceScope, DynamicResourceScope, NamespaceResourceScope, + Resource, ResourceExt, ResourceScope, SubResourceScope, }; pub mod response; diff --git a/kube-core/src/object.rs b/kube-core/src/object.rs index 2ebd3e634..6494da899 100644 --- a/kube-core/src/object.rs +++ b/kube-core/src/object.rs @@ -16,13 +16,13 @@ use std::borrow::Cow; /// and is generally produced from list/watch/delete collection queries on an [`Resource`](super::Resource). /// /// This is almost equivalent to [`k8s_openapi::List`](k8s_openapi::List), but iterable. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ObjectList where T: Clone, { /// The type fields, always present - #[serde(flatten, default)] + #[serde(flatten, deserialize_with = "deserialize_v1_list_as_default")] pub types: TypeMeta, /// ListMeta - only really used for its `resourceVersion` @@ -38,6 +38,17 @@ where pub items: Vec, } +fn deserialize_v1_list_as_default<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let meta = Option::::deserialize(deserializer)?; + Ok(meta.unwrap_or(TypeMeta { + api_version: "v1".to_owned(), + kind: "List".to_owned(), + })) +} + fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> Result where T: Default + Deserialize<'de>, @@ -394,4 +405,24 @@ mod test { assert!(mypod.status.is_none()); assert!(mypod.spec.is_none()); } + + #[test] + fn k8s_object_list_default_types() { + use k8s_openapi::api::core::v1::Pod; + + let raw_value = serde_json::json!({ + "metadata": { + "resourceVersion": "" + }, + "items": [] + }); + let pod_list: ObjectList = serde_json::from_value(raw_value).unwrap(); + assert_eq!( + TypeMeta { + api_version: "v1".to_owned(), + kind: "List".to_owned(), + }, + pod_list.types, + ); + } } diff --git a/kube-core/src/resource.rs b/kube-core/src/resource.rs index 6ae3500ed..4b159b3bc 100644 --- a/kube-core/src/resource.rs +++ b/kube-core/src/resource.rs @@ -47,14 +47,7 @@ pub trait Resource { fn version(dt: &Self::DynamicType) -> Cow<'_, str>; /// Returns apiVersion of this object fn api_version(dt: &Self::DynamicType) -> Cow<'_, str> { - let group = Self::group(dt); - if group.is_empty() { - return Self::version(dt); - } - let mut group = group.into_owned(); - group.push('/'); - group.push_str(&Self::version(dt)); - group.into() + api_version_from_group_version(Self::group(dt), Self::version(dt)) } /// Returns the plural name of the kind /// @@ -115,6 +108,18 @@ pub trait Resource { } } +/// Helper function that creates the `apiVersion` field from the group and version strings. +pub fn api_version_from_group_version<'a>(group: Cow<'a, str>, version: Cow<'a, str>) -> Cow<'a, str> { + if group.is_empty() { + return version; + } + + let mut output = group; + output.to_mut().push('/'); + output.to_mut().push_str(&version); + output +} + /// Implement accessor trait for any ObjectMeta-using Kubernetes Resource impl Resource for K where diff --git a/kube-derive/Cargo.toml b/kube-derive/Cargo.toml index 4ba1ba02f..0b91b9ff2 100644 --- a/kube-derive/Cargo.toml +++ b/kube-derive/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "kube-derive" description = "Custom derives for the kube kubernetes crates" -version = "0.87.2" +version = "0.88.1" authors = [ "clux ", "kazk ", ] edition = "2021" -rust-version = "1.70.0" +rust-version = "1.72.0" license = "Apache-2.0" repository = "https://github.com/kube-rs/kube" readme = "../README.md" @@ -29,7 +29,7 @@ proc-macro = true serde = { version = "1.0.130", features = ["derive"] } serde_yaml = "0.9.19" kube = { path = "../kube", version = "<1.0.0, >=0.61.0", features = ["derive", "client"] } -k8s-openapi = { version = "0.20.0", default-features = false, features = ["latest"] } +k8s-openapi = { version = "0.21.0", default-features = false, features = ["latest"] } schemars = { version = "0.8.6", features = ["chrono"] } chrono = { version = "0.4.19", default-features = false } trybuild = "1.0.48" diff --git a/kube-derive/README.md b/kube-derive/README.md index f675518b1..6cbd9362b 100644 --- a/kube-derive/README.md +++ b/kube-derive/README.md @@ -6,7 +6,7 @@ Add the `derive` feature to `kube`: ```toml [dependencies] -kube = { version = "0.87.2", feature = ["derive"] } +kube = { version = "0.88.1", feature = ["derive"] } ``` ## Usage diff --git a/kube-runtime/Cargo.toml b/kube-runtime/Cargo.toml index d06ae6ece..907de19dc 100644 --- a/kube-runtime/Cargo.toml +++ b/kube-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kube-runtime" -version = "0.87.2" +version = "0.88.1" description = "Kubernetes futures controller runtime" authors = [ "Natalie Klestrup Röijezon ", @@ -11,7 +11,7 @@ repository = "https://github.com/kube-rs/kube" readme = "../README.md" keywords = ["kubernetes", "runtime", "reflector", "watcher", "controller"] categories = ["web-programming::http-client", "caching", "network-programming"] -rust-version = "1.70.0" +rust-version = "1.72.0" edition = "2021" [features] @@ -28,7 +28,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] futures = "0.3.17" -kube-client = { path = "../kube-client", version = "=0.87.2", default-features = false, features = ["jsonpatch", "client"] } +kube-client = { path = "../kube-client", version = "=0.88.1", default-features = false, features = ["jsonpatch", "client"] } derivative = "2.1.1" serde = "1.0.130" smallvec = "1.7.0" @@ -46,7 +46,7 @@ async-trait = "0.1.64" hashbrown = "0.14.0" [dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false [dev-dependencies] @@ -58,6 +58,6 @@ schemars = "0.8.6" tracing-subscriber = "0.3.17" [dev-dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false features = ["latest"] diff --git a/kube-runtime/src/events.rs b/kube-runtime/src/events.rs index 2db94f3f5..cf9f53218 100644 --- a/kube-runtime/src/events.rs +++ b/kube-runtime/src/events.rs @@ -20,8 +20,7 @@ pub struct Event { /// The short reason explaining why the `action` was taken. /// - /// This must be at most 128 characters, and is often PascalCased. Shows up in `kubectl describe` as `Reason`. - /// Usually denoted + /// This must be at most 128 characters, generally in `PascalCase`. Shows up in `kubectl describe` as `Reason`. pub reason: String, /// A optional description of the status of the `action`. diff --git a/kube-runtime/src/reflector/mod.rs b/kube-runtime/src/reflector/mod.rs index d0a724b53..04403202d 100644 --- a/kube-runtime/src/reflector/mod.rs +++ b/kube-runtime/src/reflector/mod.rs @@ -3,10 +3,9 @@ mod object_ref; pub mod store; -pub use self::object_ref::{Extra as ObjectRefExtra, ObjectRef}; +pub use self::object_ref::{Extra as ObjectRefExtra, Lookup, ObjectRef}; use crate::watcher; use futures::{Stream, TryStreamExt}; -use kube_client::Resource; use std::hash::Hash; pub use store::{store, Store}; @@ -91,7 +90,7 @@ pub use store::{store, Store}; /// Additionally, only `labels`, `annotations` and `managed_fields` are safe to drop from `ObjectMeta`. pub fn reflector(mut writer: store::Writer, stream: W) -> impl Stream where - K: Resource + Clone, + K: Lookup + Clone, K::DynamicType: Eq + Hash + Clone, W: Stream>>, { diff --git a/kube-runtime/src/reflector/object_ref.rs b/kube-runtime/src/reflector/object_ref.rs index cc2049ca1..e89094fd4 100644 --- a/kube-runtime/src/reflector/object_ref.rs +++ b/kube-runtime/src/reflector/object_ref.rs @@ -2,14 +2,101 @@ use derivative::Derivative; use k8s_openapi::{api::core::v1::ObjectReference, apimachinery::pkg::apis::meta::v1::OwnerReference}; use kube_client::{ api::{DynamicObject, Resource}, - core::ObjectMeta, - ResourceExt, + core::{api_version_from_group_version, ObjectMeta}, }; use std::{ + borrow::Cow, fmt::{Debug, Display}, hash::Hash, }; +/// Minimal lookup behaviour needed by a [reflector store](super::Store). +/// +/// This trait is blanket-implemented for all [`Resource`] objects. +pub trait Lookup { + /// Type information for types that do not know their resource information at compile time. + /// This is equivalent to [`Resource::DynamicType`]. + type DynamicType; + + /// The [kind](Resource::kind) for this object. + fn kind(dyntype: &Self::DynamicType) -> Cow<'_, str>; + + /// The [group](Resource::group) for this object. + fn group(dyntype: &Self::DynamicType) -> Cow<'_, str>; + + /// The [version](Resource::version) for this object. + fn version(dyntype: &Self::DynamicType) -> Cow<'_, str>; + + /// The [apiVersion](Resource::_version) for this object. + fn api_version(dyntype: &Self::DynamicType) -> Cow<'_, str> { + api_version_from_group_version(Self::group(dyntype), Self::version(dyntype)) + } + + /// The [plural](Resource::plural) for this object. + fn plural(dyntype: &Self::DynamicType) -> Cow<'_, str>; + + /// The [name](ObjectMeta#structfield.name) of the object. + fn name(&self) -> Option>; + + /// The [namespace](ObjectMeta#structfield.namespace) of the object. + fn namespace(&self) -> Option>; + + /// The [resource version](ObjectMeta#structfield.resource_version) of the object. + fn resource_version(&self) -> Option>; + + /// The [UID](ObjectMeta#structfield.uid) of the object. + fn uid(&self) -> Option>; + + /// Constructs an [`ObjectRef`] for this object. + fn to_object_ref(&self, dyntype: Self::DynamicType) -> ObjectRef { + ObjectRef { + dyntype, + name: self.name().expect(".metadata.name missing").into_owned(), + namespace: self.namespace().map(Cow::into_owned), + extra: Extra { + resource_version: self.resource_version().map(Cow::into_owned), + uid: self.uid().map(Cow::into_owned), + }, + } + } +} + +impl Lookup for K { + type DynamicType = K::DynamicType; + + fn kind(dyntype: &Self::DynamicType) -> Cow<'_, str> { + K::kind(dyntype) + } + + fn version(dyntype: &Self::DynamicType) -> Cow<'_, str> { + K::version(dyntype) + } + + fn group(dyntype: &Self::DynamicType) -> Cow<'_, str> { + K::group(dyntype) + } + + fn plural(dyntype: &Self::DynamicType) -> Cow<'_, str> { + K::plural(dyntype) + } + + fn name(&self) -> Option> { + self.meta().name.as_deref().map(Cow::Borrowed) + } + + fn namespace(&self) -> Option> { + self.meta().namespace.as_deref().map(Cow::Borrowed) + } + + fn resource_version(&self) -> Option> { + self.meta().resource_version.as_deref().map(Cow::Borrowed) + } + + fn uid(&self) -> Option> { + self.meta().uid.as_deref().map(Cow::Borrowed) + } +} + #[derive(Derivative)] #[derivative( Debug(bound = "K::DynamicType: Debug"), @@ -33,7 +120,7 @@ use std::{ /// ); /// ``` #[non_exhaustive] -pub struct ObjectRef { +pub struct ObjectRef { pub dyntype: K::DynamicType, /// The name of the object pub name: String, @@ -69,7 +156,7 @@ pub struct Extra { pub uid: Option, } -impl ObjectRef +impl ObjectRef where K::DynamicType: Default, { @@ -81,13 +168,13 @@ where #[must_use] pub fn from_obj(obj: &K) -> Self where - K: Resource, + K: Lookup, { - Self::from_obj_with(obj, Default::default()) + obj.to_object_ref(Default::default()) } } -impl ObjectRef { +impl ObjectRef { #[must_use] pub fn new_with(name: &str, dyntype: K::DynamicType) -> Self { Self { @@ -108,15 +195,9 @@ impl ObjectRef { #[must_use] pub fn from_obj_with(obj: &K, dyntype: K::DynamicType) -> Self where - K: Resource, + K: Lookup, { - let meta = obj.meta(); - Self { - dyntype, - name: obj.name_unchecked(), - namespace: meta.namespace.clone(), - extra: Extra::from_obj_meta(meta), - } + obj.to_object_ref(dyntype) } /// Create an `ObjectRef` from an `OwnerReference` @@ -148,7 +229,7 @@ impl ObjectRef { /// Note that no checking is done on whether this conversion makes sense. For example, every `Service` /// has a corresponding `Endpoints`, but it wouldn't make sense to convert a `Pod` into a `Deployment`. #[must_use] - pub fn into_kind_unchecked(self, dt2: K2::DynamicType) -> ObjectRef { + pub fn into_kind_unchecked(self, dt2: K2::DynamicType) -> ObjectRef { ObjectRef { dyntype: dt2, name: self.name, @@ -159,7 +240,13 @@ impl ObjectRef { pub fn erase(self) -> ObjectRef { ObjectRef { - dyntype: kube_client::api::ApiResource::erase::(&self.dyntype), + dyntype: kube_client::api::ApiResource { + group: K::group(&self.dyntype).to_string(), + version: K::version(&self.dyntype).to_string(), + api_version: K::api_version(&self.dyntype).to_string(), + kind: K::kind(&self.dyntype).to_string(), + plural: K::plural(&self.dyntype).to_string(), + }, name: self.name, namespace: self.namespace, extra: self.extra, @@ -167,7 +254,7 @@ impl ObjectRef { } } -impl From> for ObjectReference { +impl From> for ObjectReference { fn from(val: ObjectRef) -> Self { let ObjectRef { dyntype: dt, @@ -190,7 +277,7 @@ impl From> for ObjectReference { } } -impl Display for ObjectRef { +impl Display for ObjectRef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -207,15 +294,6 @@ impl Display for ObjectRef { } } -impl Extra { - fn from_obj_meta(obj_meta: &ObjectMeta) -> Self { - Self { - resource_version: obj_meta.resource_version.clone(), - uid: obj_meta.uid.clone(), - } - } -} - #[cfg(test)] mod tests { use std::{ diff --git a/kube-runtime/src/reflector/store.rs b/kube-runtime/src/reflector/store.rs index 9085ab69b..8fc51b638 100644 --- a/kube-runtime/src/reflector/store.rs +++ b/kube-runtime/src/reflector/store.rs @@ -1,11 +1,10 @@ -use super::ObjectRef; +use super::{Lookup, ObjectRef}; use crate::{ utils::delayed_init::{self, DelayedInit}, watcher, }; use ahash::AHashMap; use derivative::Derivative; -use kube_client::Resource; use parking_lot::RwLock; use std::{fmt::Debug, hash::Hash, sync::Arc}; use thiserror::Error; @@ -17,7 +16,7 @@ type Cache = Arc, Arc>>>; /// This is exclusive since it's not safe to share a single `Store` between multiple reflectors. /// In particular, `Restarted` events will clobber the state of other connected reflectors. #[derive(Debug)] -pub struct Writer +pub struct Writer where K::DynamicType: Eq + Hash, { @@ -27,7 +26,7 @@ where ready_rx: Arc>, } -impl Writer +impl Writer where K::DynamicType: Eq + Hash + Clone, { @@ -61,23 +60,18 @@ where pub fn apply_watcher_event(&mut self, event: &watcher::Event) { match event { watcher::Event::Applied(obj) => { - let key = ObjectRef::from_obj_with(obj, self.dyntype.clone()); + let key = obj.to_object_ref(self.dyntype.clone()); let obj = Arc::new(obj.clone()); self.store.write().insert(key, obj); } watcher::Event::Deleted(obj) => { - let key = ObjectRef::from_obj_with(obj, self.dyntype.clone()); + let key = obj.to_object_ref(self.dyntype.clone()); self.store.write().remove(&key); } watcher::Event::Restarted(new_objs) => { let new_objs = new_objs .iter() - .map(|obj| { - ( - ObjectRef::from_obj_with(obj, self.dyntype.clone()), - Arc::new(obj.clone()), - ) - }) + .map(|obj| (obj.to_object_ref(self.dyntype.clone()), Arc::new(obj.clone()))) .collect::>(); *self.store.write() = new_objs; } @@ -91,7 +85,7 @@ where } impl Default for Writer where - K: Resource + Clone + 'static, + K: Lookup + Clone + 'static, K::DynamicType: Default + Eq + Hash + Clone, { fn default() -> Self { @@ -107,7 +101,7 @@ where /// use `Writer::as_reader()` instead. #[derive(Derivative)] #[derivative(Debug(bound = "K: Debug, K::DynamicType: Debug"), Clone)] -pub struct Store +pub struct Store where K::DynamicType: Hash + Eq, { @@ -119,7 +113,7 @@ where #[error("writer was dropped before store became ready")] pub struct WriterDropped(delayed_init::InitDropped); -impl Store +impl Store where K::DynamicType: Eq + Hash + Clone, { @@ -201,7 +195,7 @@ where #[must_use] pub fn store() -> (Store, Writer) where - K: Resource + Clone + 'static, + K: Lookup + Clone + 'static, K::DynamicType: Eq + Hash + Clone + Default, { let w = Writer::::default(); diff --git a/kube-runtime/src/watcher.rs b/kube-runtime/src/watcher.rs index 40b8d3a11..f1ee886ab 100644 --- a/kube-runtime/src/watcher.rs +++ b/kube-runtime/src/watcher.rs @@ -206,7 +206,7 @@ pub enum InitialListStrategy { /// List first, then watch from given resouce version /// /// This is the old and default way of watching. The watcher will do a paginated list call first before watching. - /// When using this mode, you can configure the page_size on the watcher. + /// When using this mode, you can configure the `page_size` on the watcher. #[default] ListWatch, /// Kubernetes 1.27 Streaming Lists @@ -245,10 +245,10 @@ pub struct Config { /// Control how the watcher fetches the initial list of objects. /// - /// ListWatch: The watcher will fetch the initial list of objects using a list call. - /// StreamingList: The watcher will fetch the initial list of objects using a watch call. + /// - `ListWatch`: The watcher will fetch the initial list of objects using a list call. + /// - `StreamingList`: The watcher will fetch the initial list of objects using a watch call. /// - /// StreamingList is more efficient than ListWatch, but it requires the server to support + /// `StreamingList` is more efficient than `ListWatch`, but it requires the server to support /// streaming list bookmarks (opt-in feature gate in Kubernetes 1.27). /// /// See [upstream documentation on streaming lists](https://kubernetes.io/docs/reference/using-api/api-concepts/#streaming-lists), diff --git a/kube/Cargo.toml b/kube/Cargo.toml index 509ef5b34..9bd2df168 100644 --- a/kube/Cargo.toml +++ b/kube/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kube" -version = "0.87.2" +version = "0.88.1" description = "Kubernetes client and async controller runtime" authors = [ "clux ", @@ -12,7 +12,7 @@ repository = "https://github.com/kube-rs/kube" readme = "../README.md" keywords = ["kubernetes", "client", "runtime", "cncf"] categories = ["network-programming", "caching", "api-bindings", "configuration", "encoding"] -rust-version = "1.70.0" +rust-version = "1.72.0" edition = "2021" [features] @@ -45,15 +45,15 @@ features = ["client", "rustls-tls", "openssl-tls", "derive", "ws", "oauth", "jso rustdoc-args = ["--cfg", "docsrs"] [dependencies] -kube-derive = { path = "../kube-derive", version = "=0.87.2", optional = true } -kube-core = { path = "../kube-core", version = "=0.87.2" } -kube-client = { path = "../kube-client", version = "=0.87.2", default-features = false, optional = true } -kube-runtime = { path = "../kube-runtime", version = "=0.87.2", optional = true} +kube-derive = { path = "../kube-derive", version = "=0.88.1", optional = true } +kube-core = { path = "../kube-core", version = "=0.88.1" } +kube-client = { path = "../kube-client", version = "=0.88.1", default-features = false, optional = true } +kube-runtime = { path = "../kube-runtime", version = "=0.88.1", optional = true} # Not used directly, but required by resolver 2.0 to ensure that the k8s-openapi dependency # is considered part of the "deps" graph rather than just the "dev-deps" graph [dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false [dev-dependencies] @@ -68,6 +68,6 @@ tower-test = "0.4.0" anyhow = "1.0.71" [dev-dependencies.k8s-openapi] -version = "0.20.0" +version = "0.21.0" default-features = false features = ["latest"] diff --git a/release.toml b/release.toml index 5f638150d..93c09d830 100644 --- a/release.toml +++ b/release.toml @@ -1,6 +1,7 @@ # Release process :: cargo-release >= 0.18.3 # # Dependencies: https://kube.rs/tools +# Process: https://kube.rs/release-process/ # # 0. (optional) cargo release minor ; verify readme + changelog bumped; then git reset --hard # 1. PUBLISH_GRACE_SLEEP=20 cargo release minor --execute @@ -8,6 +9,7 @@ # - on partial cargo publish failures with unexpected build errors; yank partials and fix issues in a PR before retrying # - on cargo-release issues waiting for crates.io: resume publish in given order manually, cd into next dirs and publish in sequence with cargo publish --features=k8s-openapi/latest # - after publish; check consolidated commit, amend if needed, then create a manual signed tag without v prefix +# 2. After publishing the release run ./scripts/release-afterdoc.sh VERSION # Reference # https://github.com/crate-ci/cargo-release/blob/master/docs/reference.md