Skip to main content

xianyun_cli/commands/
stern.rs

1//! `caproni stern`: tail logs from multiple pods with stern.
2//!
3//! Port of Go's `cmd/stern` package. Flag parsing is skipped (all trailing
4//! arguments are passed through verbatim to `stern`).
5
6use anyhow::Context as _;
7use clap::Args;
8use xianyun_cluster::clustertool::{Runner, Tool};
9use xianyun_core::config::caproni::load_from_context;
10use xianyun_core::config::global::Context;
11
12#[derive(Args, Debug)]
13pub struct SternArgs {
14    /// Arguments forwarded verbatim to the `stern` executable.
15    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
16    pub args: Vec<String>,
17}
18
19/// Port of `stern.tool`: streams log lines to the standard streams, so it
20/// needs no terminal of its own.
21fn tool() -> Tool {
22    Tool {
23        name: "stern".to_string(),
24        install_hint: "install it with `mise use -g stern`, or see https://github.com/stern/stern"
25            .to_string(),
26        require_terminal: false,
27    }
28}
29
30pub fn run(args: &SternArgs, ctx: &Context) -> anyhow::Result<()> {
31    run_stern(ctx, &args.args)
32}
33
34/// Port of `stern.runStern`: runs the `stern` executable against the
35/// Caproni-managed cluster.
36pub fn run_stern(ctx: &Context, args: &[String]) -> anyhow::Result<()> {
37    crate::drivers::ensure_registered();
38
39    let container = load_from_context(ctx).context("loading configuration")?;
40
41    let manager = xianyun_cluster::registry::new_manager(
42        container.caproni.cluster.clone(),
43        container.global.profile_name.clone(),
44    )
45    .context("creating cluster manager")?;
46
47    let code = Runner::new()
48        .run(
49            manager.as_ref(),
50            &container.global.profile_name,
51            &tool(),
52            args,
53        )
54        .context("running stern")?;
55
56    if code != 0 {
57        // stern has already reported whatever went wrong; reproduce its
58        // exit status without printing a second error line.
59        return Err(crate::exitcode::CliExit::new(code).into());
60    }
61
62    Ok(())
63}