Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 97 additions & 90 deletions src/main/java/land/oras/CopyUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SourceRefType> source,
SourceRefType sourceRef,
OCI<TargetRefType> 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());
}
}