cellos_ctl/cmd/
rollout.rs1use std::time::Duration;
8
9use indicatif::{ProgressBar, ProgressStyle};
10
11use crate::client::{formation_path, CellosClient};
12use crate::exit::{CtlError, CtlResult};
13use crate::model::Formation;
14
15const POLL_INTERVAL: Duration = Duration::from_millis(1500);
16
17pub async fn status(client: &CellosClient, name: &str, timeout_secs: Option<u64>) -> CtlResult<()> {
18 let bar = ProgressBar::new_spinner();
19 bar.set_style(
20 ProgressStyle::with_template("{spinner} {wide_msg}")
21 .unwrap_or_else(|_| ProgressStyle::default_spinner()),
22 );
23 bar.enable_steady_tick(Duration::from_millis(120));
24
25 let deadline = timeout_secs.map(|s| std::time::Instant::now() + Duration::from_secs(s));
26 let path = formation_path(name);
28
29 loop {
30 if let Some(d) = deadline {
31 if std::time::Instant::now() >= d {
32 bar.finish_and_clear();
33 return Err(CtlError::api(format!(
34 "timed out waiting for formation/{name}"
35 )));
36 }
37 }
38
39 let f: Formation = match client.get_json::<Formation>(&path).await {
40 Ok(f) => f,
41 Err(e) => {
42 bar.finish_and_clear();
43 return Err(e);
44 }
45 };
46
47 let state = f.state.to_ascii_uppercase();
48 let cells = f.cells.len();
49 bar.set_message(format!(
50 "formation/{name}: {state} ({cells} cell{s})",
51 s = if cells == 1 { "" } else { "s" }
52 ));
53
54 match state.as_str() {
55 "COMPLETED" => {
56 bar.finish_and_clear();
57 println!("formation/{name} COMPLETED");
58 return Ok(());
59 }
60 "FAILED" => {
61 bar.finish_and_clear();
62 return Err(CtlError::api(format!("formation/{name} FAILED")));
63 }
64 _ => {
65 tokio::select! {
66 _ = tokio::signal::ctrl_c() => {
67 bar.finish_and_clear();
68 return Err(CtlError::usage("rollout watch cancelled"));
69 }
70 _ = tokio::time::sleep(POLL_INTERVAL) => {}
71 }
72 }
73 }
74 }
75}