Skip to main content

xianyun_cli/commands/
kubectl.rs

1//! `caproni kubectl`: run kubectl in the context of the managed cluster.
2//!
3//! Port of Go's `cmd/kubectl` package. Flag parsing is skipped (all
4//! trailing arguments are passed through verbatim to `kubectl`).
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 KubectlArgs {
14    /// Arguments forwarded verbatim to the `kubectl` executable.
15    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
16    pub args: Vec<String>,
17}
18
19/// Port of `kubectl.tool`: line-oriented, so it needs no terminal of its
20/// own.
21fn tool() -> Tool {
22    Tool {
23        name: "kubectl".to_string(),
24        install_hint: "see https://kubernetes.io/docs/tasks/tools/".to_string(),
25        require_terminal: false,
26    }
27}
28
29pub fn run(args: &KubectlArgs, ctx: &Context) -> anyhow::Result<()> {
30    run_kubectl(ctx, &args.args)
31}
32
33/// Port of `kubectl.runKubectl`: runs the `kubectl` executable against the
34/// Caproni-managed cluster.
35pub fn run_kubectl(ctx: &Context, args: &[String]) -> anyhow::Result<()> {
36    crate::drivers::ensure_registered();
37
38    let container = load_from_context(ctx).context("loading configuration")?;
39
40    let manager = xianyun_cluster::registry::new_manager(
41        container.caproni.cluster.clone(),
42        container.global.profile_name.clone(),
43    )
44    .context("creating cluster manager")?;
45
46    let code = Runner::new()
47        .run(
48            manager.as_ref(),
49            &container.global.profile_name,
50            &tool(),
51            args,
52        )
53        .context("running kubectl")?;
54
55    if code != 0 {
56        // kubectl has already reported whatever went wrong; reproduce its
57        // exit status without printing a second error line.
58        return Err(crate::exitcode::CliExit::new(code).into());
59    }
60
61    Ok(())
62}