@@ -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" ) ]
384488pub ( 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