status: deduplicate the healthy and degraded paths, keep the System block contiguous

print_status and print_degraded_sections carried four byte-identical blocks - host
firewall (25 lines), anti-torrent (24), last actions (9) and recent events (10) - plus a
Conntrack block that was the same logic assembled differently and a Traffic desired block
of four printlns. Roughly 80 lines of copy-paste with no test covering either copy, so a
format change applied to one path would have silently diverged from the other. The
divergence would only ever be visible while the container is down, which is the worst
moment to discover the diagnostic output is wrong.

Every shared section is now one function called from both paths:
print_traffic_desired_section, print_host_firewall_section, print_antitorrent_section,
print_conntrack_section, print_last_actions_section, print_recent_events_section. The file
already had this shape for print_swap_status and print_reboot_section; the pattern is now
followed consistently. The Docker/Amnezia port path block keeps two variants because they
genuinely differ - real values against unknown - but the shared FORWARD ownership note is
a single constant.

The runtime state paths were also duplicated across modules: status.rs re-declared the
literals that daemon.rs already owns. daemon::LAST_STATE_FILE and daemon::EVENTS_FILE are
now pub and reused, and the recent-events window is a named constant instead of a bare 10.

Ordering fix in the same function: print_network_tuning_status ran between the service
line and the container lines, so container/container_id/pid/netns/interface appeared under
the "Network tuning:" header at the same two-space indent as its fields. Container
identity is now printed inside the discovery match, before any other section opens, in
both the healthy and degraded paths. Field names are unchanged; only the order of two
blocks moved. IMPLEMENTATION_SPEC section 15 documented the old order and is updated.

print_degraded_sections returned anyhow::Result<()> while never failing; it returns unit
now.

status.rs: 645 -> 602 lines. cargo test --workspace: 110 + 4 passed. clippy -D warnings
clean.
This commit is contained in:
2026-08-06 18:41:39 +05:00
parent d98ade42ce
commit 84275d0fe4
3 changed files with 131 additions and 172 deletions
+122 -165
View File
@@ -1,5 +1,9 @@
use crate::{
config::Config, docker, firewall, netns::build_transport, swap, sysctl_tuning, traffic, wg,
config::Config,
daemon::{EVENTS_FILE, LAST_STATE_FILE},
docker, firewall,
netns::build_transport,
swap, sysctl_tuning, traffic, wg,
};
use std::{
@@ -10,6 +14,9 @@ use std::{
};
const ONLINE_HANDSHAKE_THRESHOLD_SECS: u64 = 180;
const RECENT_EVENTS_LIMIT: usize = 10;
const FORWARD_OWNERSHIP_NOTE: &str =
" note: FORWARD is Docker-managed; awg2-harness does not modify host FORWARD";
pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
println!("System:");
@@ -22,26 +29,33 @@ pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
Err(_) => println!(" service: unknown"),
}
print_network_tuning_status(cfg);
// The System block stays contiguous: container identity is printed before any other
// section opens, otherwise its two-space fields read as belonging to that section.
let target = match docker::discover_target(cfg) {
Ok(t) => t,
Ok(t) => {
println!(" container: {}", t.container_name);
println!(" container_id: {}", t.container_id);
println!(" pid: {}", t.container_pid);
println!(" netns: {}", t.netns_path);
println!(" interface: {}", t.interface_name);
Some(t)
}
Err(e) => {
println!(" container: not available ({e})");
println!(" container_id: n/a");
println!(" pid: n/a");
println!(" netns: n/a");
println!(" interface: n/a");
print_degraded_sections(cfg)?;
return Ok(());
None
}
};
println!(" container: {}", target.container_name);
println!(" container_id: {}", target.container_id);
println!(" pid: {}", target.container_pid);
println!(" netns: {}", target.netns_path);
println!(" interface: {}", target.interface_name);
print_network_tuning_status(cfg);
let Some(target) = target else {
print_degraded_sections(cfg);
return Ok(());
};
let docker_path_diag = docker::collect_port_path_diagnostics(
&target.container_name,
@@ -101,11 +115,7 @@ pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
(_, Err(e)) => println!(" unavailable ({e})"),
}
println!("Traffic desired:");
println!(" peer_limit: {}", cfg.traffic.peer_limit);
println!(" root_rate: {}", cfg.traffic.root_rate);
println!(" ifb_name: {}", cfg.traffic.ifb_name);
println!(" class_start_id: {}", cfg.traffic.class_start_id);
print_traffic_desired_section(cfg);
println!("Traffic actual:");
match traffic::inspect_drift_summary(cfg) {
@@ -170,99 +180,13 @@ pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
Err(_) => println!(" filter statistics: unavailable"),
}
println!("Host firewall:");
println!(" enabled: {}", cfg.host_firewall.enabled);
println!(" ssh_port: {}/tcp", cfg.host_firewall.ssh_port);
println!(
" amnezia_port: {}/{}",
cfg.host_firewall.amnezia_port,
cfg.host_firewall.amnezia_protocols.join(",")
);
println!(
" input_policy: {}",
cfg.host_firewall
.default_input_policy
.trim()
.to_ascii_lowercase()
);
if cfg.host_firewall.enabled {
match firewall::host_firewall_drift_reason(cfg) {
Ok(None) => println!(" drift: no"),
Ok(Some(reason)) => {
println!(" drift: yes");
println!(" reason: {}", reason);
}
Err(e) => println!(" drift: unknown ({e})"),
}
}
print_host_firewall_section(cfg);
print_docker_amnezia_port_path(&docker_path_diag);
println!("Anti-torrent:");
println!(" enabled: {}", cfg.anti_torrent.enabled);
println!(
" max_connections_per_peer: {}",
cfg.anti_torrent.max_connections_per_peer
);
if cfg.anti_torrent.enabled {
match firewall::antitorrent_drift_reason(cfg) {
Ok(None) => println!(" drift: no"),
Ok(Some(reason)) => {
println!(" drift: yes");
println!(" reason: {}", reason);
}
Err(e) => println!(" drift: unknown ({e})"),
}
match firewall::antitorrent_drop_counter(cfg) {
Ok(Some((packets, bytes))) => {
println!(" dropped_packets: {}", packets);
println!(" dropped_bytes: {}", bytes);
}
Ok(None) => println!(" dropped_packets: n/a"),
Err(e) => println!(" dropped_packets: inspect error ({e})"),
}
}
print_antitorrent_section(cfg);
print_swap_status(cfg);
let ct_count = Command::new("sysctl")
.args(["-n", "net.netfilter.nf_conntrack_count"])
.output()
.ok()
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.unwrap_or_else(|| "n/a".to_string());
let ct_max = Command::new("sysctl")
.args(["-n", "net.netfilter.nf_conntrack_max"])
.output()
.ok()
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.unwrap_or_else(|| "n/a".to_string());
println!("Conntrack:");
println!(" nf_conntrack_count: {}", ct_count);
println!(" nf_conntrack_max: {}", ct_max);
println!("Last actions:");
let last = fs::read_to_string("/run/awg2-harness/last_action.state").unwrap_or_default();
if last.trim().is_empty() {
println!(" no persisted state");
} else {
for line in last.lines() {
println!(" {}", line);
}
}
println!("Recent events:");
let events = fs::read_to_string("/run/awg2-harness/events.log").unwrap_or_default();
let lines: Vec<&str> = events.lines().collect();
if lines.is_empty() {
println!(" no recent events");
} else {
for l in lines.iter().rev().take(10).rev() {
println!(" {l}");
}
}
print_conntrack_section();
print_last_actions_section();
print_recent_events_section();
print_reboot_section(cfg);
Ok(())
@@ -279,22 +203,42 @@ fn print_network_tuning_status(cfg: &Config) {
}
}
fn print_degraded_sections(cfg: &Config) -> anyhow::Result<()> {
/// Sections that stay meaningful when the container was not discovered.
///
/// Every section shared with the healthy path is rendered by the same helper, so the two
/// paths cannot drift apart in format. Only the sections that genuinely depend on the
/// container differ, and they say so explicitly.
fn print_degraded_sections(cfg: &Config) {
println!("Peers:");
println!(" unavailable (container/netns not discovered)");
println!("WireGuard clients:");
println!(" unavailable (container/netns not discovered)");
print_traffic_desired_section(cfg);
println!("Traffic actual:");
println!(" drift: unknown (container/netns not discovered)");
print_host_firewall_section(cfg);
print_unknown_docker_amnezia_port_path();
print_antitorrent_section(cfg);
print_swap_status(cfg);
print_conntrack_section();
print_last_actions_section();
print_recent_events_section();
print_reboot_section(cfg);
}
fn print_traffic_desired_section(cfg: &Config) {
println!("Traffic desired:");
println!(" peer_limit: {}", cfg.traffic.peer_limit);
println!(" root_rate: {}", cfg.traffic.root_rate);
println!(" ifb_name: {}", cfg.traffic.ifb_name);
println!(" class_start_id: {}", cfg.traffic.class_start_id);
}
println!("Traffic actual:");
println!(" drift: unknown (container/netns not discovered)");
fn print_host_firewall_section(cfg: &Config) {
println!("Host firewall:");
println!(" enabled: {}", cfg.host_firewall.enabled);
println!(" ssh_port: {}/tcp", cfg.host_firewall.ssh_port);
@@ -320,80 +264,82 @@ fn print_degraded_sections(cfg: &Config) -> anyhow::Result<()> {
Err(e) => println!(" drift: unknown ({e})"),
}
}
}
println!("Docker/Amnezia port path:");
println!(" docker_userland_proxy: unknown");
println!(" published_udp_port: unknown");
println!(" host_forward_policy_drop: unknown");
println!(" docker_forward_rules_present: unknown");
println!(" note: FORWARD is Docker-managed; awg2-harness does not modify host FORWARD");
fn print_antitorrent_section(cfg: &Config) {
println!("Anti-torrent:");
println!(" enabled: {}", cfg.anti_torrent.enabled);
println!(
" max_connections_per_peer: {}",
cfg.anti_torrent.max_connections_per_peer
);
if cfg.anti_torrent.enabled {
match firewall::antitorrent_drift_reason(cfg) {
Ok(None) => println!(" drift: no"),
Ok(Some(reason)) => {
println!(" drift: yes");
println!(" reason: {}", reason);
}
Err(e) => println!(" drift: unknown ({e})"),
}
match firewall::antitorrent_drop_counter(cfg) {
Ok(Some((packets, bytes))) => {
println!(" dropped_packets: {}", packets);
println!(" dropped_bytes: {}", bytes);
}
Ok(None) => println!(" dropped_packets: n/a"),
Err(e) => println!(" dropped_packets: inspect error ({e})"),
}
if !cfg.anti_torrent.enabled {
return;
}
match firewall::antitorrent_drift_reason(cfg) {
Ok(None) => println!(" drift: no"),
Ok(Some(reason)) => {
println!(" drift: yes");
println!(" reason: {}", reason);
}
Err(e) => println!(" drift: unknown ({e})"),
}
match firewall::antitorrent_drop_counter(cfg) {
Ok(Some((packets, bytes))) => {
println!(" dropped_packets: {}", packets);
println!(" dropped_bytes: {}", bytes);
}
Ok(None) => println!(" dropped_packets: n/a"),
Err(e) => println!(" dropped_packets: inspect error ({e})"),
}
}
print_swap_status(cfg);
fn print_conntrack_section() {
println!("Conntrack:");
let ct_count = Command::new("sysctl")
.args(["-n", "net.netfilter.nf_conntrack_count"])
.output()
.ok()
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.unwrap_or_else(|| "n/a".to_string());
let ct_max = Command::new("sysctl")
.args(["-n", "net.netfilter.nf_conntrack_max"])
.output()
.ok()
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.unwrap_or_else(|| "n/a".to_string());
println!(" nf_conntrack_count: {}", ct_count);
println!(" nf_conntrack_max: {}", ct_max);
println!(
" nf_conntrack_count: {}",
read_sysctl_or_na("net.netfilter.nf_conntrack_count")
);
println!(
" nf_conntrack_max: {}",
read_sysctl_or_na("net.netfilter.nf_conntrack_max")
);
}
fn read_sysctl_or_na(key: &str) -> String {
Command::new("sysctl")
.args(["-n", key])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.filter(|v| !v.is_empty())
.unwrap_or_else(|| "n/a".to_string())
}
fn print_last_actions_section() {
println!("Last actions:");
let last = fs::read_to_string("/run/awg2-harness/last_action.state").unwrap_or_default();
let last = fs::read_to_string(LAST_STATE_FILE).unwrap_or_default();
if last.trim().is_empty() {
println!(" no persisted state");
} else {
for line in last.lines() {
println!(" {}", line);
}
return;
}
for line in last.lines() {
println!(" {}", line);
}
}
fn print_recent_events_section() {
println!("Recent events:");
let events = fs::read_to_string("/run/awg2-harness/events.log").unwrap_or_default();
let events = fs::read_to_string(EVENTS_FILE).unwrap_or_default();
let lines: Vec<&str> = events.lines().collect();
if lines.is_empty() {
println!(" no recent events");
} else {
for l in lines.iter().rev().take(10).rev() {
println!(" {l}");
}
return;
}
for l in lines.iter().rev().take(RECENT_EVENTS_LIMIT).rev() {
println!(" {l}");
}
print_reboot_section(cfg);
Ok(())
}
fn print_reboot_section(cfg: &Config) {
@@ -487,12 +433,23 @@ fn print_docker_amnezia_port_path(diag: &docker::PortPathDiagnostics) {
" docker_forward_rules_present: {}",
diag.docker_forward_rules_present.as_str()
);
println!(" note: FORWARD is Docker-managed; awg2-harness does not modify host FORWARD");
println!("{FORWARD_OWNERSHIP_NOTE}");
if let Some(w) = diag.warning() {
println!(" warning: {w}");
}
}
/// Same block as [`print_docker_amnezia_port_path`], but every probe needs the container,
/// so all four fields are reported as unknown rather than omitted.
fn print_unknown_docker_amnezia_port_path() {
println!("Docker/Amnezia port path:");
println!(" docker_userland_proxy: unknown");
println!(" published_udp_port: unknown");
println!(" host_forward_policy_drop: unknown");
println!(" docker_forward_rules_present: unknown");
println!("{FORWARD_OWNERSHIP_NOTE}");
}
fn handshake_is_online(epoch: Option<u64>, threshold_secs: u64) -> &'static str {
if let Some(ts) = epoch {
let now = SystemTime::now()