This commit is contained in:
Robin Appelman 2026-04-06 22:51:25 +02:00
commit 22c82c59af
7 changed files with 926 additions and 924 deletions

1755
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,40 +2,40 @@
name = "sidewindow" name = "sidewindow"
version = "1.3.1" version = "1.3.1"
authors = ["Robin Appelman <robin@icewind.nl>"] authors = ["Robin Appelman <robin@icewind.nl>"]
edition = "2021" edition = "2024"
rust-version = "1.73.0" rust-version = "1.88.0"
[dependencies] [dependencies]
color-eyre = "0.6.3" color-eyre = "0.6.5"
warp = "0.3.7" warp = { version = "0.4.2", features = ["server"] }
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.51.0", features = ["macros", "rt-multi-thread"] }
ctrlc = { version = "3.4.4", features = ["termination"] } ctrlc = { version = "3.5.2", features = ["termination"] }
dotenvy = "0.15.7" dotenvy = "0.15.7"
regex = { version = "1.10.4", default-features = false, features = ["std"] } regex = { version = "1.12.3", default-features = false, features = ["std"] }
once_cell = "1.19.0" once_cell = "1.21.4"
hostname = "0.4.0" hostname = "0.4.2"
libc = "0.2.153" libc = "0.2.184"
ahash = "0.8.11" ahash = "0.8.12"
bollard = "0.16.1" bollard = "0.20.2"
futures-util = "0.3.30" futures-util = "0.3.32"
libmdns = "0.8.0" libmdns = "0.10.1"
tracing = "0.1.40" tracing = "0.1.44"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.23"
nvml-wrapper = "0.10.0" nvml-wrapper = "0.12.1"
if-addrs = "0.12.0" if-addrs = "0.15.0"
sysconf = "0.3.4" sysconf = "0.3.4"
thiserror = "1.0.58" thiserror = "2.0.18"
clap = { version = "=4.4.18", features = ["derive"] } clap = { version = "4.6.0", features = ["derive"] }
[target.'cfg(not(windows))'.dependencies] [target.'cfg(not(windows))'.dependencies]
procfs = "0.16.0" procfs = "0.18.0"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
sysinfo = { version = "0.30.8" } sysinfo = { version = "0.38.4" }
winapi = { version = "0.3.9", features = ["sysinfoapi", "processthreadsapi", "powerbase", "minwindef", "winnt", "winbase", "winerror", "impl-default"] } winapi = { version = "0.3.9", features = ["sysinfoapi", "processthreadsapi", "powerbase", "minwindef", "winnt", "winbase", "winerror", "impl-default"] }
wmi = { version = "0.13.3" } wmi = { version = "0.18.4" }
winreg = { version = "0.52.0", features = ["serialization-serde"] } winreg = { version = "0.56.0", features = ["serialization-serde"] }
os-thread-local = "0.1.3" os-thread-local = "0.1.3"
[profile.release] [profile.release]

34
flake.lock generated
View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1742394900, "lastModified": 1774313767,
"narHash": "sha256-vVOAp9ahvnU+fQoKd4SEXB2JG2wbENkpqcwlkIXgUC0=", "narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "70947c1908108c0c551ddfd73d4f750ff2ea67cd", "rev": "3d9df76e29656c679c744968b17fbaf28f0e923d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -22,11 +22,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1748868585, "lastModified": 1775481670,
"narHash": "sha256-DrrbahOQAwvNM8l5EuGxxkVS7X5/S59zcG0N9ZWQFhk=", "narHash": "sha256-y/c2JCNmjeuxbSpfE4r2qN02dQX+tkfIZpwEfOlF64s=",
"owner": "nix-community", "owner": "nix-community",
"repo": "flakelight", "repo": "flakelight",
"rev": "dfbecd12d99c1bf82906521a6a7d5b75d2aa1ca2", "rev": "0868c446b28e412ff2a685e93856658a43c30b1f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -44,11 +44,11 @@
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1748205441, "lastModified": 1775237959,
"narHash": "sha256-W+UUBT/l1DSTZo5G43494mRNNspJ2i9jW2QELC9JuMQ=", "narHash": "sha256-sAtsOHkxSPlm4KTNxHnf1sGhnuw4F47k9mjdbIRjtxo=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "dac3b74a89cebbeb21cc6602e4a346604adbee8b", "rev": "306a9f8d74d04a1bdb8e93318a25aa5745ae3c20",
"revCount": 49, "revCount": 70,
"type": "git", "type": "git",
"url": "https://codeberg.org/icewind/mill-scale" "url": "https://codeberg.org/icewind/mill-scale"
}, },
@ -59,16 +59,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1748708770, "lastModified": 1775305101,
"narHash": "sha256-q8jG2HJWgooWa9H0iatZqBPF3bp0504e05MevFmnFLY=", "narHash": "sha256-/74n1oQPtKG52Yw41cbToxspxHbYz6O3vi+XEw16Qe8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a59eb7800787c926045d51b70982ae285faa2346", "rev": "36a601196c4ebf49e035270e10b2d103fe39076b",
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
"ref": "nixos-25.05", "ref": "nixos-25.11",
"type": "indirect" "type": "indirect"
} }
}, },
@ -88,11 +88,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1742697269, "lastModified": 1774535687,
"narHash": "sha256-Lpp0XyAtIl1oGJzNmTiTGLhTkcUjwSkEb0gOiNzYFGM=", "narHash": "sha256-dpKS/8+uB0EoI4mCrpio+xs8Xxry6ZhLLwV8VIbbfrs=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "01973c84732f9275c50c5f075dd1f54cc04b3316", "rev": "75900435aa883f84b038316864b3f60956681523",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1,6 +1,6 @@
{ {
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-25.05"; nixpkgs.url = "nixpkgs/nixos-25.11";
flakelight = { flakelight = {
url = "github:nix-community/flakelight"; url = "github:nix-community/flakelight";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";

View file

@ -1,5 +1,6 @@
use bollard::container::{Stats, StatsOptions};
use bollard::models::ContainerSummary; use bollard::models::ContainerSummary;
use bollard::plugin::ContainerStatsResponse;
use bollard::query_parameters::StatsOptions;
use bollard::Docker; use bollard::Docker;
use color_eyre::Result; use color_eyre::Result;
use futures_util::future::ready; use futures_util::future::ready;
@ -45,27 +46,36 @@ impl Container {
.ok(); .ok();
} }
fn from(stats: Stats, container: ContainerSummary) -> Self { fn from(stats: ContainerStatsResponse, container: ContainerSummary) -> Self {
let cpu = stats.cpu_stats.unwrap_or_default();
Container { Container {
name: stats.name, name: stats.name.unwrap_or_default(),
image: container.image.unwrap_or_default(), image: container.image.unwrap_or_default(),
memory: stats.memory_stats.usage.unwrap_or_default(), memory: stats
cpu_time: stats.cpu_stats.cpu_usage.total_usage as f64 .memory_stats
.unwrap_or_default()
.usage
.unwrap_or_default(),
cpu_time: cpu
.cpu_usage
.unwrap_or_default()
.total_usage
.unwrap_or_default() as f64
/ 1_000_000_000.0 / 1_000_000_000.0
/ stats.cpu_stats.online_cpus.unwrap_or(1) as f64, / cpu.online_cpus.unwrap_or(1) as f64,
network_sent: stats network_sent: stats
.networks .networks
.as_ref() .as_ref()
.into_iter() .into_iter()
.flat_map(HashMap::values) .flat_map(HashMap::values)
.map(|stats| stats.tx_bytes) .map(|stats| stats.tx_bytes.unwrap_or_default())
.sum(), .sum(),
network_received: stats network_received: stats
.networks .networks
.as_ref() .as_ref()
.into_iter() .into_iter()
.flat_map(HashMap::values) .flat_map(HashMap::values)
.map(|stats| stats.rx_bytes) .map(|stats| stats.rx_bytes.unwrap_or_default())
.sum(), .sum(),
} }
} }
@ -73,24 +83,20 @@ impl Container {
pub async fn get_docker() -> Option<Docker> { pub async fn get_docker() -> Option<Docker> {
match Docker::connect_with_local_defaults() { match Docker::connect_with_local_defaults() {
Ok(docker) => docker Ok(docker) => docker.list_containers(None).await.ok().map(|_| docker),
.list_containers::<String>(None)
.await
.ok()
.map(|_| docker),
Err(_) => None, Err(_) => None,
} }
} }
pub async fn stat(docker: Docker) -> Result<impl Stream<Item = Container>> { pub async fn stat(docker: Docker) -> Result<impl Stream<Item = Container>> {
let containers = docker.list_containers::<String>(None).await?; let containers = docker.list_containers(None).await?;
Ok(containers Ok(containers
.into_iter() .into_iter()
.map(move |container| { .map(move |container| {
let docker = docker.clone(); let docker = docker.clone();
async move { async move {
let id = container.id.as_ref().unwrap(); let id = container.id.as_ref().unwrap();
let stats: Stats = docker let stats = docker
.stats( .stats(
id, id,
Some(StatsOptions { Some(StatsOptions {

View file

@ -1,4 +1,4 @@
use std::fs::{read_dir, read_to_string, File}; use std::fs::{File, read_dir, read_to_string};
use std::io; use std::io;
use std::io::{ErrorKind, Read, Seek}; use std::io::{ErrorKind, Read, Seek};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -26,9 +26,8 @@ impl FileSource {
Ok(FileSource { Ok(FileSource {
path: path.into(), path: path.into(),
buff: String::with_capacity(32), buff: String::with_capacity(32),
file: File::open(path).map_err(|e| { file: File::open(path).inspect_err(|_| {
warn!("failed to open sensor {}", path.display()); warn!("failed to open sensor {}", path.display());
e
})?, })?,
}) })
} }
@ -66,9 +65,8 @@ impl FileSource {
} }
pub fn reopen(&mut self) -> io::Result<()> { pub fn reopen(&mut self) -> io::Result<()> {
self.file = File::open(&self.path).map_err(|e| { self.file = File::open(&self.path).inspect_err(|_| {
warn!("failed to open sensor {}", self.path.display()); warn!("failed to open sensor {}", self.path.display());
e
})?; })?;
Ok(()) Ok(())
} }

View file

@ -1,11 +1,11 @@
use bollard::Docker; use bollard::Docker;
use clap::Parser; use clap::Parser;
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use futures_util::pin_mut;
use futures_util::StreamExt; use futures_util::StreamExt;
use futures_util::pin_mut;
use libmdns::Responder; use libmdns::Responder;
use sidewindow::docker::{get_docker, stat, Container}; use sidewindow::docker::{Container, get_docker, stat};
use sidewindow::{get_metrics, Sensors}; use sidewindow::{Sensors, get_metrics};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::runtime::Handle; use tokio::runtime::Handle;
@ -121,12 +121,7 @@ async fn setup_mdns(hostname: String, port: u16) {
} }
}; };
let _svc = mdns.register( let _svc = mdns.register("_prometheus-http._tcp", &hostname, port, &["/metrics"]);
"_prometheus-http._tcp".into(),
hostname,
port,
&["/metrics"],
);
loop { loop {
sleep(Duration::from_secs(60 * 60)).await; sleep(Duration::from_secs(60 * 60)).await;