@@ -6,7 +6,7 @@ use std::collections::HashSet;
66use std:: io:: { BufRead , Write } ;
77
88use anyhow:: Ok ;
9- use anyhow:: { anyhow , Context , Result } ;
9+ use anyhow:: { Context , Result , anyhow } ;
1010use cap_std:: fs:: { Dir , MetadataExt } ;
1111use cap_std_ext:: cap_std;
1212use cap_std_ext:: dirext:: CapStdExtDirExt ;
@@ -380,6 +380,112 @@ 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
404+ . pull ( & image_ref_str, crate :: podstorage:: PullMode :: Always )
405+ . await ?;
406+
407+ // Now create a containers-storage reference to read from bootc storage
408+ tracing:: info!( "Unified pull: now importing from containers-storage transport" ) ;
409+ let containers_storage_imgref = ImageReference {
410+ transport : "containers-storage" . to_string ( ) ,
411+ image : imgref. image . clone ( ) ,
412+ signature : imgref. signature . clone ( ) ,
413+ } ;
414+ let ostree_imgref = OstreeImageReference :: from ( containers_storage_imgref) ;
415+
416+ // Use the standard preparation flow but reading from containers-storage
417+ let mut imp = new_importer ( repo, & ostree_imgref) . await ?;
418+ if let Some ( target) = target_imgref {
419+ imp. set_target ( target) ;
420+ }
421+ let prep = match imp. prepare ( ) . await ? {
422+ PrepareResult :: AlreadyPresent ( c) => {
423+ println ! ( "No changes in {imgref:#} => {}" , c. manifest_digest) ;
424+ return Ok ( PreparedPullResult :: AlreadyPresent ( Box :: new ( ( * c) . into ( ) ) ) ) ;
425+ }
426+ PrepareResult :: Ready ( p) => p,
427+ } ;
428+ check_bootc_label ( & prep. config ) ;
429+ if let Some ( warning) = prep. deprecated_warning ( ) {
430+ ostree_ext:: cli:: print_deprecated_warning ( warning) . await ;
431+ }
432+ ostree_ext:: cli:: print_layer_status ( & prep) ;
433+ let layers_to_fetch = prep. layers_to_fetch ( ) . collect :: < Result < Vec < _ > > > ( ) ?;
434+
435+ // Log that we're importing a new image from containers-storage
436+ const PULLING_NEW_IMAGE_ID : & str = "6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0" ;
437+ tracing:: info!(
438+ message_id = PULLING_NEW_IMAGE_ID ,
439+ bootc. image. reference = & imgref. image,
440+ bootc. image. transport = "containers-storage" ,
441+ bootc. original_transport = & imgref. transport,
442+ bootc. status = "importing_from_storage" ,
443+ "Importing image from bootc storage: {}" ,
444+ ostree_imgref
445+ ) ;
446+
447+ let prepared_image = PreparedImportMeta {
448+ imp,
449+ n_layers_to_fetch : layers_to_fetch. len ( ) ,
450+ layers_total : prep. all_layers ( ) . count ( ) ,
451+ bytes_to_fetch : layers_to_fetch. iter ( ) . map ( |( l, _) | l. layer . size ( ) ) . sum ( ) ,
452+ bytes_total : prep. all_layers ( ) . map ( |l| l. layer . size ( ) ) . sum ( ) ,
453+ digest : prep. manifest_digest . clone ( ) ,
454+ prep,
455+ } ;
456+
457+ Ok ( PreparedPullResult :: Ready ( Box :: new ( prepared_image) ) )
458+ }
459+
460+ /// Unified pull: Use podman to pull to containers-storage, then read from there
461+ pub ( crate ) async fn pull_unified (
462+ repo : & ostree:: Repo ,
463+ imgref : & ImageReference ,
464+ target_imgref : Option < & OstreeImageReference > ,
465+ quiet : bool ,
466+ prog : ProgressWriter ,
467+ store : & Storage ,
468+ ) -> Result < Box < ImageState > > {
469+ match prepare_for_pull_unified ( repo, imgref, target_imgref, store) . await ? {
470+ PreparedPullResult :: AlreadyPresent ( existing) => {
471+ // Log that the image was already present (Debug level since it's not actionable)
472+ const IMAGE_ALREADY_PRESENT_ID : & str = "5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9" ;
473+ tracing:: debug!(
474+ message_id = IMAGE_ALREADY_PRESENT_ID ,
475+ bootc. image. reference = & imgref. image,
476+ bootc. image. transport = & imgref. transport,
477+ bootc. status = "already_present" ,
478+ "Image already present: {}" ,
479+ imgref
480+ ) ;
481+ Ok ( existing)
482+ }
483+ PreparedPullResult :: Ready ( prepared_image_meta) => {
484+ pull_from_prepared ( imgref, quiet, prog, * prepared_image_meta) . await
485+ }
486+ }
487+ }
488+
383489#[ context( "Pulling" ) ]
384490pub ( crate ) async fn pull_from_prepared (
385491 imgref : & ImageReference ,
@@ -429,18 +535,21 @@ pub(crate) async fn pull_from_prepared(
429535 let imgref_canonicalized = imgref. clone ( ) . canonicalize ( ) ?;
430536 tracing:: debug!( "Canonicalized image reference: {imgref_canonicalized:#}" ) ;
431537
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- ) ;
538+ // Log successful import completion (skip if using unified storage to avoid double logging)
539+ let is_unified_path = imgref. transport == "containers-storage" ;
540+ if !is_unified_path {
541+ const IMPORT_COMPLETE_JOURNAL_ID : & str = "4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8" ;
542+
543+ tracing:: info!(
544+ message_id = IMPORT_COMPLETE_JOURNAL_ID ,
545+ bootc. image. reference = & imgref. image,
546+ bootc. image. transport = & imgref. transport,
547+ bootc. manifest_digest = import. manifest_digest. as_ref( ) ,
548+ bootc. ostree_commit = & import. merge_commit,
549+ "Successfully imported image: {}" ,
550+ imgref
551+ ) ;
552+ }
444553
445554 if let Some ( msg) =
446555 ostree_container:: store:: image_filtered_content_warning ( & import. filtered_files )
0 commit comments