@@ -113,11 +113,16 @@ void copyLayers(
113113 Objects .requireNonNull (layer .getDigest (), "Layer digest is required for streaming copy" );
114114 Objects .requireNonNull (layer .getSize (), "Layer size is required for streaming copy" );
115115 return CompletableFuture .runAsync (
116- () -> target .pushBlob (
117- targetRef .withDigest (layer .getDigest ()),
118- layer .getSize (),
119- () -> source .fetchBlob (sourceRef .withDigest (layer .getDigest ())),
120- layer .getAnnotations ()),
116+ () -> {
117+ if (!tryMountBlob (source , sourceRef , target , targetRef , layer .getDigest ())) {
118+ target .pushBlob (
119+ targetRef .withDigest (layer .getDigest ()),
120+ layer .getSize (),
121+ () -> source .fetchBlob (
122+ sourceRef .withDigest (layer .getDigest ())),
123+ layer .getAnnotations ());
124+ }
125+ },
121126 source .getExecutorService ());
122127 })
123128 .toArray (CompletableFuture []::new ))
@@ -267,11 +272,64 @@ void copyConfig(
267272 Config config = manifest .getConfig ();
268273 Objects .requireNonNull (config .getDigest (), "Config digest is required for streaming copy" );
269274 Objects .requireNonNull (config .getSize (), "Config size is required for streaming copy" );
270- target .pushBlob (
271- targetRef .forTarget (target ).withDigest (manifest .getConfig ().getDigest ()),
272- config .getSize (),
273- () -> source .pullConfig (sourceRef , manifest .getConfig ()),
274- config .getAnnotations ());
275+ TargetRefType configTargetRef =
276+ targetRef .forTarget (target ).withDigest (manifest .getConfig ().getDigest ());
277+ if (!tryMountBlob (source , sourceRef , target , targetRef , config .getDigest ())) {
278+ target .pushBlob (
279+ configTargetRef ,
280+ config .getSize (),
281+ () -> source .pullConfig (sourceRef , manifest .getConfig ()),
282+ config .getAnnotations ());
283+ }
275284 LOG .debug ("Copied config {}" , manifest .getConfig ().getDigest ());
276285 }
286+
287+ /**
288+ * Attempt to mount a blob from source to target without downloading and re-uploading.
289+ * Mounting is only attempted when source and target are the same OCI type and,
290+ * for registries, when they share the same registry host.
291+ * @param source The source OCI
292+ * @param sourceRef The source reference
293+ * @param target The target OCI
294+ * @param targetRef The target reference
295+ * @param digest The digest of the blob to mount
296+ * @param <SourceRefType> The source reference type
297+ * @param <TargetRefType> The target reference type
298+ * @return {@code true} if the blob was successfully mounted, {@code false} if a regular upload is required
299+ */
300+ @ SuppressWarnings ("unchecked" )
301+ private static <
302+ SourceRefType extends Ref <@ NonNull SourceRefType >,
303+ TargetRefType extends Ref <@ NonNull TargetRefType >>
304+ boolean tryMountBlob (
305+ OCI <SourceRefType > source ,
306+ SourceRefType sourceRef ,
307+ OCI <TargetRefType > target ,
308+ TargetRefType targetRef ,
309+ String digest ) {
310+ // Registry-to-Registry mounting: only when pointing at the same registry host
311+ if (source instanceof Registry sourceRegistry && target instanceof Registry targetRegistry ) {
312+ ContainerRef srcRef = (ContainerRef ) sourceRef ;
313+ ContainerRef tgtRef = (ContainerRef ) targetRef ;
314+ String sourceApiRegistry = srcRef .getApiRegistry (sourceRegistry );
315+ String targetApiRegistry = tgtRef .getApiRegistry (targetRegistry );
316+ if (sourceApiRegistry .equals (targetApiRegistry )) {
317+ ContainerRef layerSrcRef = srcRef .withDigest (digest );
318+ ContainerRef layerTgtRef = tgtRef .withDigest (digest );
319+ LOG .debug ("Attempting mount of {} from {} to {}" , digest , srcRef .getFullRepository (),
320+ tgtRef .getFullRepository ());
321+ return targetRegistry .mountBlob (layerTgtRef , layerSrcRef );
322+ }
323+ }
324+ // OCILayout-to-OCILayout mounting: direct file copy between layouts
325+ if (source instanceof OCILayout && target instanceof OCILayout targetLayout ) {
326+ LayoutRef srcRef = (LayoutRef ) sourceRef ;
327+ LayoutRef tgtRef = (LayoutRef ) targetRef ;
328+ LayoutRef layerSrcRef = srcRef .withDigest (digest );
329+ LayoutRef layerTgtRef = tgtRef .withDigest (digest );
330+ LOG .debug ("Attempting mount of {} from {} to {}" , digest , srcRef .getFolder (), tgtRef .getFolder ());
331+ return targetLayout .mountBlob (layerTgtRef , layerSrcRef );
332+ }
333+ return false ;
334+ }
277335}
0 commit comments