Skip to content

Commit 1f8e7a0

Browse files
committed
feat(jetsocat): add warning field to doctor diagnostic output
Adds support for surfacing warnings in the doctor tool's diagnostic output, allowing external tools to distinguish between complete success, success with warnings, and failure. The diagnostic JSON output now includes an optional "warning" field, and the human-readable output displays a warning emoji (⚠️) instead of a success checkmark (✅) when warnings are present. This enables better integration with external monitoring and diagnostic tools that need to detect and report warning conditions even when the overall diagnostic succeeds.
1 parent e031fa3 commit 1f8e7a0

4 files changed

Lines changed: 41 additions & 5 deletions

File tree

jetsocat/src/doctor/macros.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ macro_rules! diagnostic {
1818
let diagnostic = Diagnostic {
1919
name: diagnostic_name.to_owned(),
2020
success: result.is_ok(),
21+
warning: ctx.warning,
2122
output: (!output.is_empty()).then_some(output.trim_end().to_owned()),
2223
error: result.as_ref().err().map(|e| format!("{:?}", e)),
2324
help: ctx.help,

jetsocat/src/doctor/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct Args {
2626
pub struct Diagnostic {
2727
pub name: String,
2828
pub success: bool,
29+
pub warning: Option<String>,
2930
pub output: Option<String>,
3031
pub error: Option<String>,
3132
pub help: Option<String>,
@@ -69,6 +70,10 @@ impl Diagnostic {
6970
object.insert("name".to_owned(), JsonValue::String(self.name));
7071
object.insert("success".to_owned(), JsonValue::Boolean(self.success));
7172

73+
if let Some(warning) = self.warning {
74+
object.insert("warning".to_owned(), JsonValue::String(warning));
75+
}
76+
7277
if let Some(output) = self.output {
7378
object.insert("output".to_owned(), JsonValue::String(output));
7479
}
@@ -119,11 +124,19 @@ impl Diagnostic {
119124
write!(f, "=> {} ", self.0.name)?;
120125

121126
if self.0.success {
122-
write!(f, "OK ✅")?;
127+
if self.0.warning.is_some() {
128+
write!(f, "OK ⚠️")?;
129+
} else {
130+
write!(f, "OK ✅")?;
131+
}
123132
} else {
124133
write!(f, "FAILED ❌")?;
125134
}
126135

136+
if let Some(warning) = self.0.warning.as_deref() {
137+
write!(f, "\n\n### Warning\n{}", capitalize(warning))?;
138+
}
139+
127140
if let Some(output) = self.0.output.as_deref() {
128141
write!(f, "\n\n### Output\n{output}")?;
129142
}
@@ -180,6 +193,7 @@ impl Link {
180193
#[derive(Default)]
181194
struct DiagnosticCtx {
182195
help: Option<String>,
196+
warning: Option<String>,
183197
links: Vec<Link>,
184198
}
185199

@@ -188,6 +202,10 @@ impl DiagnosticCtx {
188202
self.help = Some(message.into());
189203
}
190204

205+
fn attach_warning(&mut self, message: impl Into<String>) {
206+
self.warning = Some(message.into());
207+
}
208+
191209
fn attach_link(&mut self, name: impl Into<String>, href: impl Into<String>, description: impl Into<String>) {
192210
self.links.push(Link {
193211
name: name.into(),

jetsocat/src/doctor/rustls.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,31 @@ pub(super) fn run(args: &Args, callback: &mut dyn FnMut(Diagnostic) -> bool) {
3434
}
3535
}
3636

37-
fn rustls_load_native_certs(_: &mut DiagnosticCtx, root_store: &mut rustls::RootCertStore) -> anyhow::Result<()> {
37+
fn rustls_load_native_certs(ctx: &mut DiagnosticCtx, root_store: &mut rustls::RootCertStore) -> anyhow::Result<()> {
3838
let result = rustls_native_certs::load_native_certs();
3939

40+
let mut warning_messages = Vec::new();
41+
4042
for error in result.errors {
41-
warn!("Error when loading native certs: {:?}", anyhow::Error::new(error),);
43+
let error_msg = format!("error when loading native certs: {:?}", anyhow::Error::new(error));
44+
warn!("{}", error_msg);
45+
warning_messages.push(error_msg);
4246
}
4347

4448
for cert in result.certs {
4549
if let Err(e) = root_store.add(cert.clone()) {
46-
warn!("Invalid root certificate: {e}");
50+
let error_msg = format!("invalid root certificate: {e}");
51+
warn!("{}", error_msg);
52+
warning_messages.push(error_msg);
4753
let root_cert_pem = cert_to_pem(&cert).context("failed to write the certificate as PEM")?;
4854
info!("{root_cert_pem}");
4955
}
5056
}
5157

58+
if !warning_messages.is_empty() {
59+
ctx.attach_warning(warning_messages.join("; "));
60+
}
61+
5262
Ok(())
5363
}
5464

jetsocat/src/doctor/schannel.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,13 @@ fn schannel_fetch_chain(
312312
.context("failed to retrieve the remote chain")?;
313313

314314
let mut certificates = Vec::new();
315+
let mut warning_messages = Vec::new();
315316

316317
remote_chain.for_each(|cert_idx, element| {
317318
if let Err(error) = chain_ctx.store.add_x509_encoded_certificate(element.cert.as_x509_der()) {
318-
warn!(cert_idx, %error, "Failed to add certificate to the store");
319+
let error_msg = format!("failed to add certificate {cert_idx} to the store: {error}");
320+
warn!("{}", error_msg);
321+
warning_messages.push(error_msg);
319322
}
320323

321324
certificates.push(CertInspectProxy {
@@ -324,6 +327,10 @@ fn schannel_fetch_chain(
324327
});
325328
});
326329

330+
if !warning_messages.is_empty() {
331+
ctx.attach_warning(warning_messages.join("; "));
332+
}
333+
327334
crate::doctor::log_chain(certificates.iter());
328335
help::x509_io_link(ctx, certificates.iter());
329336

0 commit comments

Comments
 (0)