Files
komodo/bin/periphery/src/api/container.rs
T
2025-09-27 14:21:11 -07:00

415 lines
10 KiB
Rust

use anyhow::Context;
use command::run_komodo_command;
use futures::future::join_all;
use komodo_client::entities::{
docker::{
container::{Container, ContainerListItem, ContainerStats},
stats::FullContainerStats,
},
update::Log,
};
use periphery_client::api::container::*;
use resolver_api::Resolve;
use crate::{
docker::{
docker_client, stats::get_container_stats, stop_container_command,
},
helpers::format_log_grep,
};
// ======
// READ
// ======
//
impl Resolve<super::Args> for InspectContainer {
#[instrument(name = "InspectContainer", level = "debug")]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Container> {
Ok(docker_client().inspect_container(&self.name).await?)
}
}
//
impl Resolve<super::Args> for GetContainerLog {
#[instrument(name = "GetContainerLog", level = "debug")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
let GetContainerLog {
name,
tail,
timestamps,
} = self;
let timestamps = if timestamps {
" --timestamps"
} else {
Default::default()
};
let command =
format!("docker logs {name} --tail {tail}{timestamps}");
Ok(run_komodo_command("Get container log", None, command).await)
}
}
//
impl Resolve<super::Args> for GetContainerLogSearch {
#[instrument(name = "GetContainerLogSearch", level = "debug")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
let GetContainerLogSearch {
name,
terms,
combinator,
invert,
timestamps,
} = self;
let grep = format_log_grep(&terms, combinator, invert);
let timestamps = if timestamps {
" --timestamps"
} else {
Default::default()
};
let command = format!(
"docker logs {name} --tail 5000{timestamps} 2>&1 | {grep}"
);
Ok(
run_komodo_command("Get container log grep", None, command)
.await,
)
}
}
//
impl Resolve<super::Args> for GetContainerStats {
#[instrument(name = "GetContainerStats", level = "debug")]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<ContainerStats> {
let mut stats = get_container_stats(Some(self.name)).await?;
let stats =
stats.pop().context("No stats found for container")?;
Ok(stats)
}
}
//
impl Resolve<super::Args> for GetFullContainerStats {
#[instrument(name = "GetFullContainerStats", level = "debug")]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<FullContainerStats> {
docker_client()
.full_container_stats(&self.name)
.await
.map_err(Into::into)
}
}
//
impl Resolve<super::Args> for GetContainerStatsList {
#[instrument(name = "GetContainerStatsList", level = "debug")]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Vec<ContainerStats>> {
Ok(get_container_stats(None).await?)
}
}
// =========
// ACTIONS
// =========
impl Resolve<super::Args> for StartContainer {
#[instrument(name = "StartContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"Docker Start",
None,
format!("docker start {}", self.name),
)
.await,
)
}
}
//
impl Resolve<super::Args> for RestartContainer {
#[instrument(name = "RestartContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"Docker Restart",
None,
format!("docker restart {}", self.name),
)
.await,
)
}
}
//
impl Resolve<super::Args> for PauseContainer {
#[instrument(name = "PauseContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"Docker Pause",
None,
format!("docker pause {}", self.name),
)
.await,
)
}
}
impl Resolve<super::Args> for UnpauseContainer {
#[instrument(name = "UnpauseContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
Ok(
run_komodo_command(
"Docker Unpause",
None,
format!("docker unpause {}", self.name),
)
.await,
)
}
}
//
impl Resolve<super::Args> for StopContainer {
#[instrument(name = "StopContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
let StopContainer { name, signal, time } = self;
let command = stop_container_command(&name, signal, time);
let log = run_komodo_command("Docker Stop", None, command).await;
if log.stderr.contains("unknown flag: --signal") {
let command = stop_container_command(&name, None, time);
let mut log =
run_komodo_command("Docker Stop", None, command).await;
log.stderr = format!(
"old docker version: unable to use --signal flag{}",
if !log.stderr.is_empty() {
format!("\n\n{}", log.stderr)
} else {
String::new()
}
);
Ok(log)
} else {
Ok(log)
}
}
}
//
impl Resolve<super::Args> for RemoveContainer {
#[instrument(name = "RemoveContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
let RemoveContainer { name, signal, time } = self;
let stop_command = stop_container_command(&name, signal, time);
let command =
format!("{stop_command} && docker container rm {name}");
let log =
run_komodo_command("Docker Stop and Remove", None, command)
.await;
if log.stderr.contains("unknown flag: --signal") {
let stop_command = stop_container_command(&name, None, time);
let command =
format!("{stop_command} && docker container rm {name}");
let mut log =
run_komodo_command("Docker Stop and Remove", None, command)
.await;
log.stderr = format!(
"Old docker version: unable to use --signal flag{}",
if !log.stderr.is_empty() {
format!("\n\n{}", log.stderr)
} else {
String::new()
}
);
Ok(log)
} else {
Ok(log)
}
}
}
//
impl Resolve<super::Args> for RenameContainer {
#[instrument(name = "RenameContainer")]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
let RenameContainer {
curr_name,
new_name,
} = self;
let command = format!("docker rename {curr_name} {new_name}");
Ok(run_komodo_command("Docker Rename", None, command).await)
}
}
//
impl Resolve<super::Args> for PruneContainers {
#[instrument(name = "PruneContainers", skip_all)]
async fn resolve(self, _: &super::Args) -> serror::Result<Log> {
let command = String::from("docker container prune -f");
Ok(run_komodo_command("Prune Containers", None, command).await)
}
}
//
impl Resolve<super::Args> for StartAllContainers {
#[instrument(name = "StartAllContainers", skip_all)]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Vec<Log>> {
let containers = docker_client()
.list_containers()
.await
.context("failed to list all containers on host")?;
let futures = containers.iter().filter_map(
|ContainerListItem { name, labels, .. }| {
if labels.contains_key("komodo.skip") {
return None;
}
let command = format!("docker start {name}");
Some(async move {
run_komodo_command(&command.clone(), None, command).await
})
},
);
Ok(join_all(futures).await)
}
}
//
impl Resolve<super::Args> for RestartAllContainers {
#[instrument(name = "RestartAllContainers", skip_all)]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Vec<Log>> {
let containers = docker_client()
.list_containers()
.await
.context("failed to list all containers on host")?;
let futures = containers.iter().filter_map(
|ContainerListItem { name, labels, .. }| {
if labels.contains_key("komodo.skip") {
return None;
}
let command = format!("docker restart {name}");
Some(async move {
run_komodo_command(&command.clone(), None, command).await
})
},
);
Ok(join_all(futures).await)
}
}
//
impl Resolve<super::Args> for PauseAllContainers {
#[instrument(name = "PauseAllContainers", skip_all)]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Vec<Log>> {
let containers = docker_client()
.list_containers()
.await
.context("failed to list all containers on host")?;
let futures = containers.iter().filter_map(
|ContainerListItem { name, labels, .. }| {
if labels.contains_key("komodo.skip") {
return None;
}
let command = format!("docker pause {name}");
Some(async move {
run_komodo_command(&command.clone(), None, command).await
})
},
);
Ok(join_all(futures).await)
}
}
//
impl Resolve<super::Args> for UnpauseAllContainers {
#[instrument(name = "UnpauseAllContainers", skip_all)]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Vec<Log>> {
let containers = docker_client()
.list_containers()
.await
.context("failed to list all containers on host")?;
let futures = containers.iter().filter_map(
|ContainerListItem { name, labels, .. }| {
if labels.contains_key("komodo.skip") {
return None;
}
let command = format!("docker unpause {name}");
Some(async move {
run_komodo_command(&command.clone(), None, command).await
})
},
);
Ok(join_all(futures).await)
}
}
//
impl Resolve<super::Args> for StopAllContainers {
#[instrument(name = "StopAllContainers", skip_all)]
async fn resolve(
self,
_: &super::Args,
) -> serror::Result<Vec<Log>> {
let containers = docker_client()
.list_containers()
.await
.context("failed to list all containers on host")?;
let futures = containers.iter().filter_map(
|ContainerListItem { name, labels, .. }| {
if labels.contains_key("komodo.skip") {
return None;
}
Some(async move {
run_komodo_command(
&format!("docker stop {name}"),
None,
stop_container_command(name, None, None),
)
.await
})
},
);
Ok(join_all(futures).await)
}
}