Skip to content

Commit f089fdb

Browse files
authored
Add utility method on ManifestDescriptor to construct manifest descriptor with Platform and Annotation from Manifest (#591)
Signed-off-by: Valentin Delaye <jonesbusy@users.noreply.github.com>
1 parent e56e3ed commit f089fdb

6 files changed

Lines changed: 142 additions & 3 deletions

File tree

src/main/java/land/oras/ManifestDescriptor.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Objects;
2929
import land.oras.utils.Const;
3030
import land.oras.utils.JsonUtils;
31+
import land.oras.utils.SupportedAlgorithm;
3132
import org.jspecify.annotations.NullMarked;
3233
import org.jspecify.annotations.Nullable;
3334

@@ -210,6 +211,34 @@ public static ManifestDescriptor of(Descriptor descriptor) {
210211
return of(descriptor, descriptor.getDigest());
211212
}
212213

214+
/**
215+
* Utility method. Useful when assembly manifest to be added to an Index using no platform, empty annotations and default supported algorithm
216+
* @param manifest The manifest
217+
* @return The manifest descriptor
218+
*/
219+
public static ManifestDescriptor of(Manifest manifest) {
220+
return of(manifest, Platform.empty(), Annotations.empty(), SupportedAlgorithm.getDefault());
221+
}
222+
223+
/**
224+
* Utility method. Useful when assembly manifest to be added to an Index
225+
* @param manifest The manifest
226+
* @param platform The platform
227+
* @param annotations The annotations
228+
* @param supportedAlgorithm The supported algorithm to calculate the digest of the manifest
229+
* @return The manifest descriptor
230+
*/
231+
public static ManifestDescriptor of(
232+
Manifest manifest, Platform platform, Annotations annotations, SupportedAlgorithm supportedAlgorithm) {
233+
String json = manifest.toJson();
234+
String digest = supportedAlgorithm.digest(json.getBytes());
235+
long size = json.length();
236+
return ManifestDescriptor.of(manifest.getMediaType(), digest, size)
237+
.withAnnotations(annotations.manifestAnnotations())
238+
.withPlatform(platform)
239+
.withArtifactType(manifest.getArtifactTypeAsString());
240+
}
241+
213242
/**
214243
* Create a manifest descriptor with the given digest
215244
* @param descriptor The descriptor

src/main/java/land/oras/Registry.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public Manifest pushManifest(ContainerRef containerRef, Manifest manifest) {
262262
}
263263
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
264264
if (ref.isInsecure(this) && !this.isInsecure()) {
265-
return asInsecure().pushManifest(containerRef, manifest);
265+
return asInsecure().pushManifest(ref, manifest);
266266
}
267267
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
268268
byte[] manifestData = manifest.getJson() != null
@@ -284,7 +284,7 @@ public Manifest pushManifest(ContainerRef containerRef, Manifest manifest) {
284284
"Subject was set on manifest but not OCI subject header was returned. Legacy flow not implemented");
285285
}
286286
}
287-
return getManifest(containerRef);
287+
return getManifest(ref);
288288
}
289289

290290
@Override

src/test/java/land/oras/HarborS3ITCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ void shouldPushFluxArtifact() {
9191

9292
// The compressed manifests
9393
Path archive = Paths.get("src/test/resources/archives").resolve("flux-manifests.tgz");
94-
Path image = Paths.get("src/test/resources/img").resolve("flux-cd.png");
94+
Path image = Paths.get("src/test/resources/img").resolve("opentofu.png");
9595
String configMediaType = "application/vnd.cncf.flux.config.v1+json";
9696
String contentMediaType = "application/vnd.cncf.flux.content.v1.tar+gzip";
9797

src/test/java/land/oras/ManifestDescriptorTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,22 @@
2828
import org.junit.jupiter.api.parallel.Execution;
2929
import org.junit.jupiter.api.parallel.ExecutionMode;
3030

31+
/**
32+
* Test for {@link ManifestDescriptor}
33+
*/
3134
@Execution(ExecutionMode.CONCURRENT)
3235
class ManifestDescriptorTest {
3336

37+
@Test
38+
void shouldBuildDescriptorFromManifest() {
39+
Manifest manifest = Manifest.empty();
40+
ManifestDescriptor descriptor = ManifestDescriptor.of(manifest);
41+
assertEquals("sha256:961dcd96e41989cc3cbf17141e0a9b3d39447cdcf2540b844e22b4f207a2e1f1", descriptor.getDigest());
42+
assertEquals(253, descriptor.getSize());
43+
assertEquals(Platform.empty(), descriptor.getPlatform());
44+
assertEquals(Map.of(), descriptor.getAnnotations());
45+
}
46+
3447
@Test
3548
void shouldSetAnnotations() {
3649
Manifest manifest = Manifest.empty();
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*-
2+
* =LICENSE=
3+
* ORAS Java SDK
4+
* ===
5+
* Copyright (C) 2024 - 2026 ORAS
6+
* ===
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* =LICENSEEND=
19+
*/
20+
21+
package land.oras;
22+
23+
import static org.junit.jupiter.api.Assertions.assertNotNull;
24+
25+
import java.nio.file.Path;
26+
import java.nio.file.Paths;
27+
import java.util.List;
28+
import java.util.Map;
29+
import land.oras.utils.SupportedAlgorithm;
30+
import land.oras.utils.ZotUnsecureContainer;
31+
import org.junit.jupiter.api.Test;
32+
import org.junit.jupiter.api.parallel.Execution;
33+
import org.junit.jupiter.api.parallel.ExecutionMode;
34+
import org.testcontainers.junit.jupiter.Container;
35+
import org.testcontainers.junit.jupiter.Testcontainers;
36+
37+
@Testcontainers
38+
@Execution(ExecutionMode.CONCURRENT)
39+
class OpenTofuITCase {
40+
41+
@Container
42+
private final ZotUnsecureContainer unsecureRegistry = new ZotUnsecureContainer().withStartupAttempts(3);
43+
44+
/**
45+
* This test demonstrate how to assemble a Flux CD OCI Artifact
46+
*/
47+
@Test
48+
void shouldAssembleProviderArtifact() {
49+
50+
// The compressed manifests
51+
Path archive =
52+
Paths.get("src/test/resources/archives").resolve("terraform-provider-random_3.8.1_linux_amd64.zip");
53+
54+
Annotations annotations = Annotations.empty();
55+
ArtifactType indexArtifactType = ArtifactType.from("application/vnd.opentofu.provider");
56+
ArtifactType manifestArtifactType = ArtifactType.from("application/vnd.opentofu.provider-target");
57+
String contentMediaType = "archive/zip";
58+
59+
Path image = Paths.get("src/test/resources/img").resolve("flux-cd.png");
60+
61+
Platform linuxAmd64 = Platform.linuxAmd64();
62+
63+
// Create objects
64+
Config config = Config.empty();
65+
Layer layer = Layer.fromFile(archive).withMediaType(contentMediaType);
66+
Layer imageLayer = Layer.fromFile(image)
67+
.withMediaType("image/png")
68+
.withAnnotations(Map.of("io.goharbor.artifact.v1alpha1.icon", ""));
69+
Manifest manifest = Manifest.empty()
70+
.withArtifactType(manifestArtifactType)
71+
.withConfig(config)
72+
.withLayers(List.of(layer, imageLayer));
73+
74+
// Index with given platform
75+
ManifestDescriptor manifestDescriptor =
76+
ManifestDescriptor.of(manifest, linuxAmd64, annotations, SupportedAlgorithm.SHA256);
77+
Index index = Index.fromManifests(List.of(manifestDescriptor)).withArtifactType(indexArtifactType);
78+
79+
// Push config, layers and manifest to registry
80+
Registry registry = Registry.builder()
81+
.defaults()
82+
.insecure()
83+
.withRegistry(unsecureRegistry.getRegistry())
84+
.build();
85+
ContainerRef containerRef = ContainerRef.parse("oras/opentofu-providers/terraform-provider-random:3.8.1");
86+
87+
registry.pushConfig(containerRef, config);
88+
registry.pushBlob(containerRef, archive);
89+
registry.pushBlob(containerRef, image);
90+
registry.pushManifest(containerRef.withDigest(manifestDescriptor.getDigest()), manifest);
91+
registry.pushIndex(containerRef, index);
92+
93+
// Ensure we can pull
94+
Index createdIndex = registry.getIndex(containerRef);
95+
assertNotNull(createdIndex);
96+
}
97+
}
24.2 KB
Loading

0 commit comments

Comments
 (0)