Skip to content

Commit d1b4925

Browse files
authored
Merge pull request #393 from clux/eviction
add eviction subresource - for #127
2 parents 5dc29b4 + 3380bb8 commit d1b4925

File tree

4 files changed

+133
-2
lines changed

4 files changed

+133
-2
lines changed

examples/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ path = "pod_attach.rs"
109109
name = "pod_exec"
110110
path = "pod_exec.rs"
111111

112+
[[example]]
113+
name = "pod_evict"
114+
path = "pod_evict.rs"
115+
112116
[[example]]
113117
name = "pod_shell"
114118
path = "pod_shell.rs"

examples/pod_evict.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#[macro_use] extern crate log;
2+
use futures::{StreamExt, TryStreamExt};
3+
use k8s_openapi::api::core::v1::Pod;
4+
use serde_json::json;
5+
6+
use kube::{
7+
api::{Api, EvictParams, ListParams, Meta, PostParams, WatchEvent},
8+
Client,
9+
};
10+
11+
#[tokio::main]
12+
async fn main() -> anyhow::Result<()> {
13+
std::env::set_var("RUST_LOG", "info,kube=debug");
14+
env_logger::init();
15+
let client = Client::try_default().await?;
16+
let namespace = std::env::var("NAMESPACE").unwrap_or("default".into());
17+
18+
// Create a Job
19+
let pod_name = "empty-pod";
20+
let empty_pod = serde_json::from_value(json!({
21+
"apiVersion": "v1",
22+
"kind": "Pod",
23+
"metadata": {
24+
"name": pod_name,
25+
},
26+
"spec": {
27+
"containers": [{
28+
"name": "empty",
29+
"image": "alpine:latest",
30+
"command": ["tail", "-f", "/dev/null"]
31+
}],
32+
}
33+
}))?;
34+
35+
let pods: Api<Pod> = Api::namespaced(client, &namespace);
36+
let pp = PostParams::default();
37+
pods.create(&pp, &empty_pod).await?;
38+
39+
// Wait until the pod is running, although it's not necessary
40+
let lp = ListParams::default()
41+
.fields("metadata.name=empty-pod")
42+
.timeout(10);
43+
let mut stream = pods.watch(&lp, "0").await?.boxed();
44+
while let Some(status) = stream.try_next().await? {
45+
match status {
46+
WatchEvent::Added(o) => {
47+
info!("Added {}", Meta::name(&o));
48+
}
49+
WatchEvent::Modified(o) => {
50+
let s = o.status.as_ref().expect("status exists on pod");
51+
if s.phase.clone().unwrap_or_default() == "Running" {
52+
info!("Ready to evict to {}", Meta::name(&o));
53+
break;
54+
}
55+
}
56+
_ => {}
57+
}
58+
}
59+
60+
// Clean up the old job record..
61+
let ep = EvictParams::default();
62+
let eres = pods.evict(pod_name, &ep).await?;
63+
println!("{:?}", eres);
64+
Ok(())
65+
}

kube/src/api/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub use dynamic::DynamicResource;
2626
mod subresource;
2727
#[cfg(feature = "ws")]
2828
pub use subresource::{AttachParams, AttachableObject, ExecutingObject};
29-
pub use subresource::{LogParams, LoggingObject, ScaleSpec, ScaleStatus};
29+
pub use subresource::{EvictParams, Evictable, LogParams, LoggingObject, ScaleSpec, ScaleStatus};
3030

3131
pub(crate) mod object;
3232
pub use self::object::{Object, ObjectList, WatchEvent};

kube/src/api/subresource.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use futures::Stream;
33
use serde::de::DeserializeOwned;
44

55
use crate::{
6-
api::{Api, Patch, PatchParams, PostParams, Resource},
6+
api::{Api, DeleteParams, Patch, PatchParams, PostParams, Resource},
7+
client::Status,
78
Error, Result,
89
};
910

@@ -221,6 +222,67 @@ where
221222
}
222223
}
223224

225+
// ----------------------------------------------------------------------------
226+
// Eviction subresource
227+
// ----------------------------------------------------------------------------
228+
229+
/// Params for evictable objects
230+
#[derive(Default, Clone)]
231+
pub struct EvictParams {
232+
/// How the eviction should occur
233+
pub delete_options: Option<DeleteParams>,
234+
/// How the http post should occur
235+
pub post_options: PostParams,
236+
}
237+
238+
impl Resource {
239+
/// Create an eviction
240+
pub fn evict(&self, name: &str, ep: &EvictParams) -> Result<http::Request<Vec<u8>>> {
241+
let base_url = self.make_url() + "/" + name + "/" + "eviction?";
242+
// This is technically identical to Resource::create, but different url
243+
let pp = &ep.post_options;
244+
pp.validate()?;
245+
let mut qp = url::form_urlencoded::Serializer::new(base_url);
246+
if pp.dry_run {
247+
qp.append_pair("dryRun", "All");
248+
}
249+
let urlstr = qp.finish();
250+
// eviction body parameters are awkward, need metadata with name
251+
let data = serde_json::to_vec(&serde_json::json!({
252+
"delete_options": ep.delete_options,
253+
"metadata": { "name": name }
254+
}))?;
255+
let req = http::Request::post(urlstr);
256+
req.body(data).map_err(Error::HttpError)
257+
}
258+
}
259+
260+
#[test]
261+
fn evict_path() {
262+
use crate::api::Resource;
263+
use k8s_openapi::api::core::v1 as corev1;
264+
let r = Resource::namespaced::<corev1::Pod>("ns");
265+
let ep = EvictParams::default();
266+
let req = r.evict("foo", &ep).unwrap();
267+
assert_eq!(req.uri(), "/api/v1/namespaces/ns/pods/foo/eviction?");
268+
}
269+
270+
/// Marker trait for objects that can be evicted
271+
pub trait Evictable {}
272+
273+
impl Evictable for k8s_openapi::api::core::v1::Pod {}
274+
275+
impl<K> Api<K>
276+
where
277+
K: Clone + DeserializeOwned + Evictable,
278+
{
279+
/// Create an eviction
280+
pub async fn evict(&self, name: &str, ep: &EvictParams) -> Result<Status> {
281+
let req = self.resource.evict(name, ep)?;
282+
self.client.request::<Status>(req).await
283+
}
284+
}
285+
224286
// ----------------------------------------------------------------------------
225287
// Attach subresource
226288
// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)