Skip to content

Commit

Permalink
Merge pull request #393 from clux/eviction
Browse files Browse the repository at this point in the history
add eviction subresource - for #127
  • Loading branch information
clux authored Feb 7, 2021
2 parents 5dc29b4 + 3380bb8 commit d1b4925
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 2 deletions.
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ path = "pod_attach.rs"
name = "pod_exec"
path = "pod_exec.rs"

[[example]]
name = "pod_evict"
path = "pod_evict.rs"

[[example]]
name = "pod_shell"
path = "pod_shell.rs"
Expand Down
65 changes: 65 additions & 0 deletions examples/pod_evict.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#[macro_use] extern crate log;
use futures::{StreamExt, TryStreamExt};
use k8s_openapi::api::core::v1::Pod;
use serde_json::json;

use kube::{
api::{Api, EvictParams, ListParams, Meta, PostParams, WatchEvent},
Client,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
std::env::set_var("RUST_LOG", "info,kube=debug");
env_logger::init();
let client = Client::try_default().await?;
let namespace = std::env::var("NAMESPACE").unwrap_or("default".into());

// Create a Job
let pod_name = "empty-pod";
let empty_pod = serde_json::from_value(json!({
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": pod_name,
},
"spec": {
"containers": [{
"name": "empty",
"image": "alpine:latest",
"command": ["tail", "-f", "/dev/null"]
}],
}
}))?;

let pods: Api<Pod> = Api::namespaced(client, &namespace);
let pp = PostParams::default();
pods.create(&pp, &empty_pod).await?;

// Wait until the pod is running, although it's not necessary
let lp = ListParams::default()
.fields("metadata.name=empty-pod")
.timeout(10);
let mut stream = pods.watch(&lp, "0").await?.boxed();
while let Some(status) = stream.try_next().await? {
match status {
WatchEvent::Added(o) => {
info!("Added {}", Meta::name(&o));
}
WatchEvent::Modified(o) => {
let s = o.status.as_ref().expect("status exists on pod");
if s.phase.clone().unwrap_or_default() == "Running" {
info!("Ready to evict to {}", Meta::name(&o));
break;
}
}
_ => {}
}
}

// Clean up the old job record..
let ep = EvictParams::default();
let eres = pods.evict(pod_name, &ep).await?;
println!("{:?}", eres);
Ok(())
}
2 changes: 1 addition & 1 deletion kube/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub use dynamic::DynamicResource;
mod subresource;
#[cfg(feature = "ws")]
pub use subresource::{AttachParams, AttachableObject, ExecutingObject};
pub use subresource::{LogParams, LoggingObject, ScaleSpec, ScaleStatus};
pub use subresource::{EvictParams, Evictable, LogParams, LoggingObject, ScaleSpec, ScaleStatus};

pub(crate) mod object;
pub use self::object::{Object, ObjectList, WatchEvent};
Expand Down
64 changes: 63 additions & 1 deletion kube/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use futures::Stream;
use serde::de::DeserializeOwned;

use crate::{
api::{Api, Patch, PatchParams, PostParams, Resource},
api::{Api, DeleteParams, Patch, PatchParams, PostParams, Resource},
client::Status,
Error, Result,
};

Expand Down Expand Up @@ -221,6 +222,67 @@ where
}
}

// ----------------------------------------------------------------------------
// Eviction subresource
// ----------------------------------------------------------------------------

/// Params for evictable objects
#[derive(Default, Clone)]
pub struct EvictParams {
/// How the eviction should occur
pub delete_options: Option<DeleteParams>,
/// How the http post should occur
pub post_options: PostParams,
}

impl Resource {
/// Create an eviction
pub fn evict(&self, name: &str, ep: &EvictParams) -> Result<http::Request<Vec<u8>>> {
let base_url = self.make_url() + "/" + name + "/" + "eviction?";
// This is technically identical to Resource::create, but different url
let pp = &ep.post_options;
pp.validate()?;
let mut qp = url::form_urlencoded::Serializer::new(base_url);
if pp.dry_run {
qp.append_pair("dryRun", "All");
}
let urlstr = qp.finish();
// eviction body parameters are awkward, need metadata with name
let data = serde_json::to_vec(&serde_json::json!({
"delete_options": ep.delete_options,
"metadata": { "name": name }
}))?;
let req = http::Request::post(urlstr);
req.body(data).map_err(Error::HttpError)
}
}

#[test]
fn evict_path() {
use crate::api::Resource;
use k8s_openapi::api::core::v1 as corev1;
let r = Resource::namespaced::<corev1::Pod>("ns");
let ep = EvictParams::default();
let req = r.evict("foo", &ep).unwrap();
assert_eq!(req.uri(), "/api/v1/namespaces/ns/pods/foo/eviction?");
}

/// Marker trait for objects that can be evicted
pub trait Evictable {}

impl Evictable for k8s_openapi::api::core::v1::Pod {}

impl<K> Api<K>
where
K: Clone + DeserializeOwned + Evictable,
{
/// Create an eviction
pub async fn evict(&self, name: &str, ep: &EvictParams) -> Result<Status> {
let req = self.resource.evict(name, ep)?;
self.client.request::<Status>(req).await
}
}

// ----------------------------------------------------------------------------
// Attach subresource
// ----------------------------------------------------------------------------
Expand Down

0 comments on commit d1b4925

Please sign in to comment.