diff --git a/src/main/java/land/oras/CopyUtils.java b/src/main/java/land/oras/CopyUtils.java index 3366066a..439fd973 100644 --- a/src/main/java/land/oras/CopyUtils.java +++ b/src/main/java/land/oras/CopyUtils.java @@ -20,8 +20,6 @@ * =LICENSEEND= */ -import java.io.IOException; -import java.io.InputStream; import java.util.Objects; import land.oras.exception.OrasException; import org.jspecify.annotations.NonNull; @@ -63,106 +61,115 @@ void copy( TargetRefType targetRef, boolean recursive) { - try { - - Descriptor descriptor = source.probeDescriptor(sourceRef); - - // Get the resolved source registry - String resolveSourceRegistry = descriptor.getRegistry(); - Objects.requireNonNull(resolveSourceRegistry, "Registry is required for streaming copy"); - - // Get the resolve target registry - String effectiveTargetRegistry = targetRef.getTarget(target); - Objects.requireNonNull(effectiveTargetRegistry, "Target registry is required for streaming copy"); - - String contentType = descriptor.getMediaType(); - String manifestDigest = descriptor.getDigest(); - LOG.debug("Content type: {}", contentType); - LOG.debug("Manifest digest: {}", manifestDigest); - - // Write all layer - for (Layer layer : source.collectLayers(sourceRef, contentType, true)) { - Objects.requireNonNull(layer.getDigest(), "Layer digest is required for streaming copy"); - Objects.requireNonNull(layer.getSize(), "Layer size is required for streaming copy"); - LOG.debug("Copying layer {}", layer.getDigest()); - target.pushBlob( - targetRef.forTarget(effectiveTargetRegistry).withDigest(layer.getDigest()), - layer.getSize(), - () -> source.fetchBlob( - sourceRef.forTarget(resolveSourceRegistry).withDigest(layer.getDigest())), - layer.getAnnotations()); - LOG.debug("Copied layer {}", layer.getDigest()); - } + Descriptor descriptor = source.probeDescriptor(sourceRef); + + // Get the resolved source registry + String resolveSourceRegistry = descriptor.getRegistry(); + Objects.requireNonNull(resolveSourceRegistry, "Registry is required for streaming copy"); + + // Get the resolve target registry + String effectiveTargetRegistry = targetRef.getTarget(target); + Objects.requireNonNull(effectiveTargetRegistry, "Target registry is required for streaming copy"); + + String contentType = descriptor.getMediaType(); + String manifestDigest = descriptor.getDigest(); + LOG.debug("Content type: {}", contentType); + LOG.debug("Manifest digest: {}", manifestDigest); + + // Write all layer + for (Layer layer : source.collectLayers(sourceRef, contentType, true)) { + Objects.requireNonNull(layer.getDigest(), "Layer digest is required for streaming copy"); + Objects.requireNonNull(layer.getSize(), "Layer size is required for streaming copy"); + LOG.debug("Copying layer {}", layer.getDigest()); + target.pushBlob( + targetRef.forTarget(effectiveTargetRegistry).withDigest(layer.getDigest()), + layer.getSize(), + () -> source.fetchBlob( + sourceRef.forTarget(resolveSourceRegistry).withDigest(layer.getDigest())), + layer.getAnnotations()); + LOG.debug("Copied layer {}", layer.getDigest()); + } - // Single manifest - if (source.isManifestMediaType(contentType)) { + // Single manifest + if (source.isManifestMediaType(contentType)) { - // Write manifest as any blob - Manifest manifest = source.getManifest(sourceRef); - String tag = sourceRef.getTag(); + // Write manifest as any blob + Manifest manifest = source.getManifest(sourceRef); + String tag = sourceRef.getTag(); - Objects.requireNonNull(manifest.getDigest(), "Manifest digest is required for streaming copy"); + Objects.requireNonNull(manifest.getDigest(), "Manifest digest is required for streaming copy"); - // Write config as any blob - Config config = manifest.getConfig(); - Objects.requireNonNull(config.getDigest(), "Config digest is required for streaming copy"); - Objects.requireNonNull(config.getSize(), "Config size is required for streaming copy"); - target.pushBlob( - targetRef - .forTarget(effectiveTargetRegistry) - .withDigest(manifest.getConfig().getDigest()), - config.getSize(), - () -> source.pullConfig(sourceRef.forTarget(resolveSourceRegistry), manifest.getConfig()), - config.getAnnotations()); + // Push config + copyConfig(manifest, resolveSourceRegistry, effectiveTargetRegistry, source, sourceRef, target, targetRef); - // Push the manifest - LOG.debug("Copying manifest {}", manifestDigest); - target.pushManifest(targetRef.withDigest(tag), manifest); - LOG.debug("Copied manifest {}", manifestDigest); + // Push the manifest + LOG.debug("Copying manifest {}", manifestDigest); + target.pushManifest(targetRef.withDigest(tag), manifest); + LOG.debug("Copied manifest {}", manifestDigest); - if (recursive) { - LOG.debug("Recursively copy referrers"); - Referrers referrers = source.getReferrers(sourceRef.withDigest(manifestDigest), null); - for (ManifestDescriptor referer : referrers.getManifests()) { - LOG.info("Copy reference {}", referer.getDigest()); - copy(source, sourceRef.withDigest(referer.getDigest()), target, targetRef, recursive); - } + if (recursive) { + LOG.debug("Recursively copy referrers"); + Referrers referrers = source.getReferrers(sourceRef.withDigest(manifestDigest), null); + for (ManifestDescriptor referer : referrers.getManifests()) { + LOG.info("Copy reference {}", referer.getDigest()); + copy(source, sourceRef.withDigest(referer.getDigest()), target, targetRef, recursive); } - } - // Index - else if (source.isIndexMediaType(contentType)) { - - Index index = source.getIndex(sourceRef); - String tag = sourceRef.getTag(); - - // Write all manifests and their config - for (ManifestDescriptor manifestDescriptor : index.getManifests()) { - Manifest manifest = source.getManifest(sourceRef.withDigest(manifestDescriptor.getDigest())); - - // Write config as any blob - try (InputStream is = source.pullConfig(sourceRef, manifest.getConfig())) { - LOG.debug("Copying config blob {}", manifest.getConfig().getDigest()); - target.pushBlob( - targetRef.withDigest(manifest.getConfig().getDigest()), is); - LOG.debug("Copied config blob {}", manifest.getConfig().getDigest()); - } - - // Push the manifest - target.pushManifest( - targetRef.withDigest(manifest.getDigest()), manifest.withDescriptor(manifestDescriptor)); - } - LOG.debug("Copying index {}", manifestDigest); - target.pushIndex(targetRef.withDigest(tag), index); - LOG.debug("Copied index {}", manifestDigest); + } + // Index + else if (source.isIndexMediaType(contentType)) { + + Index index = source.getIndex(sourceRef); + String tag = sourceRef.getTag(); + + // Write all manifests and their config + for (ManifestDescriptor manifestDescriptor : index.getManifests()) { + Manifest manifest = source.getManifest(sourceRef.withDigest(manifestDescriptor.getDigest())); - } else { - throw new OrasException("Unsupported content type: %s".formatted(contentType)); + // Push config + copyConfig( + manifest, resolveSourceRegistry, effectiveTargetRegistry, source, sourceRef, target, targetRef); + + // Push the manifest + LOG.debug("Copying manifest {}", manifestDigest); + target.pushManifest( + targetRef.withDigest(manifest.getDigest()), manifest.withDescriptor(manifestDescriptor)); + LOG.debug("Copied manifest {}", manifestDigest); } - } catch (IOException e) { - throw new OrasException("Failed to copy container", e); + LOG.debug("Copying index {}", manifestDigest); + target.pushIndex(targetRef.withDigest(tag), index); + LOG.debug("Copied index {}", manifestDigest); + + } else { + throw new OrasException("Unsupported content type: %s".formatted(contentType)); } } + + private static < + SourceRefType extends Ref<@NonNull SourceRefType>, + TargetRefType extends Ref<@NonNull TargetRefType>> + void copyConfig( + Manifest manifest, + String resolvedSourceRegistry, + String effectiveTargetRegistry, + OCI source, + SourceRefType sourceRef, + OCI target, + TargetRefType targetRef) { + // Write config as any blob + LOG.debug("Copying config {}", manifest.getConfig().getDigest()); + Config config = manifest.getConfig(); + Objects.requireNonNull(config.getDigest(), "Config digest is required for streaming copy"); + Objects.requireNonNull(config.getSize(), "Config size is required for streaming copy"); + target.pushBlob( + targetRef + .forTarget(effectiveTargetRegistry) + .withDigest(manifest.getConfig().getDigest()), + config.getSize(), + () -> source.pullConfig(sourceRef.forTarget(resolvedSourceRegistry), manifest.getConfig()), + config.getAnnotations()); + LOG.debug("Copied config {}", manifest.getConfig().getDigest()); + } }