From 7dc2e33c43a52cbcd6787f5d727601c6e8deb5df Mon Sep 17 00:00:00 2001 From: Krishna Ketan Rai Date: Sun, 20 Jul 2025 13:40:45 +0530 Subject: [PATCH 1/2] add resize subresource for Pods Implement the resize subresource for Kubernetes Pods as specified in GitHub issue #1793. The resize subresource allows changing resource requirements (CPU/memory limits and requests) for containers in running pods, available in Kubernetes 1.33+. Changes: - Add Request::resize method in kube-core for PATCH requests to /resize endpoint - Add Resize marker trait and Api::resize method in kube-client with k8s_if_ge_1_33 version gating - Include test for resize URL construction - Add pod_resize example demonstrating usage with resource limit modifications Closes #1793 Signed-off-by: Krishna Ketan Rai --- examples/Cargo.toml | 5 ++ examples/pod_resize.rs | 69 +++++++++++++++++++++++++++ kube-client/src/api/subresource.rs | 75 ++++++++++++++++++++++++++++++ kube-core/src/subresource.rs | 17 +++++++ 4 files changed, 166 insertions(+) create mode 100644 examples/pod_resize.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d16979159..d921d96bf 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -236,3 +236,8 @@ path = "secret_syncer.rs" name = "pod_shell_crossterm" path = "pod_shell_crossterm.rs" required-features = ["ws"] + +[[example]] +name = "pod_resize" +path = "pod_resize.rs" +required-features = ["latest"] diff --git a/examples/pod_resize.rs b/examples/pod_resize.rs new file mode 100644 index 000000000..4352f74d4 --- /dev/null +++ b/examples/pod_resize.rs @@ -0,0 +1,69 @@ +use k8s_openapi::api::core::v1::Pod; +use kube::{ + api::{Api, PostParams, ResourceExt}, + Client, Result, +}; + +#[tokio::main] +async fn main() -> Result<()> { + unsafe { std::env::set_var("RUST_LOG", "info,kube=debug"); } + tracing_subscriber::fmt::init(); + let client = Client::try_default().await?; + + let name = std::env::args() + .nth(1) + .expect("Usage: cargo run --bin pod_resize "); + + let pods: Api = Api::default_namespaced(client); + + // Resize is only available in Kubernetes 1.33+ + k8s_openapi::k8s_if_ge_1_33! { + tracing::info!("Resizing pod {}", name); + + // Get the current pod + let mut pod = pods.get(&name).await?; + tracing::info!("Current pod: {}", pod.name_any()); + + // Modify the pod's resource requirements + if let Some(ref mut spec) = pod.spec { + if let Some(container) = spec.containers.get_mut(0) { + // Example: Update CPU and memory limits + if container.resources.is_none() { + container.resources = Some(Default::default()); + } + if let Some(ref mut resources) = container.resources { + use k8s_openapi::apimachinery::pkg::api::resource::Quantity; + use std::collections::BTreeMap; + + // Set new resource limits + let mut limits = BTreeMap::new(); + limits.insert("cpu".to_string(), Quantity("500m".to_string())); + limits.insert("memory".to_string(), Quantity("256Mi".to_string())); + resources.limits = Some(limits); + + // Set new resource requests + let mut requests = BTreeMap::new(); + requests.insert("cpu".to_string(), Quantity("250m".to_string())); + requests.insert("memory".to_string(), Quantity("128Mi".to_string())); + resources.requests = Some(requests); + } + } + } + + // Apply the resize + let pp = PostParams::default(); + let updated_pod = pods.resize(&name, &pp, &pod).await?; + tracing::info!("Pod resized successfully: {}", updated_pod.name_any()); + + if let Some(ref spec) = updated_pod.spec { + if let Some(container) = spec.containers.get(0) { + if let Some(ref resources) = container.resources { + tracing::info!("New limits: {:?}", resources.limits); + tracing::info!("New requests: {:?}", resources.requests); + } + } + } + } + + Ok(()) +} diff --git a/kube-client/src/api/subresource.rs b/kube-client/src/api/subresource.rs index cc6b2bdf1..64887ef41 100644 --- a/kube-client/src/api/subresource.rs +++ b/kube-client/src/api/subresource.rs @@ -610,3 +610,78 @@ where Ok(Portforwarder::new(connection.into_stream(), ports)) } } + +// ---------------------------------------------------------------------------- +// Resize subresource +// ---------------------------------------------------------------------------- + +#[test] +fn resize_path() { + k8s_openapi::k8s_if_ge_1_33! { + use kube_core::{request::Request, Resource, params::PostParams}; + use k8s_openapi::api::core::v1 as corev1; + let pp = PostParams::default(); + let url = corev1::Pod::url_path(&(), Some("ns")); + let req = Request::new(url).resize("foo", vec![], &pp).unwrap(); + assert_eq!(req.uri(), "/api/v1/namespaces/ns/pods/foo/resize?"); + } +} + +/// Marker trait for objects that can be resized +/// +/// See [`Api::resize`] for usage +pub trait Resize {} + +k8s_openapi::k8s_if_ge_1_33! { + impl Resize for k8s_openapi::api::core::v1::Pod {} +} + +impl Api +where + K: DeserializeOwned + Resize, +{ + /// Resize a resource + /// + /// This works similarly to [`Api::replace`] but uses the resize subresource. + /// Takes a full Pod object to specify the new resource requirements. + /// + /// ```no_run + /// use kube::api::{Api, PostParams}; + /// use k8s_openapi::api::core::v1::Pod; + /// # async fn wrapper() -> Result<(), Box> { + /// # let client = kube::Client::try_default().await?; + /// let pods: Api = Api::namespaced(client, "apps"); + /// let mut pod = pods.get("mypod").await?; + /// + /// // Modify the pod's resource requirements + /// if let Some(ref mut spec) = pod.spec { + /// if let Some(ref mut containers) = spec.containers.first_mut() { + /// if let Some(ref mut resources) = containers.resources { + /// // Update CPU/memory limits + /// } + /// } + /// } + /// + /// let pp = PostParams::default(); + /// let resized_pod = pods.resize("mypod", &pp, &pod).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn resize(&self, name: &str, pp: &PostParams, data: &K) -> Result + where + K: serde::Serialize, + { + let mut req = self + .request + .resize( + name, + serde_json::to_vec(data).map_err(Error::SerdeError)?, + pp, + ) + .map_err(Error::BuildRequest)?; + req.extensions_mut().insert("resize"); + self.client.request::(req).await + } +} + +// ---------------------------------------------------------------------------- diff --git a/kube-core/src/subresource.rs b/kube-core/src/subresource.rs index d4dddce4e..b62e37d75 100644 --- a/kube-core/src/subresource.rs +++ b/kube-core/src/subresource.rs @@ -405,6 +405,23 @@ impl Request { } } +// ---------------------------------------------------------------------------- +// Resize subresource +// ---------------------------------------------------------------------------- + +impl Request { + /// Resize a pod's resources + pub fn resize(&self, name: &str, data: Vec, pp: &PostParams) -> Result>, Error> { + let target = format!("{}/{}/resize?", self.url_path, name); + pp.validate()?; + let mut qp = form_urlencoded::Serializer::new(target); + pp.populate_qp(&mut qp); + let urlstr = qp.finish(); + let req = http::Request::patch(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME); + req.body(data).map_err(Error::BuildRequest) + } +} + // ---------------------------------------------------------------------------- // tests // ---------------------------------------------------------------------------- From 8b0fccd931859b890a1dc1c2b2892f5e708f1157 Mon Sep 17 00:00:00 2001 From: Krishna Ketan Rai Date: Sun, 20 Jul 2025 14:18:36 +0530 Subject: [PATCH 2/2] Add resize subresource support for Pods Implements the resize subresource for Kubernetes Pods following the same patterns as other subresources in kube-rs. Changes: - Add Request::resize method in kube-core for PATCH requests to /resize - Add Resize trait and Api::resize method in kube-client (gated on k8s_if_ge_1_33) - Add resize URL construction test - Add pod_resize example demonstrating usage The resize subresource allows updating pod resource requests/limits and is available in Kubernetes 1.33+. Fixes #1793 Signed-off-by: Krishna Ketan Rai --- examples/pod_resize.rs | 6 ++++-- kube-core/src/subresource.rs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/examples/pod_resize.rs b/examples/pod_resize.rs index 4352f74d4..209d7bb44 100644 --- a/examples/pod_resize.rs +++ b/examples/pod_resize.rs @@ -1,12 +1,14 @@ use k8s_openapi::api::core::v1::Pod; use kube::{ - api::{Api, PostParams, ResourceExt}, Client, Result, + api::{Api, PostParams, ResourceExt}, }; #[tokio::main] async fn main() -> Result<()> { - unsafe { std::env::set_var("RUST_LOG", "info,kube=debug"); } + unsafe { + std::env::set_var("RUST_LOG", "info,kube=debug"); + } tracing_subscriber::fmt::init(); let client = Client::try_default().await?; diff --git a/kube-core/src/subresource.rs b/kube-core/src/subresource.rs index b62e37d75..29fd09f05 100644 --- a/kube-core/src/subresource.rs +++ b/kube-core/src/subresource.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use crate::{ params::{DeleteParams, PostParams}, - request::{Error, Request, JSON_MIME}, + request::{Error, JSON_MIME, Request}, }; pub use k8s_openapi::api::autoscaling::v1::{Scale, ScaleSpec, ScaleStatus}; @@ -411,7 +411,12 @@ impl Request { impl Request { /// Resize a pod's resources - pub fn resize(&self, name: &str, data: Vec, pp: &PostParams) -> Result>, Error> { + pub fn resize( + &self, + name: &str, + data: Vec, + pp: &PostParams, + ) -> Result>, Error> { let target = format!("{}/{}/resize?", self.url_path, name); pp.validate()?; let mut qp = form_urlencoded::Serializer::new(target); @@ -451,7 +456,10 @@ mod test { timestamps: true, }; let req = Request::new(url).logs("mypod", &lp).unwrap(); - assert_eq!(req.uri(), "/api/v1/namespaces/ns/pods/mypod/log?&container=nginx&follow=true&limitBytes=10485760&pretty=true&previous=true&sinceSeconds=3600&tailLines=4096×tamps=true"); + assert_eq!( + req.uri(), + "/api/v1/namespaces/ns/pods/mypod/log?&container=nginx&follow=true&limitBytes=10485760&pretty=true&previous=true&sinceSeconds=3600&tailLines=4096×tamps=true" + ); } #[test]