Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
71 changes: 71 additions & 0 deletions examples/pod_resize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use k8s_openapi::api::core::v1::Pod;
use kube::{
Client, Result,
api::{Api, PostParams, ResourceExt},
};

#[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 <pod-name>");

let pods: Api<Pod> = Api::default_namespaced(client);

// Resize is only available in Kubernetes 1.33+
k8s_openapi::k8s_if_ge_1_33! {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need to gate the example on 1_33 because the we have latest as a required feature

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(())
}
75 changes: 75 additions & 0 deletions kube-client/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<K> Api<K>
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.
Comment on lines +645 to +646
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should link to limitations

///
/// ```no_run
/// use kube::api::{Api, PostParams};
/// use k8s_openapi::api::core::v1::Pod;
/// # async fn wrapper() -> Result<(), Box<dyn std::error::Error>> {
/// # let client = kube::Client::try_default().await?;
/// let pods: Api<Pod> = 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<K>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

misses support for the other verbs get/patch. need to rename this to replace_resize to make room for patch_resize and get_resize

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::<K>(req).await
}
}

// ----------------------------------------------------------------------------
29 changes: 27 additions & 2 deletions kube-core/src/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -405,6 +405,28 @@ impl Request {
}
}

// ----------------------------------------------------------------------------
// Resize subresource
// ----------------------------------------------------------------------------

impl Request {
/// Resize a pod's resources
pub fn resize(
&self,
name: &str,
data: Vec<u8>,
pp: &PostParams,
) -> Result<http::Request<Vec<u8>>, 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
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -434,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&timestamps=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&timestamps=true"
);
}

#[test]
Expand Down
Loading