chore: merge release-v0.10.5 into main

This commit is contained in:
Jack Wills
2025-06-19 21:32:07 +00:00
11 changed files with 271 additions and 284 deletions
+3 -23
View File
@@ -1,26 +1,6 @@
### 2025-06-18 ### 2025-06-19
### Chores ### Reverts
+ .devcontainer updated, [324f8268278081504d5357f2ed89b78ca2c25d04] + Bollard update rolled back, closes #66, [aac9c6b598ce6c23b14f5a8b0116e662b18074d2]
+ dependencies updated, [0ace9dd662144a589341779a64d7fcd8de7d9978], [a636007547280b3b3db69374601dbece4bc21eef]
+ Rust 1.87.0 linting, [395b1aa7e997a528e4f21e66f5f859001c1c3ec1], [67e5888e008cfd504c10e47f678f9351c838be99]
### Docs
+ example config files updated, [63ab7de72897de460f31181c5a42befbee2f91d3], [8fb5ac4a945b75f3fcd118c53be1202ccbc43c59]
+ README.md updated, link to directories crate, closes #65, [c2bfe3296563daf4b7f077469f3eeff6895720b0]
### Features
+ log panel size configurable, closes #50, use the `-` or `=` keys to change the height of the logs panel, or `\` to toggle visibility. Automatically hide the logs panel using a new config item `show_logs`, see `example_config/*` files for more details, [6edf99e0846bb4134d8ee5b646065b8cda8074d7]
+ build release binaries for aarch64-apple-darwin, closes #62, personally untested on MacOS - but others suggest it works as expected, [e7114d2f5e0ed8935943be64726fc2d90464a777], [2e8500902a515a246f9d9a503b4350849d634978]
### Fixes
+ merge args color/raw fix, [d198398795698a21d81d3fd20231c482cc346ab5]
### Refactors
+ reduce cloning of the logs text items, can expect 40-50% reduction in CPU and memory usage in certain common situations, [ecefa302b9ef5320ad4cce0b606aca70a7b459e2]
+ dead code removed, [b40b6b197e4e5fbdab083bc918d1a5d2750597f3]
### Tests
+ add more whole layout tests, [4b81c6caaf12028d7527c3f23cd2de6d1503e223]
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
+6
View File
@@ -1,3 +1,9 @@
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.10.5'>v0.10.5</a>
### 2025-06-19
### Reverts
+ Bollard update rolled back, closes [#66](https://github.com/mrjackwills/oxker/issues/66), [aac9c6b5](https://github.com/mrjackwills/oxker/commit/aac9c6b598ce6c23b14f5a8b0116e662b18074d2)
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.10.4'>v0.10.4</a> # <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.10.4'>v0.10.4</a>
### 2025-06-18 ### 2025-06-18
Generated
+5 -6
View File
@@ -129,9 +129,9 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]] [[package]]
name = "bollard" name = "bollard"
version = "0.19.1" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899ca34eb6924d6ec2a77c6f7f5c7339e60fd68235eaf91edd5a15f12958bb06" checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30"
dependencies = [ dependencies = [
"base64", "base64",
"bollard-stubs", "bollard-stubs",
@@ -162,12 +162,11 @@ dependencies = [
[[package]] [[package]]
name = "bollard-stubs" name = "bollard-stubs"
version = "1.48.3-rc.28.0.4" version = "1.47.1-rc.27.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ea257e555d16a2c01e5593f40b73865cdf12efbceda33c6d14a2d8d1490368" checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json",
"serde_repr", "serde_repr",
"serde_with", "serde_with",
] ]
@@ -1174,7 +1173,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "oxker" name = "oxker"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bollard", "bollard",
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "oxker" name = "oxker"
version = "0.10.4" version = "0.10.5"
edition = "2024" edition = "2024"
authors = ["Jack Wills <email@mrjackwills.com>"] authors = ["Jack Wills <email@mrjackwills.com>"]
description = "A simple tui to view & control docker containers" description = "A simple tui to view & control docker containers"
@@ -27,7 +27,7 @@ similar_names = "allow"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
bollard = "0.19" bollard = "0.18"
cansi = "2.2" cansi = "2.2"
clap = { version = "4.5", features = ["color", "derive", "unicode"] } clap = { version = "4.5", features = ["color", "derive", "unicode"] }
crossterm = "0.29" crossterm = "0.29"
+3 -2
View File
@@ -104,7 +104,6 @@ ask_changelog_update() {
else else
exit exit
fi fi
} }
# Edit the release-body to include new lines from changelog # Edit the release-body to include new lines from changelog
@@ -239,7 +238,6 @@ zig_build_aarch64_apple() {
echo -e "${YELLOW}sudo chown -R vscode:vscode $(pwd)/target${RESET}" echo -e "${YELLOW}sudo chown -R vscode:vscode $(pwd)/target${RESET}"
sudo chown -R vscode:vscode "$(pwd)/target" sudo chown -R vscode:vscode "$(pwd)/target"
fi fi
} }
# Build all releases that GitHub workflow would # Build all releases that GitHub workflow would
@@ -290,12 +288,14 @@ build_container_amd64() {
docker build --platform linux/amd64 --no-cache -t oxker_amd64 -f containerised/Dockerfile . docker build --platform linux/amd64 --no-cache -t oxker_amd64 -f containerised/Dockerfile .
docker save -o /tmp/oxker_amd64.tar oxker_amd64 docker save -o /tmp/oxker_amd64.tar oxker_amd64
} }
# build container for aarm64 platform # build container for aarm64 platform
build_container_arm64() { build_container_arm64() {
echo -e "${YELLOW}docker build --platform linux/arm64 --no-cache -t oxker_arm64 --no-cache -f containerised/Dockerfile .; docker save -o /tmp/oxker_arm64.tar oxker_arm64${RESET}" echo -e "${YELLOW}docker build --platform linux/arm64 --no-cache -t oxker_arm64 --no-cache -f containerised/Dockerfile .; docker save -o /tmp/oxker_arm64.tar oxker_arm64${RESET}"
docker build --platform linux/arm64 --no-cache -t oxker_arm64 -f containerised/Dockerfile . docker build --platform linux/arm64 --no-cache -t oxker_arm64 -f containerised/Dockerfile .
docker save -o /tmp/oxker_arm64.tar oxker_arm64 docker save -o /tmp/oxker_arm64.tar oxker_arm64
} }
# build container for armv6 platform # build container for armv6 platform
build_container_armv6() { build_container_armv6() {
echo -e "${YELLOW}docker build --platform linux/arm/v6 --no-cache -t oxker_armv6 --no-cache -f containerised/Dockerfile .; docker save -o /tmp/oxker_armv6.tar oxker_armv6${RESET}" echo -e "${YELLOW}docker build --platform linux/arm/v6 --no-cache -t oxker_armv6 --no-cache -f containerised/Dockerfile .; docker save -o /tmp/oxker_armv6.tar oxker_armv6${RESET}"
@@ -310,6 +310,7 @@ build_container_all() {
build_container_arm64 build_container_arm64
ask_continue ask_continue
build_container_armv6 build_container_armv6
ask_continue
} }
# Full flow to create a new release # Full flow to create a new release
+2 -2
View File
@@ -2,7 +2,7 @@
FROM alpine:latest FROM alpine:latest
# Install a simple utility (e.g., curl) to run as a health check # Install a simple utility (e.g., curl) to run as a health check
RUN apk --no-cache add curl RUN apk --no-cache add curl speedtest-cli
# Create a dummy file that we will use in our health check # Create a dummy file that we will use in our health check
RUN touch /tmp/healthy RUN touch /tmp/healthy
@@ -12,6 +12,6 @@ HEALTHCHECK --interval=5s --timeout=3s --retries=3 \
CMD [ ! -f /tmp/healthy ] || exit 1 CMD [ ! -f /tmp/healthy ] || exit 1
# Start a basic loop that keeps the container running # Start a basic loop that keeps the container running
CMD ["sh", "-c", "while :; do >&2 echo 'Container is running but will be unhealthy (also printing to stderr)'; sleep 30; done"] CMD ["sh", "-c", "speedtest-cli; while :; do >&2 echo 'Container is running but will be unhealthy (also printing to stderr)'; sleep 30; done"]
# docker build -t unhealthy-container . -f Dockerfile.unhealthy; docker run -d --name unhealthy unhealthy-container # docker build -t unhealthy-container . -f Dockerfile.unhealthy; docker run -d --name unhealthy unhealthy-container
-48
View File
@@ -342,54 +342,6 @@ impl From<(&str, &ContainerStatus)> for State {
} }
} }
/// Need status, to check if container is unhealthy or not
impl
From<(
&bollard::secret::ContainerSummaryStateEnum,
&ContainerStatus,
)> for State
{
fn from(
(input, status): (
&bollard::secret::ContainerSummaryStateEnum,
&ContainerStatus,
),
) -> Self {
match input {
bollard::secret::ContainerSummaryStateEnum::DEAD => Self::Dead,
bollard::secret::ContainerSummaryStateEnum::EXITED => Self::Exited,
bollard::secret::ContainerSummaryStateEnum::PAUSED => Self::Paused,
bollard::secret::ContainerSummaryStateEnum::REMOVING => Self::Removing,
bollard::secret::ContainerSummaryStateEnum::RESTARTING => Self::Restarting,
bollard::secret::ContainerSummaryStateEnum::RUNNING => {
if status.unhealthy() {
Self::Running(RunningState::Unhealthy)
} else {
Self::Running(RunningState::Healthy)
}
}
_ => Self::Unknown,
}
}
}
/// Again, need status, to check if container is unhealthy or not
impl
From<(
Option<&bollard::secret::ContainerSummaryStateEnum>,
&ContainerStatus,
)> for State
{
fn from(
(input, status): (
Option<&bollard::secret::ContainerSummaryStateEnum>,
&ContainerStatus,
),
) -> Self {
input.map_or(Self::Unknown, |input| Self::from((input, status)))
}
}
/// Again, need status, to check if container is unhealthy or not /// Again, need status, to check if container is unhealthy or not
impl From<(Option<String>, &ContainerStatus)> for State { impl From<(Option<String>, &ContainerStatus)> for State {
fn from((input, status): (Option<String>, &ContainerStatus)) -> Self { fn from((input, status): (Option<String>, &ContainerStatus)) -> Self {
+1 -7
View File
@@ -881,13 +881,7 @@ impl AppData {
.as_ref() .as_ref()
.map_or(String::new(), std::clone::Clone::clone), .map_or(String::new(), std::clone::Clone::clone),
); );
let state = State::from((i.state.as_ref().map_or("dead", |z| z), &status));
let state = State::from((
i.state
.as_ref()
.map_or(&bollard::secret::ContainerSummaryStateEnum::DEAD, |z| z),
&status,
));
let image = i let image = i
.image .image
.as_ref() .as_ref()
+236 -180
View File
@@ -1,7 +1,9 @@
use bollard::{ use bollard::{
Docker, Docker,
query_parameters::{ListContainersOptions, LogsOptions, RemoveContainerOptions, StatsOptions}, container::{
secret::ContainerStatsResponse, ListContainersOptions, LogsOptions, MemoryStatsStats, RemoveContainerOptions,
StartContainerOptions, Stats, StatsOptions,
},
service::ContainerSummary, service::ContainerSummary,
}; };
use futures_util::StreamExt; use futures_util::StreamExt;
@@ -73,44 +75,31 @@ pub struct DockerData {
impl DockerData { impl DockerData {
/// Use docker stats to calculate current cpu usage /// Use docker stats to calculate current cpu usage
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn calculate_usage(stats: &ContainerStatsResponse) -> f64 { fn calculate_usage(stats: &Stats) -> f64 {
let mut cpu_percentage = 0.0; let mut cpu_percentage = 0.0;
let cpu_delta = stats
.cpu_stats
.cpu_usage
.total_usage
.saturating_sub(stats.precpu_stats.cpu_usage.total_usage)
as f64;
let total_usage = stats.precpu_stats.as_ref().map_or(0, |i| { if let (Some(cpu_stats_usage), Some(precpu_stats_usage)) = (
i.cpu_usage stats.cpu_stats.system_cpu_usage,
.as_ref() stats.precpu_stats.system_cpu_usage,
.map_or(0, |i| i.total_usage.unwrap_or_default())
});
let cpu_delta = stats.cpu_stats.as_ref().map_or(0, |i| {
i.cpu_usage.as_ref().map_or(0, |i| {
i.total_usage
.unwrap_or_default()
.saturating_sub(total_usage)
})
}) as f64;
if let (Some(Some(cpu_stats_usage)), Some(Some(precpu_stats_usage))) = (
stats.cpu_stats.as_ref().map(|i| i.system_cpu_usage),
stats.precpu_stats.as_ref().map(|i| i.system_cpu_usage),
) { ) {
let system_delta = cpu_stats_usage.saturating_sub(precpu_stats_usage) as f64; let system_delta = cpu_stats_usage.saturating_sub(precpu_stats_usage) as f64;
let online_cpus = f64::from(stats.cpu_stats.as_ref().map_or(0, |i| { let online_cpus = stats.cpu_stats.online_cpus.unwrap_or_else(|| {
i.online_cpus.unwrap_or_else(|| { u64::try_from(
u32::try_from(
stats stats
.cpu_stats .cpu_stats
.clone()
.unwrap_or_default()
.cpu_usage .cpu_usage
.unwrap_or_default()
.percpu_usage .percpu_usage
.as_ref() .as_ref()
.map_or(0, std::vec::Vec::len), .map_or(0, std::vec::Vec::len),
) )
.unwrap_or_default() .unwrap_or_default()
}) }) as f64;
}));
if system_delta > 0.0 && cpu_delta > 0.0 { if system_delta > 0.0 && cpu_delta > 0.0 {
cpu_percentage = (cpu_delta / system_delta) * online_cpus * 100.0; cpu_percentage = (cpu_delta / system_delta) * online_cpus * 100.0;
} }
@@ -142,23 +131,20 @@ impl DockerData {
) )
.take(1); .take(1);
// some err here
while let Some(Ok(stats)) = stream.next().await { while let Some(Ok(stats)) = stream.next().await {
// Memory stats are only collected if the container is alive - is this the behaviour we want? // Memory stats are only collected if the container is alive - is this the behaviour we want?
let (mem_stat, cpu_stats) = if state.is_alive() { let (mem_stat, cpu_stats) = if state.is_alive() {
let mem_cache = stats.memory_stats.as_ref().map_or(&0, |i| { let mem_cache = stats.memory_stats.stats.map_or(0, |i| match i {
i.stats MemoryStatsStats::V1(x) => x.inactive_file,
.as_ref() MemoryStatsStats::V2(x) => x.inactive_file,
.map_or(&0, |i| i.get("inactive_file").unwrap_or(&0))
}); });
( (
Some( Some(
stats stats
.memory_stats .memory_stats
.as_ref() .usage
.map_or(0, |i| i.usage.unwrap_or_default()) .unwrap_or_default()
.saturating_sub(*mem_cache), .saturating_sub(mem_cache),
), ),
Some(Self::calculate_usage(&stats)), Some(Self::calculate_usage(&stats)),
) )
@@ -166,22 +152,26 @@ impl DockerData {
(None, None) (None, None)
}; };
let (rx, tx) = stats.networks.as_ref().map_or((0, 0), |i| { let op_key = stats
( .networks
i.rx_bytes.unwrap_or_default(), .as_ref()
i.tx_bytes.unwrap_or_default(), .and_then(|networks| networks.keys().next().cloned());
)
}); let (rx, tx) = if let Some(key) = op_key {
stats
.networks
.unwrap_or_default()
.get(&key)
.map_or((0, 0), |f| (f.rx_bytes, f.tx_bytes))
} else {
(0, 0)
};
app_data.lock().update_stats_by_id( app_data.lock().update_stats_by_id(
id, id,
cpu_stats, cpu_stats,
mem_stat, mem_stat,
stats stats.memory_stats.limit.unwrap_or_default(),
.memory_stats
.unwrap_or_default()
.limit
.unwrap_or_default(),
rx, rx,
tx, tx,
); );
@@ -216,7 +206,7 @@ impl DockerData {
async fn update_all_containers(&self) { async fn update_all_containers(&self) {
let containers = self let containers = self
.docker .docker
.list_containers(Some(ListContainersOptions { .list_containers(Some(ListContainersOptions::<String> {
all: true, all: true,
..Default::default() ..Default::default()
})) }))
@@ -241,7 +231,6 @@ impl DockerData {
None => None, None => None,
}) })
.collect::<Vec<ContainerSummary>>(); .collect::<Vec<ContainerSummary>>();
self.app_data.lock().update_containers(output); self.app_data.lock().update_containers(output);
} }
@@ -255,11 +244,11 @@ impl DockerData {
spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>, spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>,
stderr: bool, stderr: bool,
) { ) {
let options = Some(LogsOptions { let options = Some(LogsOptions::<String> {
stdout: true, stdout: true,
stderr, stderr,
timestamps: true, timestamps: true,
since: i32::try_from(since).unwrap_or_default(), since: i64::try_from(since).unwrap_or_default(),
..Default::default() ..Default::default()
}); });
@@ -376,31 +365,14 @@ impl DockerData {
.await .await
} }
DockerCommand::Pause => docker.pause_container(id.get()).await, DockerCommand::Pause => docker.pause_container(id.get()).await,
DockerCommand::Restart => { DockerCommand::Restart => docker.restart_container(id.get(), None).await,
docker
.restart_container(
id.get(),
None::<bollard::query_parameters::RestartContainerOptions>,
)
.await
}
DockerCommand::Resume => docker.unpause_container(id.get()).await, DockerCommand::Resume => docker.unpause_container(id.get()).await,
DockerCommand::Start => { DockerCommand::Start => {
docker docker
.start_container( .start_container(id.get(), None::<StartContainerOptions<String>>)
id.get(),
None::<bollard::query_parameters::StartContainerOptions>,
)
.await
}
DockerCommand::Stop => {
docker
.stop_container(
id.get(),
None::<bollard::query_parameters::StopContainerOptions>,
)
.await .await
} }
DockerCommand::Stop => docker.stop_container(id.get(), None).await,
} }
.is_err() .is_err()
{ {
@@ -476,72 +448,119 @@ impl DockerData {
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
mod tests { mod tests {
use bollard::secret::{ContainerCpuStats, ContainerCpuUsage}; use bollard::container::{
BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, Stats, StorageStats, ThrottlingData,
};
use super::*; use super::*;
fn gen_stats() -> ContainerStatsResponse { fn gen_stats() -> Stats {
ContainerStatsResponse { Stats {
read: None, read: String::new(),
preread: None, preread: String::new(),
num_procs: Some(1), num_procs: 1,
pids_stats: None, pids_stats: PidsStats {
current: None,
limit: None,
},
network: None,
networks: None, networks: None,
memory_stats: None, memory_stats: MemoryStats {
blkio_stats: None, stats: None,
cpu_stats: Some(ContainerCpuStats { max_usage: None,
cpu_usage: Some(ContainerCpuUsage { usage: None,
failcnt: None,
limit: None,
commit: None,
commit_peak: None,
commitbytes: None,
commitpeakbytes: None,
privateworkingset: None,
},
blkio_stats: BlkioStats {
io_service_bytes_recursive: None,
io_serviced_recursive: None,
io_queue_recursive: None,
io_service_time_recursive: None,
io_wait_time_recursive: None,
io_merged_recursive: None,
io_time_recursive: None,
sectors_recursive: None,
},
cpu_stats: CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}), periods: 0,
precpu_stats: Some(ContainerCpuStats { throttled_periods: 0,
cpu_usage: Some(ContainerCpuUsage { throttled_time: 0,
},
},
precpu_stats: CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}), periods: 0,
storage_stats: None, throttled_periods: 0,
name: None, throttled_time: 0,
id: None, },
},
storage_stats: StorageStats {
read_count_normalized: None,
read_size_bytes: None,
write_count_normalized: None,
write_size_bytes: None,
},
name: String::new(),
id: String::new(),
} }
} }
#[test] #[test]
fn test_calculate_usage_50() { fn test_calculate_usage_50() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = Some(ContainerCpuStats { stats.precpu_stats = CPUStats {
cpu_usage: Some(ContainerCpuUsage { cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
stats.cpu_stats = Some(ContainerCpuStats { throttled_periods: 0,
cpu_usage: Some(ContainerCpuUsage { throttled_time: 0,
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![150]), percpu_usage: Some(vec![150]),
usage_in_usermode: Some(20), usage_in_usermode: 20,
total_usage: Some(150), total_usage: 150,
usage_in_kernelmode: Some(30), usage_in_kernelmode: 30,
}), },
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(50.0, cpu_percentage); assert_eq!(50.0, cpu_percentage);
} }
@@ -549,28 +568,37 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_25() { fn test_calculate_usage_25() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = Some(ContainerCpuStats { stats.precpu_stats = CPUStats {
cpu_usage: Some(ContainerCpuUsage { cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
stats.cpu_stats = Some(ContainerCpuStats { throttled_periods: 0,
cpu_usage: Some(ContainerCpuUsage { throttled_time: 0,
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![75]), percpu_usage: Some(vec![75]),
usage_in_usermode: Some(20), usage_in_usermode: 20,
total_usage: Some(125), total_usage: 125,
usage_in_kernelmode: Some(30), usage_in_kernelmode: 30,
}), },
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(25.0, cpu_percentage); assert_eq!(25.0, cpu_percentage);
} }
@@ -578,28 +606,38 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_75() { fn test_calculate_usage_75() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = Some(ContainerCpuStats { stats.precpu_stats = CPUStats {
cpu_usage: Some(ContainerCpuUsage { cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
stats.cpu_stats = Some(ContainerCpuStats { throttled_periods: 0,
cpu_usage: Some(ContainerCpuUsage { throttled_time: 0,
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![175]), percpu_usage: Some(vec![175]),
usage_in_usermode: Some(20), usage_in_usermode: 20,
total_usage: Some(175), total_usage: 175,
usage_in_kernelmode: Some(30), usage_in_kernelmode: 30,
}), },
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(75.0, cpu_percentage); assert_eq!(75.0, cpu_percentage);
} }
@@ -607,28 +645,36 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_100() { fn test_calculate_usage_100() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = Some(ContainerCpuStats { stats.precpu_stats = CPUStats {
cpu_usage: Some(ContainerCpuUsage { cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
stats.cpu_stats = Some(ContainerCpuStats { throttled_periods: 0,
cpu_usage: Some(ContainerCpuUsage { throttled_time: 0,
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![200]), percpu_usage: Some(vec![200]),
usage_in_usermode: Some(20), usage_in_usermode: 20,
total_usage: Some(200), total_usage: 200,
usage_in_kernelmode: Some(30), usage_in_kernelmode: 30,
}), },
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(100.0, cpu_percentage); assert_eq!(100.0, cpu_percentage);
} }
@@ -636,28 +682,38 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_175() { fn test_calculate_usage_175() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = Some(ContainerCpuStats { stats.precpu_stats = CPUStats {
cpu_usage: Some(ContainerCpuUsage { cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: Some(10), usage_in_usermode: 10,
total_usage: Some(100), total_usage: 100,
usage_in_kernelmode: Some(20), usage_in_kernelmode: 20,
}), },
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
stats.cpu_stats = Some(ContainerCpuStats { throttled_periods: 0,
cpu_usage: Some(ContainerCpuUsage { throttled_time: 0,
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![275]), percpu_usage: Some(vec![275]),
usage_in_usermode: Some(20), usage_in_usermode: 20,
total_usage: Some(275), total_usage: 275,
usage_in_kernelmode: Some(30), usage_in_kernelmode: 30,
}), },
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: None, throttling_data: ThrottlingData {
}); periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(175.0, cpu_percentage); assert_eq!(175.0, cpu_percentage);
} }
+2 -2
View File
@@ -5,7 +5,7 @@ use std::{
time::SystemTime, time::SystemTime,
}; };
use bollard::query_parameters::LogsOptions; use bollard::container::LogsOptions;
// use bollard::container::LogsOptions; // use bollard::container::LogsOptions;
use cansi::v3::categorise_text; use cansi::v3::categorise_text;
use crossterm::{ use crossterm::{
@@ -188,7 +188,7 @@ impl InputHandler {
let path = log_path.join(format!("{name}_{now}.log")); let path = log_path.join(format!("{name}_{now}.log"));
let options = Some(LogsOptions { let options = Some(LogsOptions::<String> {
stderr: true, stderr: true,
stdout: true, stdout: true,
timestamps: args.show_timestamp, timestamps: args.show_timestamp,
+2 -3
View File
@@ -149,7 +149,7 @@ async fn main() {
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
mod tests { mod tests {
use std::{str::FromStr, sync::Arc}; use std::sync::Arc;
use bollard::service::{ContainerSummary, Port}; use bollard::service::{ContainerSummary, Port};
@@ -228,7 +228,6 @@ mod tests {
pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary { pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary {
ContainerSummary { ContainerSummary {
image_manifest_descriptor: None,
id: Some(format!("{index}")), id: Some(format!("{index}")),
names: Some(vec![format!("container_{}", index)]), names: Some(vec![format!("container_{}", index)]),
image: Some(format!("image_{index}")), image: Some(format!("image_{index}")),
@@ -244,7 +243,7 @@ mod tests {
size_rw: None, size_rw: None,
size_root_fs: None, size_root_fs: None,
labels: None, labels: None,
state: Some(bollard::secret::ContainerSummaryStateEnum::from_str(state).unwrap()), state: Some(state.to_owned()),
status: Some(format!("Up {index} hour")), status: Some(format!("Up {index} hour")),
host_config: None, host_config: None,
network_settings: None, network_settings: None,