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 for InspectContainer { #[instrument(name = "InspectContainer", level = "debug")] async fn resolve( self, _: &super::Args, ) -> serror::Result { Ok(docker_client().inspect_container(&self.name).await?) } } // impl Resolve for GetContainerLog { #[instrument(name = "GetContainerLog", level = "debug")] async fn resolve(self, _: &super::Args) -> serror::Result { 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 for GetContainerLogSearch { #[instrument(name = "GetContainerLogSearch", level = "debug")] async fn resolve(self, _: &super::Args) -> serror::Result { 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 for GetContainerStats { #[instrument(name = "GetContainerStats", level = "debug")] async fn resolve( self, _: &super::Args, ) -> serror::Result { let mut stats = get_container_stats(Some(self.name)).await?; let stats = stats.pop().context("No stats found for container")?; Ok(stats) } } // impl Resolve for GetFullContainerStats { #[instrument(name = "GetFullContainerStats", level = "debug")] async fn resolve( self, _: &super::Args, ) -> serror::Result { docker_client() .full_container_stats(&self.name) .await .map_err(Into::into) } } // impl Resolve for GetContainerStatsList { #[instrument(name = "GetContainerStatsList", level = "debug")] async fn resolve( self, _: &super::Args, ) -> serror::Result> { Ok(get_container_stats(None).await?) } } // ========= // ACTIONS // ========= impl Resolve for StartContainer { #[instrument(name = "StartContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "Docker Start", None, format!("docker start {}", self.name), ) .await, ) } } // impl Resolve for RestartContainer { #[instrument(name = "RestartContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "Docker Restart", None, format!("docker restart {}", self.name), ) .await, ) } } // impl Resolve for PauseContainer { #[instrument(name = "PauseContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "Docker Pause", None, format!("docker pause {}", self.name), ) .await, ) } } impl Resolve for UnpauseContainer { #[instrument(name = "UnpauseContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "Docker Unpause", None, format!("docker unpause {}", self.name), ) .await, ) } } // impl Resolve for StopContainer { #[instrument(name = "StopContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { 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 for RemoveContainer { #[instrument(name = "RemoveContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { 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 for RenameContainer { #[instrument(name = "RenameContainer")] async fn resolve(self, _: &super::Args) -> serror::Result { 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 for PruneContainers { #[instrument(name = "PruneContainers", skip_all)] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker container prune -f"); Ok(run_komodo_command("Prune Containers", None, command).await) } } // impl Resolve for StartAllContainers { #[instrument(name = "StartAllContainers", skip_all)] async fn resolve( self, _: &super::Args, ) -> serror::Result> { 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 for RestartAllContainers { #[instrument(name = "RestartAllContainers", skip_all)] async fn resolve( self, _: &super::Args, ) -> serror::Result> { 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 for PauseAllContainers { #[instrument(name = "PauseAllContainers", skip_all)] async fn resolve( self, _: &super::Args, ) -> serror::Result> { 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 for UnpauseAllContainers { #[instrument(name = "UnpauseAllContainers", skip_all)] async fn resolve( self, _: &super::Args, ) -> serror::Result> { 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 for StopAllContainers { #[instrument(name = "StopAllContainers", skip_all)] async fn resolve( self, _: &super::Args, ) -> serror::Result> { 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) } }