xianyun_cli/commands/
doctor.rs1use std::sync::Arc;
6
7use anyhow::Context as _;
8use clap::Args;
9use xianyun_backends::kubectl;
10use xianyun_cluster::driver::State;
11use xianyun_core::config::caproni::load_from_context;
12use xianyun_core::config::global::Context;
13use xianyun_deploy::diagnostics::base::Meta;
14use xianyun_deploy::diagnostics::builtin;
15use xianyun_executor::exec::CommandRunner;
16
17#[derive(Args, Debug)]
18pub struct DoctorArgs {}
19
20pub fn run(_args: &DoctorArgs, ctx: &Context) -> anyhow::Result<()> {
21 run_doctor(ctx)
22}
23
24pub fn run_doctor(ctx: &Context) -> anyhow::Result<()> {
35 crate::drivers::ensure_registered();
36
37 let container = load_from_context(ctx).context("loading configuration")?;
38
39 let manager = xianyun_cluster::registry::new_manager(
40 container.caproni.cluster.clone(),
41 container.global.profile_name.clone(),
42 )
43 .context("creating cluster manager")?;
44
45 let status = manager.status().context("getting cluster status")?;
46
47 let is_cluster_up = status.state == Some(State::Ready);
48
49 let kubectl_backend: Option<Arc<dyn kubectl::Backend + Send + Sync>> = if is_cluster_up {
50 let kubeconfig = manager.get_kubeconfig().context("getting kubeconfig")?;
51 let executor: Arc<dyn CommandRunner + Send + Sync> =
52 Arc::new(xianyun_executor::exec::Client::new());
53 Some(Arc::new(kubectl::Client::with_config(
54 executor,
55 &kubeconfig,
56 )))
57 } else {
58 tracing::warn!(
59 status = ?status.state,
60 "cluster is not ready, running limited set of diagnostics"
61 );
62 None
63 };
64
65 let meta = Meta {
66 config: Some(container.caproni.clone()),
67 kubectl: kubectl_backend,
68 };
69
70 let runner = xianyun_deploy::diagnostics::Runner::new(builtin::all());
71 let results = runner.run(meta, is_cluster_up);
72
73 let mut errors = Vec::new();
74 let mut warnings = Vec::new();
75
76 for result in results {
77 if result.err.is_some() {
78 errors.push(result);
79 } else if !result.output.is_empty() {
80 warnings.push(result);
81 }
82 }
83
84 if errors.is_empty() && warnings.is_empty() {
85 println!("Caproni is healthy.");
86 return Ok(());
87 }
88
89 for result in &errors {
90 println!("ERROR: {}", result.name);
91 println!("{}", "=".repeat(result.name.len() + 7));
92 println!("{}", result.err.as_deref().unwrap_or_default());
93 println!();
94 }
95
96 for result in &warnings {
97 println!("WARN: {}", result.name);
98 println!("{}", "=".repeat(result.name.len() + 6));
99 println!("{}", result.output);
100 println!();
101 }
102
103 let exit_code = if errors.is_empty() { 1 } else { 2 };
104 Err(crate::exitcode::CliExit::new(exit_code).into())
105}