diff --git a/crates/build/bin/fetch_kernel.rs b/crates/build/bin/fetch_kernel.rs index cd70fbc..fa5d332 100644 --- a/crates/build/bin/fetch_kernel.rs +++ b/crates/build/bin/fetch_kernel.rs @@ -50,7 +50,7 @@ async fn main() -> Result<()> { let (context, _) = OciProgressContext::create(); let service = OciPackerService::new(None, &cache_dir, platform).await?; let packed = service - .request(image.clone(), OciPackedFormat::Tar, false, context) + .request(image.clone(), OciPackedFormat::Tar, false, true, context) .await?; let annotations = packed .manifest diff --git a/crates/ctl/src/cli/image/pull.rs b/crates/ctl/src/cli/image/pull.rs index 0a05008..ab2fa61 100644 --- a/crates/ctl/src/cli/image/pull.rs +++ b/crates/ctl/src/cli/image/pull.rs @@ -38,6 +38,7 @@ impl ImagePullCommand { ImagePullImageFormat::Tar => OciImageFormat::Tar.into(), }, overwrite_cache: self.overwrite_cache, + update: true, }) .await?; let reply = pull_interactive_progress(response.into_inner()).await?; diff --git a/crates/ctl/src/cli/zone/launch.rs b/crates/ctl/src/cli/zone/launch.rs index 452d700..896a620 100644 --- a/crates/ctl/src/cli/zone/launch.rs +++ b/crates/ctl/src/cli/zone/launch.rs @@ -34,6 +34,8 @@ pub struct ZoneLaunchCommand { image_format: LaunchImageFormat, #[arg(long, help = "Overwrite image cache on pull")] pull_overwrite_cache: bool, + #[arg(long, help = "Update image on pull")] + pull_update: bool, #[arg(short, long, help = "Name of the zone")] name: Option, #[arg(short, long, default_value_t = 1, help = "vCPUs available to the zone")] @@ -181,6 +183,7 @@ impl ZoneLaunchCommand { image: image.to_string(), format: format.into(), overwrite_cache: self.pull_overwrite_cache, + update: self.pull_update, }) .await?; let reply = pull_interactive_progress(response.into_inner()).await?; diff --git a/crates/daemon/src/control.rs b/crates/daemon/src/control.rs index fa14cd7..419abab 100644 --- a/crates/daemon/src/control.rs +++ b/crates/daemon/src/control.rs @@ -448,7 +448,7 @@ impl ControlService for DaemonControlService { let output = try_stream! { let mut task = tokio::task::spawn(async move { - our_packer.request(name, format, request.overwrite_cache, context).await + our_packer.request(name, format, request.overwrite_cache, request.update, context).await }); let abort_handle = task.abort_handle(); let _task_cancel_guard = scopeguard::guard(abort_handle, |handle| { diff --git a/crates/krata/proto/krata/v1/control.proto b/crates/krata/proto/krata/v1/control.proto index 5cfccff..e9f9be1 100644 --- a/crates/krata/proto/krata/v1/control.proto +++ b/crates/krata/proto/krata/v1/control.proto @@ -184,6 +184,7 @@ message PullImageRequest { string image = 1; krata.v1.common.OciImageFormat format = 2; bool overwrite_cache = 3; + bool update = 4; } message PullImageReply { diff --git a/crates/oci/examples/squashify.rs b/crates/oci/examples/squashify.rs index c6361c1..e1023fd 100644 --- a/crates/oci/examples/squashify.rs +++ b/crates/oci/examples/squashify.rs @@ -37,7 +37,13 @@ async fn main() -> Result<()> { }); let service = OciPackerService::new(seed, &cache_dir, OciPlatform::current()).await?; let packed = service - .request(image.clone(), OciPackedFormat::Squashfs, false, context) + .request( + image.clone(), + OciPackedFormat::Squashfs, + false, + true, + context, + ) .await?; println!( "generated squashfs of {} to {}", diff --git a/crates/oci/src/packer/cache.rs b/crates/oci/src/packer/cache.rs index 00b020b..f056713 100644 --- a/crates/oci/src/packer/cache.rs +++ b/crates/oci/src/packer/cache.rs @@ -4,6 +4,7 @@ use crate::{ schema::OciSchema, }; +use crate::fetch::OciResolvedImage; use anyhow::Result; use log::{debug, error}; use oci_spec::image::{ @@ -50,6 +51,51 @@ impl OciPackerCache { Ok(index.manifests().clone()) } + pub async fn resolve( + &self, + name: ImageName, + format: OciPackedFormat, + ) -> Result> { + if name.reference.as_deref() == Some("latest") { + return Ok(None); + } + let name_str = name.to_string(); + let index = self.index.read().await; + let mut descriptor: Option = None; + for manifest in index.manifests() { + let Some(name) = manifest + .annotations() + .clone() + .unwrap_or_default() + .get(ANNOTATION_IMAGE_NAME) + .cloned() + else { + continue; + }; + + if name == name_str { + descriptor = Some(manifest.clone()); + } + } + + let Some(descriptor) = descriptor else { + return Ok(None); + }; + + debug!("resolve hit name={} digest={}", name, descriptor.digest()); + + self.recall(name, descriptor.digest().as_ref(), format) + .await + .map(|image| { + image.map(|i| OciResolvedImage { + name: i.name, + digest: i.digest, + descriptor: i.descriptor, + manifest: i.manifest, + }) + }) + } + pub async fn recall( &self, name: ImageName, diff --git a/crates/oci/src/packer/service.rs b/crates/oci/src/packer/service.rs index 8e563a1..c5aebb1 100644 --- a/crates/oci/src/packer/service.rs +++ b/crates/oci/src/packer/service.rs @@ -75,13 +75,23 @@ impl OciPackerService { name: ImageName, format: OciPackedFormat, overwrite: bool, + pull: bool, progress_context: OciProgressContext, ) -> Result { let progress = OciProgress::new(); let progress = OciBoundProgress::new(progress_context.clone(), progress); + let mut resolved = None; + if !pull && !overwrite { + resolved = self.cache.resolve(name.clone(), format).await?; + } let fetcher = OciImageFetcher::new(self.seed.clone(), self.platform.clone(), progress.clone()); - let resolved = fetcher.resolve(name.clone()).await?; + let resolved = if let Some(resolved) = resolved { + resolved + } else { + fetcher.resolve(name.clone()).await? + }; + let key = OciPackerTaskKey { digest: resolved.digest.clone(), format, diff --git a/crates/oci/src/vfs.rs b/crates/oci/src/vfs.rs index e9964ef..8a464d8 100644 --- a/crates/oci/src/vfs.rs +++ b/crates/oci/src/vfs.rs @@ -138,7 +138,7 @@ impl VfsNode { header.set_mode(self.mode); if let Some(link_name) = self.link_name.as_ref() { - header.set_link_name(&PathBuf::from(link_name))?; + header.set_link_name(PathBuf::from(link_name))?; } header.set_size(self.size); Ok(header)