Skip to content

Commit 0b79b88

Browse files
authored
Use streaming copy for config during copy and ensure to set target (2) and extract to copy method (#559)
2 parents 9277ec6 + ba88bcf commit 0b79b88

1 file changed

Lines changed: 97 additions & 90 deletions

File tree

src/main/java/land/oras/CopyUtils.java

Lines changed: 97 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
* =LICENSEEND=
2121
*/
2222

23-
import java.io.IOException;
24-
import java.io.InputStream;
2523
import java.util.Objects;
2624
import land.oras.exception.OrasException;
2725
import org.jspecify.annotations.NonNull;
@@ -63,106 +61,115 @@ void copy(
6361
TargetRefType targetRef,
6462
boolean recursive) {
6563

66-
try {
67-
68-
Descriptor descriptor = source.probeDescriptor(sourceRef);
69-
70-
// Get the resolved source registry
71-
String resolveSourceRegistry = descriptor.getRegistry();
72-
Objects.requireNonNull(resolveSourceRegistry, "Registry is required for streaming copy");
73-
74-
// Get the resolve target registry
75-
String effectiveTargetRegistry = targetRef.getTarget(target);
76-
Objects.requireNonNull(effectiveTargetRegistry, "Target registry is required for streaming copy");
77-
78-
String contentType = descriptor.getMediaType();
79-
String manifestDigest = descriptor.getDigest();
80-
LOG.debug("Content type: {}", contentType);
81-
LOG.debug("Manifest digest: {}", manifestDigest);
82-
83-
// Write all layer
84-
for (Layer layer : source.collectLayers(sourceRef, contentType, true)) {
85-
Objects.requireNonNull(layer.getDigest(), "Layer digest is required for streaming copy");
86-
Objects.requireNonNull(layer.getSize(), "Layer size is required for streaming copy");
87-
LOG.debug("Copying layer {}", layer.getDigest());
88-
target.pushBlob(
89-
targetRef.forTarget(effectiveTargetRegistry).withDigest(layer.getDigest()),
90-
layer.getSize(),
91-
() -> source.fetchBlob(
92-
sourceRef.forTarget(resolveSourceRegistry).withDigest(layer.getDigest())),
93-
layer.getAnnotations());
94-
LOG.debug("Copied layer {}", layer.getDigest());
95-
}
64+
Descriptor descriptor = source.probeDescriptor(sourceRef);
65+
66+
// Get the resolved source registry
67+
String resolveSourceRegistry = descriptor.getRegistry();
68+
Objects.requireNonNull(resolveSourceRegistry, "Registry is required for streaming copy");
69+
70+
// Get the resolve target registry
71+
String effectiveTargetRegistry = targetRef.getTarget(target);
72+
Objects.requireNonNull(effectiveTargetRegistry, "Target registry is required for streaming copy");
73+
74+
String contentType = descriptor.getMediaType();
75+
String manifestDigest = descriptor.getDigest();
76+
LOG.debug("Content type: {}", contentType);
77+
LOG.debug("Manifest digest: {}", manifestDigest);
78+
79+
// Write all layer
80+
for (Layer layer : source.collectLayers(sourceRef, contentType, true)) {
81+
Objects.requireNonNull(layer.getDigest(), "Layer digest is required for streaming copy");
82+
Objects.requireNonNull(layer.getSize(), "Layer size is required for streaming copy");
83+
LOG.debug("Copying layer {}", layer.getDigest());
84+
target.pushBlob(
85+
targetRef.forTarget(effectiveTargetRegistry).withDigest(layer.getDigest()),
86+
layer.getSize(),
87+
() -> source.fetchBlob(
88+
sourceRef.forTarget(resolveSourceRegistry).withDigest(layer.getDigest())),
89+
layer.getAnnotations());
90+
LOG.debug("Copied layer {}", layer.getDigest());
91+
}
9692

97-
// Single manifest
98-
if (source.isManifestMediaType(contentType)) {
93+
// Single manifest
94+
if (source.isManifestMediaType(contentType)) {
9995

100-
// Write manifest as any blob
101-
Manifest manifest = source.getManifest(sourceRef);
102-
String tag = sourceRef.getTag();
96+
// Write manifest as any blob
97+
Manifest manifest = source.getManifest(sourceRef);
98+
String tag = sourceRef.getTag();
10399

104-
Objects.requireNonNull(manifest.getDigest(), "Manifest digest is required for streaming copy");
100+
Objects.requireNonNull(manifest.getDigest(), "Manifest digest is required for streaming copy");
105101

106-
// Write config as any blob
107-
Config config = manifest.getConfig();
108-
Objects.requireNonNull(config.getDigest(), "Config digest is required for streaming copy");
109-
Objects.requireNonNull(config.getSize(), "Config size is required for streaming copy");
110-
target.pushBlob(
111-
targetRef
112-
.forTarget(effectiveTargetRegistry)
113-
.withDigest(manifest.getConfig().getDigest()),
114-
config.getSize(),
115-
() -> source.pullConfig(sourceRef.forTarget(resolveSourceRegistry), manifest.getConfig()),
116-
config.getAnnotations());
102+
// Push config
103+
copyConfig(manifest, resolveSourceRegistry, effectiveTargetRegistry, source, sourceRef, target, targetRef);
117104

118-
// Push the manifest
119-
LOG.debug("Copying manifest {}", manifestDigest);
120-
target.pushManifest(targetRef.withDigest(tag), manifest);
121-
LOG.debug("Copied manifest {}", manifestDigest);
105+
// Push the manifest
106+
LOG.debug("Copying manifest {}", manifestDigest);
107+
target.pushManifest(targetRef.withDigest(tag), manifest);
108+
LOG.debug("Copied manifest {}", manifestDigest);
122109

123-
if (recursive) {
124-
LOG.debug("Recursively copy referrers");
125-
Referrers referrers = source.getReferrers(sourceRef.withDigest(manifestDigest), null);
126-
for (ManifestDescriptor referer : referrers.getManifests()) {
127-
LOG.info("Copy reference {}", referer.getDigest());
128-
copy(source, sourceRef.withDigest(referer.getDigest()), target, targetRef, recursive);
129-
}
110+
if (recursive) {
111+
LOG.debug("Recursively copy referrers");
112+
Referrers referrers = source.getReferrers(sourceRef.withDigest(manifestDigest), null);
113+
for (ManifestDescriptor referer : referrers.getManifests()) {
114+
LOG.info("Copy reference {}", referer.getDigest());
115+
copy(source, sourceRef.withDigest(referer.getDigest()), target, targetRef, recursive);
130116
}
131-
132117
}
133-
// Index
134-
else if (source.isIndexMediaType(contentType)) {
135-
136-
Index index = source.getIndex(sourceRef);
137-
String tag = sourceRef.getTag();
138-
139-
// Write all manifests and their config
140-
for (ManifestDescriptor manifestDescriptor : index.getManifests()) {
141-
Manifest manifest = source.getManifest(sourceRef.withDigest(manifestDescriptor.getDigest()));
142-
143-
// Write config as any blob
144-
try (InputStream is = source.pullConfig(sourceRef, manifest.getConfig())) {
145-
LOG.debug("Copying config blob {}", manifest.getConfig().getDigest());
146-
target.pushBlob(
147-
targetRef.withDigest(manifest.getConfig().getDigest()), is);
148-
LOG.debug("Copied config blob {}", manifest.getConfig().getDigest());
149-
}
150-
151-
// Push the manifest
152-
target.pushManifest(
153-
targetRef.withDigest(manifest.getDigest()), manifest.withDescriptor(manifestDescriptor));
154-
}
155118

156-
LOG.debug("Copying index {}", manifestDigest);
157-
target.pushIndex(targetRef.withDigest(tag), index);
158-
LOG.debug("Copied index {}", manifestDigest);
119+
}
120+
// Index
121+
else if (source.isIndexMediaType(contentType)) {
122+
123+
Index index = source.getIndex(sourceRef);
124+
String tag = sourceRef.getTag();
125+
126+
// Write all manifests and their config
127+
for (ManifestDescriptor manifestDescriptor : index.getManifests()) {
128+
Manifest manifest = source.getManifest(sourceRef.withDigest(manifestDescriptor.getDigest()));
159129

160-
} else {
161-
throw new OrasException("Unsupported content type: %s".formatted(contentType));
130+
// Push config
131+
copyConfig(
132+
manifest, resolveSourceRegistry, effectiveTargetRegistry, source, sourceRef, target, targetRef);
133+
134+
// Push the manifest
135+
LOG.debug("Copying manifest {}", manifestDigest);
136+
target.pushManifest(
137+
targetRef.withDigest(manifest.getDigest()), manifest.withDescriptor(manifestDescriptor));
138+
LOG.debug("Copied manifest {}", manifestDigest);
162139
}
163140

164-
} catch (IOException e) {
165-
throw new OrasException("Failed to copy container", e);
141+
LOG.debug("Copying index {}", manifestDigest);
142+
target.pushIndex(targetRef.withDigest(tag), index);
143+
LOG.debug("Copied index {}", manifestDigest);
144+
145+
} else {
146+
throw new OrasException("Unsupported content type: %s".formatted(contentType));
166147
}
167148
}
149+
150+
private static <
151+
SourceRefType extends Ref<@NonNull SourceRefType>,
152+
TargetRefType extends Ref<@NonNull TargetRefType>>
153+
void copyConfig(
154+
Manifest manifest,
155+
String resolvedSourceRegistry,
156+
String effectiveTargetRegistry,
157+
OCI<SourceRefType> source,
158+
SourceRefType sourceRef,
159+
OCI<TargetRefType> target,
160+
TargetRefType targetRef) {
161+
// Write config as any blob
162+
LOG.debug("Copying config {}", manifest.getConfig().getDigest());
163+
Config config = manifest.getConfig();
164+
Objects.requireNonNull(config.getDigest(), "Config digest is required for streaming copy");
165+
Objects.requireNonNull(config.getSize(), "Config size is required for streaming copy");
166+
target.pushBlob(
167+
targetRef
168+
.forTarget(effectiveTargetRegistry)
169+
.withDigest(manifest.getConfig().getDigest()),
170+
config.getSize(),
171+
() -> source.pullConfig(sourceRef.forTarget(resolvedSourceRegistry), manifest.getConfig()),
172+
config.getAnnotations());
173+
LOG.debug("Copied config {}", manifest.getConfig().getDigest());
174+
}
168175
}

0 commit comments

Comments
 (0)