use std::sync::OnceLock; use cache::TimeoutCache; use command::run_komodo_command; use komodo_client::entities::{ deployment::extract_registry_domain, docker::image::{Image, ImageHistoryResponseItem}, komodo_timestamp, update::Log, }; use periphery_client::api::image::*; use resolver_api::Resolve; use crate::docker::{docker_client, docker_login}; // impl Resolve for InspectImage { #[instrument(name = "InspectImage", level = "debug")] async fn resolve(self, _: &super::Args) -> serror::Result { Ok(docker_client().inspect_image(&self.name).await?) } } // impl Resolve for ImageHistory { #[instrument(name = "ImageHistory", level = "debug")] async fn resolve( self, _: &super::Args, ) -> serror::Result> { Ok(docker_client().image_history(&self.name).await?) } } // /// Wait this long after a pull to allow another pull through const PULL_TIMEOUT: i64 = 5_000; fn pull_cache() -> &'static TimeoutCache { static PULL_CACHE: OnceLock> = OnceLock::new(); PULL_CACHE.get_or_init(Default::default) } impl Resolve for PullImage { #[instrument(name = "PullImage", skip_all, fields(name = &self.name))] async fn resolve(self, _: &super::Args) -> serror::Result { let PullImage { name, account, token, } = self; // Acquire the image lock let lock = pull_cache().get_lock(name.clone()).await; // Lock the image lock, prevents simultaneous pulls by // ensuring simultaneous pulls will wait for first to finish // and checking cached results. let mut locked = lock.lock().await; // Early return from cache if lasted pulled with PULL_TIMEOUT if locked.last_ts + PULL_TIMEOUT > komodo_timestamp() { return locked.clone_res().map_err(Into::into); } let res = async { docker_login( &extract_registry_domain(&name)?, account.as_deref().unwrap_or_default(), token.as_deref(), ) .await?; anyhow::Ok( run_komodo_command( "Docker Pull", None, format!("docker pull {name}"), ) .await, ) } .await; // Set the cache with results. Any other calls waiting on the lock will // then immediately also use this same result. locked.set(&res, komodo_timestamp()); res.map_err(Into::into) } } // impl Resolve for DeleteImage { #[instrument(name = "DeleteImage")] async fn resolve(self, _: &super::Args) -> serror::Result { let command = format!("docker image rm {}", self.name); Ok(run_komodo_command("Delete Image", None, command).await) } } // impl Resolve for PruneImages { #[instrument(name = "PruneImages")] async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker image prune -a -f"); Ok(run_komodo_command("Prune Images", None, command).await) } }