Skip to content

Commit 62eeb5b

Browse files
committed
Show resource usage
1 parent ea32f43 commit 62eeb5b

File tree

5 files changed

+122
-0
lines changed

5 files changed

+122
-0
lines changed

src/container.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
container_stats::Usage,
23
distrobox::{ContainerInfo, CreateArgName, CreateArgs, ExportableApp, Status},
34
distrobox_task::DistroboxTask,
45
fakers::Command,
@@ -40,6 +41,7 @@ mod imp {
4041
pub distro: RefCell<Option<KnownDistro>>,
4142
pub apps: Query<TypedListStore<glib::BoxedAnyObject>>,
4243
pub binaries: Query<TypedListStore<glib::BoxedAnyObject>>,
44+
pub usage: Query<Usage>,
4345
}
4446

4547
impl Default for Container {
@@ -72,6 +74,7 @@ mod imp {
7274
None
7375
}
7476
}),
77+
usage: Query::new("usage".into(), || async { Ok(Usage::default()) }),
7578
}
7679
}
7780
}
@@ -138,6 +141,35 @@ impl Container {
138141
}
139142
});
140143

144+
let this_clone = this.clone();
145+
this.usage().set_fetcher(move || {
146+
let this = this_clone.clone();
147+
async move {
148+
let root_store = this.root_store();
149+
let runtime = root_store.get_container_runtime().await?;
150+
let runner = root_store.imp().command_runner.get().unwrap();
151+
152+
let mut cmd = Command::new(runtime.as_str());
153+
cmd.arg("stats");
154+
cmd.arg("--no-stream");
155+
cmd.arg("--format");
156+
cmd.arg("json");
157+
cmd.arg(this.name());
158+
cmd.stdout = crate::fakers::FdMode::Pipe;
159+
cmd.stderr = crate::fakers::FdMode::Pipe;
160+
161+
let output = runner.output(cmd).await?;
162+
if !output.status.success() {
163+
return Err(anyhow::anyhow!("Failed to get stats"));
164+
}
165+
166+
let stdout = String::from_utf8(output.stdout)?;
167+
let usages: Vec<Usage> = serde_json::from_str(&stdout)?;
168+
169+
usages.into_iter().next().ok_or_else(|| anyhow::anyhow!("No stats found"))
170+
}
171+
});
172+
141173
this
142174
}
143175

@@ -170,6 +202,10 @@ impl Container {
170202
self.imp().binaries.clone()
171203
}
172204

205+
pub fn usage(&self) -> Query<Usage> {
206+
self.imp().usage.clone()
207+
}
208+
173209
pub fn upgrade(&self) -> DistroboxTask {
174210
let this = self.clone();
175211
self.root_store()

src/container_stats.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use serde::Deserialize;
2+
3+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4+
pub enum ContainerRuntime {
5+
Podman,
6+
Docker,
7+
}
8+
9+
impl ContainerRuntime {
10+
pub fn as_str(&self) -> &'static str {
11+
match self {
12+
ContainerRuntime::Podman => "podman",
13+
ContainerRuntime::Docker => "docker",
14+
}
15+
}
16+
}
17+
18+
#[derive(Debug, Clone, Deserialize, Default)]
19+
pub struct Usage {
20+
#[serde(rename = "mem_usage", alias = "MemUsage")]
21+
pub mem_usage: String,
22+
#[serde(rename = "mem_percent", alias = "MemPerc")]
23+
pub mem_perc: String,
24+
#[serde(rename = "cpu_percent", alias = "CPU")]
25+
pub cpu_perc: String,
26+
#[serde(rename = "net_io", alias = "NetIO")]
27+
pub net_io: String,
28+
#[serde(rename = "block_io", alias = "BlockIO")]
29+
pub block_io: String,
30+
#[serde(rename = "pids", alias = "PIDs")]
31+
pub pids: String,
32+
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
mod application;
2222
mod config;
2323
mod container;
24+
mod container_stats;
2425
mod dialogs;
2526
mod distro_icon;
2627
mod distrobox;

src/store/root_store.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use tracing::info;
1616
use tracing::{debug, warn};
1717

1818
use crate::container::Container;
19+
use crate::container_stats::ContainerRuntime;
1920
use crate::distrobox;
2021
use crate::distrobox::CreateArgs;
2122
use crate::distrobox::Distrobox;
@@ -126,6 +127,7 @@ mod imp {
126127
pub distrobox: OnceCell<crate::distrobox::Distrobox>,
127128
pub terminal_repository: RefCell<TerminalRepository>,
128129
pub command_runner: OnceCell<CommandRunner>,
130+
pub container_runtime: RefCell<Option<ContainerRuntime>>,
129131

130132
pub distrobox_version: Query<String>,
131133
pub images_query: Query<Vec<String>>,
@@ -152,6 +154,7 @@ mod imp {
152154
Self {
153155
containers: TypedListStore::new(),
154156
command_runner: OnceCell::new(),
157+
container_runtime: RefCell::new(None),
155158
terminal_repository: RefCell::new(TerminalRepository::new(
156159
CommandRunner::new_null(),
157160
)),
@@ -668,6 +671,38 @@ impl RootStore {
668671
}
669672
}
670673
}
674+
675+
pub async fn get_container_runtime(&self) -> anyhow::Result<ContainerRuntime> {
676+
if let Some(runtime) = *self.imp().container_runtime.borrow() {
677+
return Ok(runtime);
678+
}
679+
680+
let runner = self.imp().command_runner.get().unwrap();
681+
let mut cmd = Command::new("podman");
682+
cmd.arg("--version");
683+
cmd.stdout = FdMode::Pipe;
684+
cmd.stderr = FdMode::Pipe;
685+
686+
let podman_check = runner.output(cmd).await;
687+
688+
let runtime = if podman_check.is_ok() && podman_check.unwrap().status.success() {
689+
ContainerRuntime::Podman
690+
} else {
691+
let mut cmd = Command::new("docker");
692+
cmd.arg("--version");
693+
cmd.stdout = FdMode::Pipe;
694+
cmd.stderr = FdMode::Pipe;
695+
let docker_check = runner.output(cmd).await;
696+
if docker_check.is_ok() && docker_check.unwrap().status.success() {
697+
ContainerRuntime::Docker
698+
} else {
699+
ContainerRuntime::Podman
700+
}
701+
};
702+
703+
*self.imp().container_runtime.borrow_mut() = Some(runtime);
704+
Ok(runtime)
705+
}
671706
}
672707

673708
impl Default for RootStore {

src/window.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,12 +459,30 @@ impl DistroShelfWindow {
459459
status_row.add_suffix(&status_child);
460460
status_group.add(&status_row);
461461

462+
// Usage stats row
463+
let usage_row = adw::ActionRow::new();
464+
usage_row.set_title("Resources");
465+
usage_row.set_subtitle(&format!("CPU: 0.0% • Mem: 0 (0%)"));
466+
status_group.add(&usage_row);
467+
468+
469+
let usage_query = container.usage();
470+
usage_query.connect_success(clone!(
471+
#[weak] usage_row,
472+
move |usage| {
473+
usage_row.set_subtitle(&format!("CPU: {} • Mem: {} ({})", usage.cpu_perc, usage.mem_usage, usage.mem_perc));
474+
}
475+
));
476+
462477
reaction! {
463478
(container.status_detail(), container.status_tag()),
464479
move |(detail, tag): (String, String)| {
465480
let text = format!("{tag}: {detail}");
466481
status_row.set_subtitle(&text);
467482
stop_btn.set_visible(tag == "up");
483+
if tag == "up" {
484+
usage_query.fetch();
485+
}
468486
}
469487
};
470488

0 commit comments

Comments
 (0)