Skip to content

Commit

Permalink
Add support for terminal size when executing command inside a contain…
Browse files Browse the repository at this point in the history
…er (kube-rs#983)

* Add support for terminal size when executing command inside a container

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* remove unused import + fix example

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Use crossterm::event::EventStream to get terminal change in
pod_exec_terminal size example

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Fix error when terminal_resize_tx is None

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Add some comments

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Fix typos

Co-authored-by: kazk <kazk.dev@gmail.com>
Signed-off-by: armand picard <33733022+armandpicard@users.noreply.github.com>

* Fix: uniformize message send

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Remove crossterm event stream

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Rename example pod_shell_crossterm

Signed-off-by: armandpicard <armandpicard71@gmail.com>

* Make example run on window but without handling terminal size change + run fmt

Signed-off-by: armandpicard <armandpicard71@gmail.com>

Signed-off-by: armandpicard <armandpicard71@gmail.com>
Signed-off-by: armand picard <33733022+armandpicard@users.noreply.github.com>
Co-authored-by: kazk <kazk.dev@gmail.com>
Co-authored-by: Eirik A <sszynrae@gmail.com>
  • Loading branch information
3 people authored and aviramha committed Nov 19, 2022
1 parent 24c98b1 commit 66b9699
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 61 deletions.
6 changes: 6 additions & 0 deletions examples/Cargo.toml
Expand Up @@ -53,6 +53,7 @@ backoff = "0.4.0"
clap = { version = "4.0", default-features = false, features = ["std", "cargo", "derive"] }
edit = "0.1.3"
tokio-stream = { version = "0.1.9", features = ["net"] }
crossterm = {version = "0.25.0" }

[[example]]
name = "configmapgen_controller"
Expand Down Expand Up @@ -206,3 +207,8 @@ path = "custom_client_trace.rs"
[[example]]
name = "secret_syncer"
path = "secret_syncer.rs"

[[example]]
name = "pod_shell_crossterm"
path = "pod_shell_crossterm.rs"
required-features = ["ws"]
132 changes: 132 additions & 0 deletions examples/pod_shell_crossterm.rs
@@ -0,0 +1,132 @@
use futures::{channel::mpsc::Sender, SinkExt, StreamExt};
use k8s_openapi::api::core::v1::Pod;

#[cfg(unix)] use crossterm::event::Event;
use kube::{
api::{Api, AttachParams, AttachedProcess, DeleteParams, PostParams, ResourceExt, TerminalSize},
runtime::wait::{await_condition, conditions::is_pod_running},
Client,
};
#[cfg(unix)] use tokio::signal;
use tokio::{io::AsyncWriteExt, select};

#[cfg(unix)]
// Send the new terminal size to channel when it change
async fn handle_terminal_size(mut channel: Sender<TerminalSize>) -> Result<(), anyhow::Error> {
let (width, height) = crossterm::terminal::size()?;
channel.send(TerminalSize { height, width }).await?;

// create a stream to catch SIGWINCH signal
let mut sig = signal::unix::signal(signal::unix::SignalKind::window_change())?;
loop {
if sig.recv().await == None {
return Ok(());
}

let (width, height) = crossterm::terminal::size()?;
channel.send(TerminalSize { height, width }).await?;
}
}

#[cfg(windows)]
// We don't support window for terminal size change, we only send the initial size
async fn handle_terminal_size(mut channel: Sender<TerminalSize>) -> Result<(), anyhow::Error> {
let (width, height) = crossterm::terminal::size()?;
channel.send(TerminalSize { height, width }).await?;
let mut ctrl_c = tokio::signal::windows::ctrl_c()?;
ctrl_c.recv().await;
Ok(())
}


#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::try_default().await?;

let pods: Api<Pod> = Api::default_namespaced(client);
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"],
}],
}
}))?;
// Create pod if don't exist
pods.create(&PostParams::default(), &p).await?;

// Wait until the pod is running, otherwise we get 500 error.
let running = await_condition(pods.clone(), "example", is_pod_running());
let _ = tokio::time::timeout(std::time::Duration::from_secs(15), running).await?;

{
// Here we we put the terminal in 'raw' mode to directly get the input from the user and sending it to the server and getting the result from the server to display directly.
// We also watch for change in your terminal size and send it to the server so that application that use the size work properly.
crossterm::terminal::enable_raw_mode()?;
let mut attached: AttachedProcess = pods
.exec(
"example",
vec!["sh"],
&AttachParams::default().stdin(true).tty(true).stderr(false),
)
.await?;

let mut stdin = tokio_util::io::ReaderStream::new(tokio::io::stdin());
let mut stdout = tokio::io::stdout();

let mut output = tokio_util::io::ReaderStream::new(attached.stdout().unwrap());
let mut input = attached.stdin().unwrap();

let term_tx = attached.terminal_size().unwrap();

let mut handle_terminal_size_handle = tokio::spawn(handle_terminal_size(term_tx));

loop {
select! {
message = stdin.next() => {
match message {
Some(Ok(message)) => {
input.write(&message).await?;
}
_ => {
break;
},
}
},
message = output.next() => {
match message {
Some(Ok(message)) => {
stdout.write(&message).await?;
stdout.flush().await?;
},
_ => {
break
},
}
},
result = &mut handle_terminal_size_handle => {
match result {
Ok(_) => println!("End of terminal size stream"),
Err(e) => println!("Error getting terminal size: {e:?}")
}
},
};
}
crossterm::terminal::disable_raw_mode()?;
}

// Delete it
pods.delete("example", &DeleteParams::default())
.await?
.map_left(|pdel| {
assert_eq!(pdel.name_any(), "example");
});

println!("");
Ok(())
}
2 changes: 1 addition & 1 deletion kube-client/src/api/mod.rs
Expand Up @@ -5,7 +5,7 @@ mod core_methods;
#[cfg(feature = "ws")] mod remote_command;
use std::fmt::Debug;

#[cfg(feature = "ws")] pub use remote_command::AttachedProcess;
#[cfg(feature = "ws")] pub use remote_command::{AttachedProcess, TerminalSize};
#[cfg(feature = "ws")] mod portforward;
#[cfg(feature = "ws")] pub use portforward::Portforwarder;

Expand Down

0 comments on commit 66b9699

Please sign in to comment.