Skip to content

Commit fc87fc1

Browse files
authored
Add Platform model and some filtering utils for Index (#527)
2 parents 20fd340 + f729a71 commit fc87fc1

File tree

11 files changed

+484
-13
lines changed

11 files changed

+484
-13
lines changed

pom.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@
153153
<artifactId>junit-jupiter-engine</artifactId>
154154
<version>${junit.version}</version>
155155
</dependency>
156+
<dependency>
157+
<groupId>org.junit.jupiter</groupId>
158+
<artifactId>junit-jupiter-params</artifactId>
159+
<version>${junit.version}</version>
160+
</dependency>
156161
<dependency>
157162
<groupId>org.mockito</groupId>
158163
<artifactId>mockito-core</artifactId>
@@ -209,6 +214,10 @@
209214
<groupId>org.jspecify</groupId>
210215
<artifactId>jspecify</artifactId>
211216
</dependency>
217+
<dependency>
218+
<groupId>org.junit.jupiter</groupId>
219+
<artifactId>junit-jupiter-params</artifactId>
220+
</dependency>
212221
<dependency>
213222
<groupId>org.slf4j</groupId>
214223
<artifactId>slf4j-api</artifactId>

src/main/java/land/oras/Index.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public final class Index extends Descriptor implements Describable {
5757
private final Subject subject;
5858

5959
/**
60-
* The manifest descriptor
60+
* The index descriptor
6161
*/
6262
private final ManifestDescriptor descriptor;
6363

@@ -105,6 +105,39 @@ public List<ManifestDescriptor> getManifests() {
105105
return manifests;
106106
}
107107

108+
/**
109+
* Filter the manifests by platform
110+
* @param platform The platform
111+
* @return The list of manifests that match the platform
112+
*/
113+
public List<ManifestDescriptor> filter(Platform platform) {
114+
return getManifests().stream()
115+
.filter(descriptor -> descriptor.getPlatform().equals(platform))
116+
.toList();
117+
}
118+
119+
/**
120+
* Get the list of manifests that have unspecified platform
121+
* @return The list of manifests that have unspecified platform
122+
*/
123+
public List<ManifestDescriptor> unspecifiedPlatforms() {
124+
return getManifests().stream()
125+
.filter(descriptor -> Platform.unspecified(descriptor.getPlatform()))
126+
.toList();
127+
}
128+
129+
/**
130+
* Find a unique manifest by platform. If there are multiple manifests that match the platform, return null
131+
* @param platform The platform
132+
* @return The manifest that matches the platform, or null if there are multiple matches or no matches
133+
*/
134+
public @Nullable ManifestDescriptor findUnique(Platform platform) {
135+
return getManifests().stream()
136+
.filter(descriptor -> descriptor.getPlatform().equals(platform))
137+
.findFirst()
138+
.orElse(null);
139+
}
140+
108141
/**
109142
* Get the artifact type as string for JSON serialization
110143
* @return The artifact type as string

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package land.oras;
2222

2323
import com.fasterxml.jackson.annotation.JsonCreator;
24+
import com.fasterxml.jackson.annotation.JsonIgnore;
2425
import com.fasterxml.jackson.annotation.JsonProperty;
2526
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
2627
import java.util.Map;
@@ -105,13 +106,22 @@ public long getSize() {
105106
}
106107

107108
/**
108-
* Get the platform
109+
* Get the platform as annotations
109110
* @return The platform
110111
*/
111-
public Map<String, String> getPlatform() {
112+
public @Nullable @JsonProperty(Const.JSON_PROPERTY_PLATFORM) Map<String, String> getPlatformAnnotations() {
112113
return platform;
113114
}
114115

116+
/**
117+
* Return the platform as a Platform object
118+
* @return The platform
119+
*/
120+
@JsonIgnore
121+
public Platform getPlatform() {
122+
return Platform.of(platform);
123+
}
124+
115125
/**
116126
* Get the annotations
117127
* @return The annotations
@@ -171,6 +181,15 @@ public ManifestDescriptor withArtifactType(@Nullable String artifactType) {
171181
return new ManifestDescriptor(artifactType, mediaType, digest, size, platform, annotations);
172182
}
173183

184+
/**
185+
* Create a manifest descriptor with the given platform
186+
* @param platform The platform
187+
* @return The subject
188+
*/
189+
public ManifestDescriptor withPlatform(Platform platform) {
190+
return new ManifestDescriptor(artifactType, mediaType, digest, size, platform.annotations(), annotations);
191+
}
192+
174193
/**
175194
* Create a manifest descriptor
176195
* @param mediaType The media type
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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 com.fasterxml.jackson.annotation.JsonCreator;
24+
import com.fasterxml.jackson.annotation.JsonGetter;
25+
import com.fasterxml.jackson.annotation.JsonIgnore;
26+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
27+
import java.util.Map;
28+
import land.oras.utils.Const;
29+
import org.jspecify.annotations.NullMarked;
30+
import org.jspecify.annotations.Nullable;
31+
32+
/**
33+
* Record for platform information
34+
* @param annotations Platform annotations, which can include os and architecture information
35+
*/
36+
@NullMarked
37+
@OrasModel
38+
@JsonPropertyOrder({Const.PLATFORM_OS, Const.PLATFORM_ARCHITECTURE})
39+
public record Platform(@Nullable @JsonIgnore Map<String, String> annotations) {
40+
41+
/**
42+
* Constructor for JSON deserialization
43+
* @param annotations Platform annotations, which can include os and architecture information
44+
*/
45+
@JsonCreator
46+
public Platform {}
47+
48+
/**
49+
* Create a new platform with the given annotations
50+
* @param annotations Platform annotations, which can include os and architecture information
51+
* @return The platform
52+
*/
53+
public static Platform of(@Nullable Map<String, String> annotations) {
54+
return new Platform(annotations);
55+
}
56+
57+
/**
58+
* Create a new platform linux/amd64
59+
* @return The platform
60+
*/
61+
public static Platform linuxAmd64() {
62+
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_AMD64);
63+
}
64+
65+
/**
66+
* Create a new platform with empty os and architecture
67+
* @return The platform
68+
*/
69+
public static Platform empty() {
70+
return new Platform(null);
71+
}
72+
73+
/**
74+
* Create a new platform with unknown os and architecture
75+
* @return The platform
76+
*/
77+
public static Platform unknown() {
78+
return of(Const.PLATFORM_UNKNOWN, Const.PLATFORM_UNKNOWN);
79+
}
80+
81+
/**
82+
* Create a new platform with the given os and architecture
83+
* @param os The os of the platform
84+
* @param architecture The architecture of the platform
85+
* @return The platform
86+
*/
87+
public static Platform of(String os, String architecture) {
88+
return new Platform(Map.of(Const.PLATFORM_OS, os, Const.PLATFORM_ARCHITECTURE, architecture));
89+
}
90+
91+
/**
92+
* Create a new platform with the given os, architecture and variant
93+
* @param os The os of the platform
94+
* @param architecture The architecture of the platform
95+
* @param variant The variant of the platform
96+
* @return The platform
97+
*/
98+
public static Platform of(String os, String architecture, @Nullable String variant) {
99+
if (variant == null) {
100+
return of(os, architecture);
101+
}
102+
return new Platform(Map.of(
103+
Const.PLATFORM_OS, os,
104+
Const.PLATFORM_ARCHITECTURE, architecture,
105+
Const.PLATFORM_VARIANT, variant));
106+
}
107+
108+
/**
109+
* Return the architecture of the platform, or "unknown" if not specified
110+
* @return The architecture of the platform
111+
*/
112+
@JsonGetter
113+
public String os() {
114+
return annotations != null
115+
? annotations.getOrDefault(Const.PLATFORM_OS, Const.PLATFORM_UNKNOWN)
116+
: Const.PLATFORM_UNKNOWN;
117+
}
118+
119+
/**
120+
* Return the architecture of the platform, or "unknown" if not specified
121+
* @return The architecture of the platform
122+
*/
123+
@JsonGetter
124+
public String architecture() {
125+
return annotations != null
126+
? annotations.getOrDefault(Const.PLATFORM_ARCHITECTURE, Const.PLATFORM_UNKNOWN)
127+
: Const.PLATFORM_UNKNOWN;
128+
}
129+
130+
/**
131+
* Return the variant of the platform, or null if not specified
132+
* @return The variant of the platform
133+
*/
134+
@JsonGetter
135+
public @Nullable String variant() {
136+
return annotations != null ? annotations.get(Const.PLATFORM_VARIANT) : null;
137+
}
138+
139+
/**
140+
* Return true if the platform is unspecified, which means both os and architecture are either empty or unknown
141+
* @param platform The platform to check
142+
* @return True if the platform is unspecified, false otherwise
143+
*/
144+
public static boolean unspecified(Platform platform) {
145+
return platform.equals(Platform.empty()) || platform.equals(Platform.unknown());
146+
}
147+
}

src/main/java/land/oras/utils/Const.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,91 @@ private Const() {
219219
*/
220220
public static final String ANNOTATION_REVISION = "org.opencontainers.image.revision";
221221

222+
/**
223+
* Annotation for the base image name
224+
*/
225+
public static final String ANNOTATION_IMAGE_BASE_NAME = "org.opencontainers.image.base.name";
226+
227+
/**
228+
* Annotation for the image URL
229+
*/
230+
public static final String ANNOTATION_IMAGE_URL = "org.opencontainers.image.url";
231+
232+
/**
233+
* Annotation for the image version
234+
*/
235+
public static final String ANNOTATION_IMAGE_VERSION = "org.opencontainers.image.version";
236+
237+
/**
238+
* The platform OS key
239+
*/
240+
public static final String PLATFORM_OS = "os";
241+
242+
/**
243+
* The platform architecture key
244+
*/
245+
public static final String PLATFORM_ARCHITECTURE = "architecture";
246+
247+
/**
248+
* The platform variant key, which can be used in the annotation "variant" to specify the variant of the architecture, such as armv7 or armv8
249+
*/
250+
public static final String PLATFORM_VARIANT = "variant";
251+
252+
/**
253+
* The default value for unknown platform information
254+
*/
255+
public static final String PLATFORM_UNKNOWN = "unknown";
256+
257+
/**
258+
* The platform value for linux os
259+
*/
260+
public static final String PLATFORM_LINUX = "linux";
261+
262+
/**
263+
* The platform value for windows OS
264+
*/
265+
public static final String PLATFORM_WINDOWS = "windows";
266+
267+
/**
268+
* The platform value for amd64 architecture
269+
*/
270+
public static final String PLATFORM_ARCHITECTURE_AMD64 = "amd64";
271+
272+
/**
273+
* The platform value for 386 architecture
274+
*/
275+
public static final String PLATFORM_ARCHITECTURE_386 = "386";
276+
277+
/**
278+
* The platform value for arm architecture
279+
*/
280+
public static final String PLATFORM_ARCHITECTURE_ARM = "arm";
281+
282+
/**
283+
* The platform value for arm64 architecture
284+
*/
285+
public static final String PLATFORM_ARCHITECTURE_ARM64 = "arm64";
286+
287+
/**
288+
* The v5 variant for arm architecture, which can be used in the annotation "variant" to specify the variant of the arm architecture
289+
*/
290+
public static final String VARIANT_V5 = "v5";
291+
292+
/**
293+
* The v6 variant for arm architecture, which can be used in the annotation "variant" to specify the variant of the arm architecture
294+
*/
295+
public static final String VARIANT_V6 = "v6";
296+
297+
/**
298+
* The v7 variant for arm architecture, which can be used in the annotation "variant" to specify the variant of the arm architecture
299+
*/
300+
public static final String VARIANT_V7 = "v7";
301+
302+
/**
303+
* The v8 variant for arm architecture, which can be used in the annotation "variant" to specify the variant of the arm architecture
304+
*/
305+
public static final String VARIANT_V8 = "v8";
306+
222307
/**
223308
* Get the current timestamp for the created annotation
224309
* @return The current timestamp

src/test/java/land/oras/ClassAnnotationsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ void shouldHaveAnnotationOnModel() {
4848
.loadClasses());
4949

5050
// Check number of classes
51-
assertEquals(18, modelClasses.size());
51+
assertEquals(19, modelClasses.size());
5252

5353
// Check classes
5454
assertTrue(modelClasses.contains(Annotations.class));

0 commit comments

Comments
 (0)