containers: Docker (and best-effort Podman) plugin
- minimal Docker Engine API client over Unix/TCP sockets with version negotiation and chunked response decoding - drop-in stats schema: active/stopped containers, CPU/memory/IO/network rates, ports, uptime (pretty_date), image, engine field - Docker + Podman engines, config (all, podman_sock, max_name_size, disable_stats, show/hide, per-container thresholds) - export strips the computation sub-dicts (--stdout/--stdout-csv) - TUI block above the process count - CI-safe fake Docker socket test + live differential parity
This commit is contained in:
@@ -115,6 +115,64 @@ pub fn bar_custom(
|
||||
bar
|
||||
}
|
||||
|
||||
/// Get a datetime or a timestamp and return a pretty string like
|
||||
/// 'an hour ago', 'yesterday', '3 months ago', 'just now'...
|
||||
/// (port of `glances.globals.pretty_date`).
|
||||
pub fn pretty_date(
|
||||
started: chrono::DateTime<chrono::Local>,
|
||||
now: chrono::DateTime<chrono::Local>,
|
||||
) -> String {
|
||||
let diff = now.signed_duration_since(started);
|
||||
let day_diff = diff.num_days();
|
||||
if day_diff < 0 {
|
||||
return String::new();
|
||||
}
|
||||
// Python's timedelta.seconds excludes the days.
|
||||
let second_diff = diff.num_seconds() - day_diff * 86_400;
|
||||
|
||||
if day_diff == 0 {
|
||||
let thresholds: [(i64, i64, Option<&str>, Option<&str>); 6] = [
|
||||
(10, 1, Some("just now"), None),
|
||||
(60, 1, None, Some(" secs")),
|
||||
(120, 1, Some("a min"), None),
|
||||
(3600, 60, None, Some(" mins")),
|
||||
(7200, 1, Some("an hour"), None),
|
||||
(86_400, 3600, None, Some(" hours")),
|
||||
];
|
||||
for (max_value, divisor, singular, plural) in thresholds {
|
||||
if second_diff < max_value {
|
||||
if let Some(singular) = singular
|
||||
&& plural.is_none()
|
||||
{
|
||||
return singular.to_string();
|
||||
}
|
||||
let value = second_diff / divisor;
|
||||
return format!("{value}{}", plural.unwrap_or(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let day_thresholds: [(i64, i64, Option<&str>, Option<&str>); 5] = [
|
||||
(2, 1, Some("yesterday"), None),
|
||||
(7, 1, Some("a day"), Some(" days")),
|
||||
(31, 7, Some("a week"), Some(" weeks")),
|
||||
(365, 30, Some("a month"), Some(" months")),
|
||||
(i64::MAX, 365, Some("an year"), Some(" years")),
|
||||
];
|
||||
for (max_days, divisor, singular, plural) in day_thresholds {
|
||||
if day_diff < max_days {
|
||||
let value = day_diff / divisor;
|
||||
if let Some(singular) = singular
|
||||
&& (value <= 1 || plural.is_none())
|
||||
{
|
||||
return singular.to_string();
|
||||
}
|
||||
return format!("{value}{}", plural.unwrap_or(""));
|
||||
}
|
||||
}
|
||||
String::new()
|
||||
}
|
||||
|
||||
/// Parse a value like `'12.5 MB'` into a float (port of `string_value_to_float`).
|
||||
pub fn string_value_to_float(s: &str) -> Option<f64> {
|
||||
let cleaned: String = s.chars().filter(|c| *c != ' ').collect();
|
||||
|
||||
Reference in New Issue
Block a user