1use crate::cli::CliError;
13use crate::client::{Client, SERVER_CLUSTER_NAME};
14use crate::config::Settings;
15use crate::docker_runtime::{DockerRuntime, DockerStatus};
16use crate::log;
17use owo_colors::{OwoColorize, Stream};
18use std::fmt;
19
20#[derive(Debug, Clone, serde::Serialize)]
22#[serde(rename_all = "snake_case", tag = "status")]
23pub enum ServerClusterHealth {
24 Healthy,
26 NotReady { reason: String },
28 Missing,
30}
31
32async fn check_server_cluster(client: &Client) -> Result<ServerClusterHealth, CliError> {
33 match client
34 .introspection()
35 .get_cluster(SERVER_CLUSTER_NAME)
36 .await?
37 {
38 None => Ok(ServerClusterHealth::Missing),
39 Some(c) if c.replication_factor.unwrap_or(0) > 0 => Ok(ServerClusterHealth::Healthy),
40 Some(_) => Ok(ServerClusterHealth::NotReady {
41 reason: "replication factor is 0".into(),
42 }),
43 }
44}
45
46#[derive(serde::Serialize)]
47enum RemoteOutput {
48 Success {
49 environment_id: String,
50 server_cluster_health: ServerClusterHealth,
51 },
52
53 Failure {
54 host: String,
55 port: u16,
56 },
57}
58
59#[derive(serde::Serialize)]
60struct DebugOutput {
61 profile: String,
62 docker_status: String,
63 remote: RemoteOutput,
64}
65
66impl fmt::Display for DebugOutput {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 writeln!(
69 f,
70 "{}: {}",
71 "Profile".if_supports_color(Stream::Stderr, |t| t.green()),
72 self.profile.if_supports_color(Stream::Stderr, |t| t.cyan())
73 )?;
74 let docker_label = match self.docker_status.as_str() {
75 "running" => format!(
76 "{}: {}",
77 "Docker".if_supports_color(Stream::Stderr, |t| t.green()),
78 "daemon running".if_supports_color(Stream::Stderr, |t| t.green())
79 ),
80 "not_running" => format!(
81 "{}: {}",
82 "Docker".if_supports_color(Stream::Stderr, |t| t.green()),
83 "daemon not running".if_supports_color(Stream::Stderr, |t| t.yellow())
84 ),
85 _ => format!(
86 "{}: {}",
87 "Docker".if_supports_color(Stream::Stderr, |t| t.green()),
88 "not installed".if_supports_color(Stream::Stderr, |t| t.yellow())
89 ),
90 };
91 writeln!(f, "{}", docker_label)?;
92
93 match &self.remote {
94 RemoteOutput::Success {
95 environment_id,
96 server_cluster_health,
97 } => {
98 writeln!(
99 f,
100 "{}: {}",
101 "Environment".if_supports_color(Stream::Stderr, |t| t.green()),
102 environment_id.if_supports_color(Stream::Stderr, |t| t.cyan())
103 )?;
104 let cluster_line = match server_cluster_health {
105 ServerClusterHealth::Healthy => format!(
106 "{}: {} ({})",
107 "Server cluster".if_supports_color(Stream::Stderr, |t| t.green()),
108 SERVER_CLUSTER_NAME.if_supports_color(Stream::Stderr, |t| t.cyan()),
109 "healthy"
110 ),
111 ServerClusterHealth::NotReady { reason } => format!(
112 "{}: {} ({}: {})\n hint: run `mz-deploy setup`",
113 "Server cluster".if_supports_color(Stream::Stderr, |t| t.green()),
114 SERVER_CLUSTER_NAME.if_supports_color(Stream::Stderr, |t| t.cyan()),
115 "not ready".if_supports_color(Stream::Stderr, |t| t.yellow()),
116 reason,
117 ),
118 ServerClusterHealth::Missing => format!(
119 "{}: {} ({})\n hint: run `mz-deploy setup`",
120 "Server cluster".if_supports_color(Stream::Stderr, |t| t.green()),
121 SERVER_CLUSTER_NAME.if_supports_color(Stream::Stderr, |t| t.cyan()),
122 "missing".if_supports_color(Stream::Stderr, |t| t.red()),
123 ),
124 };
125 write!(f, "{}", cluster_line)?;
126 }
127 RemoteOutput::Failure { host, port } => {
128 write!(
129 f,
130 "{} {}:{}",
131 "Failed to connect to".if_supports_color(Stream::Stderr, |t| t.red()),
132 host.if_supports_color(Stream::Stderr, |t| t.cyan()),
133 port.to_string()
134 .if_supports_color(Stream::Stderr, |t| t.cyan())
135 )?;
136 }
137 }
138
139 Ok(())
140 }
141}
142
143pub async fn run(settings: &Settings) -> Result<(), CliError> {
154 let profile = settings.connection();
155 let docker_status = DockerRuntime::check_availability().await;
156 let docker_status_str = match docker_status {
157 DockerStatus::Running => "running",
158 DockerStatus::NotRunning => "not_running",
159 DockerStatus::NotInstalled => "not_installed",
160 };
161
162 let client = Client::connect_with_profile(profile.clone()).await;
163
164 let remote = match client {
165 Ok(client) => {
166 let (environment_id, cluster_result) =
167 tokio::join!(query_session_info(&client), check_server_cluster(&client),);
168
169 let environment_id = environment_id?;
170 let server_cluster_health = cluster_result?;
171
172 RemoteOutput::Success {
173 environment_id,
174 server_cluster_health,
175 }
176 }
177 Err(_) => RemoteOutput::Failure {
178 host: profile.require_host()?.to_string(),
179 port: profile.port,
180 },
181 };
182
183 let output = DebugOutput {
184 profile: profile.name.clone(),
185 docker_status: docker_status_str.to_string(),
186 remote,
187 };
188
189 log::output(&output);
190
191 Ok(())
192}
193
194async fn query_session_info(client: &Client) -> Result<String, CliError> {
195 let row = client
196 .query_one("SELECT mz_environment_id() AS environment_id", &[])
197 .await?;
198
199 let environment_id: String = row.get("environment_id");
200
201 Ok(environment_id)
202}