diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 218e56894..3f73b7c9c 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -111,6 +111,10 @@ path = "pod_attach.rs" name = "pod_exec" path = "pod_exec.rs" +[[example]] +name = "pod_shell" +path = "pod_shell.rs" + [[example]] name = "proxy" path = "proxy.rs" diff --git a/examples/pod_shell.rs b/examples/pod_shell.rs new file mode 100644 index 000000000..7bd82faf0 --- /dev/null +++ b/examples/pod_shell.rs @@ -0,0 +1,90 @@ +#[macro_use] +extern crate log; + +use futures::{StreamExt, TryStreamExt}; +use k8s_openapi::api::core::v1::Pod; + +use kube::{ + api::{Api, AttachParams, DeleteParams, 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_else(|_| "default".into()); + + let p: Pod = serde_json::from_value(serde_json::json!({ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { "name": "example" }, + "spec": { + "containers": [{ + "name": "example", + "image": "alpine", + // Do nothing + "command": ["tail", "-f", "/dev/null"], + }], + } + }))?; + + let pods: Api = Api::namespaced(client, &namespace); + // Stop on error including a pod already exists or is still being deleted. + pods.create(&PostParams::default(), &p).await?; + + // Wait until the pod is running, otherwise we get 500 error. + let lp = ListParams::default().fields("metadata.name=example").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 attach to {}", Meta::name(&o)); + break; + } + } + _ => {} + } + } + + // Piping current stdin/stdout + { + let mut attached = pods + .exec( + "example", + vec!["sh"], + &AttachParams::default() + .stdin(true) + .stdout(true) + .stderr(false) + .tty(true), + ) + .await?; + let mut stdin_writer = attached.stdin().unwrap(); + let mut stdout_reader = attached.stdout().unwrap(); + // > For interactive uses, it is recommended to spawn a thread dedicated to user input and use blocking IO directly in that thread. + // > https://docs.rs/tokio/0.2.24/tokio/io/fn.stdin.html + let mut stdin = tokio::io::stdin(); + let mut stdout = tokio::io::stdout(); + futures::join!( + tokio::io::copy(&mut stdin, &mut stdin_writer), + tokio::io::copy(&mut stdout_reader, &mut stdout) + ); + } + + // Delete it + println!("deleting"); + pods.delete("example", &DeleteParams::default()) + .await? + .map_left(|pdel| { + assert_eq!(Meta::name(&pdel), "example"); + }); + + Ok(()) +}