Skip to content

Commit 9c3ef81

Browse files
committed
WIP: Use our container-storage for host images if the refspec exists on it
Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
1 parent 24f2dd0 commit 9c3ef81

File tree

2 files changed

+137
-14
lines changed

2 files changed

+137
-14
lines changed

crates/lib/src/cli.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,15 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
928928
}
929929
}
930930
} else {
931-
let fetched = crate::deploy::pull(repo, imgref, None, opts.quiet, prog.clone()).await?;
931+
// Check if image exists in bootc storage (/usr/lib/bootc/storage)
932+
let imgstore = sysroot.get_ensure_imgstore()?;
933+
let use_unified = imgstore.exists(&format!("{imgref:#}")).await.unwrap_or(false);
934+
935+
let fetched = if use_unified {
936+
crate::deploy::pull_unified(repo, imgref, None, opts.quiet, prog.clone(), sysroot).await?
937+
} else {
938+
crate::deploy::pull(repo, imgref, None, opts.quiet, prog.clone()).await?
939+
};
932940
let staged_digest = staged_image.map(|s| s.digest().expect("valid digest in status"));
933941
let fetched_digest = &fetched.manifest_digest;
934942
tracing::debug!("staged: {staged_digest:?}");
@@ -1056,7 +1064,15 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
10561064

10571065
let new_spec = RequiredHostSpec::from_spec(&new_spec)?;
10581066

1059-
let fetched = crate::deploy::pull(repo, &target, None, opts.quiet, prog.clone()).await?;
1067+
// Check if image exists in bootc storage (/usr/lib/bootc/storage)
1068+
let imgstore = sysroot.get_ensure_imgstore()?;
1069+
let use_unified = imgstore.exists(&format!("{target:#}")).await.unwrap_or(false);
1070+
1071+
let fetched = if use_unified {
1072+
crate::deploy::pull_unified(repo, &target, None, opts.quiet, prog.clone(), sysroot).await?
1073+
} else {
1074+
crate::deploy::pull(repo, &target, None, opts.quiet, prog.clone()).await?
1075+
};
10601076

10611077
if !opts.retain {
10621078
// By default, we prune the previous ostree ref so it will go away after later upgrades

crates/lib/src/deploy.rs

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,110 @@ pub(crate) async fn prepare_for_pull(
380380
Ok(PreparedPullResult::Ready(Box::new(prepared_image)))
381381
}
382382

383+
/// Unified approach: Use bootc's CStorage to pull the image, then prepare from containers-storage.
384+
/// This reuses the same infrastructure as LBIs.
385+
pub(crate) async fn prepare_for_pull_unified(
386+
repo: &ostree::Repo,
387+
imgref: &ImageReference,
388+
target_imgref: Option<&OstreeImageReference>,
389+
store: &Storage,
390+
) -> Result<PreparedPullResult> {
391+
// Get or initialize the bootc container storage (same as used for LBIs)
392+
let imgstore = store.get_ensure_imgstore()?;
393+
394+
let image_ref_str = format!("{imgref:#}");
395+
396+
// Log the original transport being used for the pull
397+
tracing::info!(
398+
"Unified pull: pulling from transport '{}' to bootc storage",
399+
&imgref.transport
400+
);
401+
402+
// Pull the image to bootc storage using the same method as LBIs
403+
imgstore.pull(&image_ref_str, crate::podstorage::PullMode::Always).await?;
404+
405+
// Now create a containers-storage reference to read from bootc storage
406+
tracing::info!("Unified pull: now importing from containers-storage transport");
407+
let containers_storage_imgref = ImageReference {
408+
transport: "containers-storage".to_string(),
409+
image: imgref.image.clone(),
410+
signature: imgref.signature.clone(),
411+
};
412+
let ostree_imgref = OstreeImageReference::from(containers_storage_imgref);
413+
414+
// Use the standard preparation flow but reading from containers-storage
415+
let mut imp = new_importer(repo, &ostree_imgref).await?;
416+
if let Some(target) = target_imgref {
417+
imp.set_target(target);
418+
}
419+
let prep = match imp.prepare().await? {
420+
PrepareResult::AlreadyPresent(c) => {
421+
println!("No changes in {imgref:#} => {}", c.manifest_digest);
422+
return Ok(PreparedPullResult::AlreadyPresent(Box::new((*c).into())));
423+
}
424+
PrepareResult::Ready(p) => p,
425+
};
426+
check_bootc_label(&prep.config);
427+
if let Some(warning) = prep.deprecated_warning() {
428+
ostree_ext::cli::print_deprecated_warning(warning).await;
429+
}
430+
ostree_ext::cli::print_layer_status(&prep);
431+
let layers_to_fetch = prep.layers_to_fetch().collect::<Result<Vec<_>>>()?;
432+
433+
// Log that we're importing a new image from containers-storage
434+
const PULLING_NEW_IMAGE_ID: &str = "6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0";
435+
tracing::info!(
436+
message_id = PULLING_NEW_IMAGE_ID,
437+
bootc.image.reference = &imgref.image,
438+
bootc.image.transport = "containers-storage",
439+
bootc.original_transport = &imgref.transport,
440+
bootc.status = "importing_from_storage",
441+
"Importing image from bootc storage: {}",
442+
ostree_imgref
443+
);
444+
445+
let prepared_image = PreparedImportMeta {
446+
imp,
447+
n_layers_to_fetch: layers_to_fetch.len(),
448+
layers_total: prep.all_layers().count(),
449+
bytes_to_fetch: layers_to_fetch.iter().map(|(l, _)| l.layer.size()).sum(),
450+
bytes_total: prep.all_layers().map(|l| l.layer.size()).sum(),
451+
digest: prep.manifest_digest.clone(),
452+
prep,
453+
};
454+
455+
Ok(PreparedPullResult::Ready(Box::new(prepared_image)))
456+
}
457+
458+
/// Unified pull: Use podman to pull to containers-storage, then read from there
459+
pub(crate) async fn pull_unified(
460+
repo: &ostree::Repo,
461+
imgref: &ImageReference,
462+
target_imgref: Option<&OstreeImageReference>,
463+
quiet: bool,
464+
prog: ProgressWriter,
465+
store: &Storage,
466+
) -> Result<Box<ImageState>> {
467+
match prepare_for_pull_unified(repo, imgref, target_imgref, store).await? {
468+
PreparedPullResult::AlreadyPresent(existing) => {
469+
// Log that the image was already present (Debug level since it's not actionable)
470+
const IMAGE_ALREADY_PRESENT_ID: &str = "5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9";
471+
tracing::debug!(
472+
message_id = IMAGE_ALREADY_PRESENT_ID,
473+
bootc.image.reference = &imgref.image,
474+
bootc.image.transport = &imgref.transport,
475+
bootc.status = "already_present",
476+
"Image already present: {}",
477+
imgref
478+
);
479+
Ok(existing)
480+
}
481+
PreparedPullResult::Ready(prepared_image_meta) => {
482+
pull_from_prepared(imgref, quiet, prog, *prepared_image_meta).await
483+
}
484+
}
485+
}
486+
383487
#[context("Pulling")]
384488
pub(crate) async fn pull_from_prepared(
385489
imgref: &ImageReference,
@@ -429,18 +533,21 @@ pub(crate) async fn pull_from_prepared(
429533
let imgref_canonicalized = imgref.clone().canonicalize()?;
430534
tracing::debug!("Canonicalized image reference: {imgref_canonicalized:#}");
431535

432-
// Log successful import completion
433-
const IMPORT_COMPLETE_JOURNAL_ID: &str = "4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8";
434-
435-
tracing::info!(
436-
message_id = IMPORT_COMPLETE_JOURNAL_ID,
437-
bootc.image.reference = &imgref.image,
438-
bootc.image.transport = &imgref.transport,
439-
bootc.manifest_digest = import.manifest_digest.as_ref(),
440-
bootc.ostree_commit = &import.merge_commit,
441-
"Successfully imported image: {}",
442-
imgref
443-
);
536+
// Log successful import completion (skip if using unified storage to avoid double logging)
537+
let is_unified_path = imgref.transport == "containers-storage";
538+
if !is_unified_path {
539+
const IMPORT_COMPLETE_JOURNAL_ID: &str = "4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8";
540+
541+
tracing::info!(
542+
message_id = IMPORT_COMPLETE_JOURNAL_ID,
543+
bootc.image.reference = &imgref.image,
544+
bootc.image.transport = &imgref.transport,
545+
bootc.manifest_digest = import.manifest_digest.as_ref(),
546+
bootc.ostree_commit = &import.merge_commit,
547+
"Successfully imported image: {}",
548+
imgref
549+
);
550+
}
444551

445552
if let Some(msg) =
446553
ostree_container::store::image_filtered_content_warning(&import.filtered_files)

0 commit comments

Comments
 (0)