From aa1dd9813eecf5778439a4bb7430354bfdfc1492 Mon Sep 17 00:00:00 2001 From: Valentin Delaye Date: Sat, 28 Feb 2026 14:21:17 +0100 Subject: [PATCH] Add optional OS version and feature Signed-off-by: Valentin Delaye --- src/main/java/land/oras/Index.java | 18 +- .../java/land/oras/ManifestDescriptor.java | 24 ++- src/main/java/land/oras/Platform.java | 197 ++++++++++++++---- src/main/java/land/oras/utils/Const.java | 30 +++ src/test/java/land/oras/DockerIoITCase.java | 31 ++- .../land/oras/ManifestDescriptorTest.java | 6 +- src/test/java/land/oras/OCILayoutTest.java | 1 - src/test/java/land/oras/PlatformTest.java | 67 +++++- .../java/land/oras/PublicAzureCRITCase.java | 25 +++ src/test/java/land/oras/PublicECRITCase.java | 2 + src/test/java/land/oras/ReferrersTest.java | 2 +- 11 files changed, 335 insertions(+), 68 deletions(-) diff --git a/src/main/java/land/oras/Index.java b/src/main/java/land/oras/Index.java index 67bed2cd..03d48fbb 100644 --- a/src/main/java/land/oras/Index.java +++ b/src/main/java/land/oras/Index.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.BiPredicate; import land.oras.utils.Const; import land.oras.utils.JsonUtils; import org.jspecify.annotations.NonNull; @@ -149,8 +150,18 @@ public List getManifests() { * @return The list of manifests that match the platform */ public List filter(Platform platform) { + return filter(platform, Platform::matches); + } + + /** + * Filter the manifests by platform with a custom comparator + * @param platform The platform + * @param comparator The comparator to compare the platform of the manifest descriptor and the given platform. The first parameter is the platform of the manifest descriptor, and the second parameter is the given platform. The comparator should return true if the manifest descriptor matches the given platform, and false otherwise. + * @return The list of manifests that match the platform + */ + public List filter(Platform platform, BiPredicate comparator) { return getManifests().stream() - .filter(descriptor -> descriptor.getPlatform().equals(platform)) + .filter(descriptor -> comparator.test(descriptor.getPlatform(), platform)) .toList(); } @@ -170,10 +181,7 @@ public List unspecifiedPlatforms() { * @return The manifest that matches the platform, or null if there are multiple matches or no matches */ public @Nullable ManifestDescriptor findUnique(Platform platform) { - return getManifests().stream() - .filter(descriptor -> descriptor.getPlatform().equals(platform)) - .findFirst() - .orElse(null); + return filter(platform).stream().findFirst().orElse(null); } @Override diff --git a/src/main/java/land/oras/ManifestDescriptor.java b/src/main/java/land/oras/ManifestDescriptor.java index 5bea00cb..045c1e30 100644 --- a/src/main/java/land/oras/ManifestDescriptor.java +++ b/src/main/java/land/oras/ManifestDescriptor.java @@ -53,7 +53,7 @@ public final class ManifestDescriptor { private final long size; @Nullable - private final Map platform; + private final Platform platform; @Nullable private final Map annotations; @@ -64,7 +64,7 @@ private ManifestDescriptor( @JsonProperty(Const.JSON_PROPERTY_MEDIA_TYPE) String mediaType, @JsonProperty(Const.JSON_PROPERTY_DIGEST) String digest, @JsonProperty(Const.JSON_PROPERTY_SIZE) long size, - @JsonProperty(Const.JSON_PROPERTY_PLATFORM) @Nullable Map platform, + @JsonProperty(Const.JSON_PROPERTY_PLATFORM) @Nullable Platform platform, @JsonProperty(Const.JSON_PROPERTY_ANNOTATIONS) @Nullable Map annotations) { this.artifactType = artifactType; this.mediaType = mediaType; @@ -107,20 +107,22 @@ public long getSize() { } /** - * Get the platform as annotations + * Return the platform as a Platform object * @return The platform */ - public @Nullable @JsonProperty(Const.JSON_PROPERTY_PLATFORM) Map getPlatformAnnotations() { - return platform; + @JsonIgnore + public Platform getPlatform() { + return platform != null ? platform : Platform.empty(); } /** - * Return the platform as a Platform object - * @return The platform + * Return the platform or null if the platform is not set + * Only use for serialization purposes, as the platform field is nullable in the JSON representation of the manifest descriptor + * @return The platform or null */ - @JsonIgnore - public Platform getPlatform() { - return Platform.of(platform); + @JsonProperty(Const.JSON_PROPERTY_PLATFORM) + public @Nullable Platform getPlatformOrNull() { + return platform; } /** @@ -188,7 +190,7 @@ public ManifestDescriptor withArtifactType(@Nullable String artifactType) { * @return The subject */ public ManifestDescriptor withPlatform(Platform platform) { - return new ManifestDescriptor(artifactType, mediaType, digest, size, platform.annotations(), annotations); + return new ManifestDescriptor(artifactType, mediaType, digest, size, platform, annotations); } /** diff --git a/src/main/java/land/oras/Platform.java b/src/main/java/land/oras/Platform.java index a016e4ce..742acb88 100644 --- a/src/main/java/land/oras/Platform.java +++ b/src/main/java/land/oras/Platform.java @@ -20,46 +20,110 @@ package land.oras; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import java.util.Map; +import java.util.List; +import java.util.Objects; import land.oras.utils.Const; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** * Record for platform information - * @param annotations Platform annotations, which can include os and architecture information + * @param os The operating system of the platform + * @param architecture The architecture of the platform + * @param variant The variant of the platform, which is optional and may be null + * @param osVersion The operating system version of the platform, which is optional and may be + * @param features The features of the platform, which is optional and may be null + * @param osFeatures The operating system features of the platform, which is optional and may be */ @NullMarked @OrasModel -@JsonPropertyOrder({Const.PLATFORM_OS, Const.PLATFORM_ARCHITECTURE}) -public record Platform(@Nullable @JsonIgnore Map annotations) { +@JsonPropertyOrder({ + Const.PLATFORM_OS, + Const.PLATFORM_ARCHITECTURE, + Const.PLATFORM_VARIANT, + Const.PLATFORM_OS_VERSION, + Const.PLATFORM_OS_FEATURES +}) +public record Platform( + @Nullable @JsonProperty(Const.PLATFORM_OS) String os, + @Nullable @JsonProperty(Const.PLATFORM_ARCHITECTURE) String architecture, + @Nullable @JsonProperty(Const.PLATFORM_OS_VERSION) String osVersion, + @Nullable @JsonProperty(Const.PLATFORM_VARIANT) String variant, + @Nullable @JsonProperty(Const.PLATFORM_FEATURES) List features, + @Nullable @JsonProperty(Const.PLATFORM_OS_FEATURES) List osFeatures) { /** - * Constructor for JSON deserialization - * @param annotations Platform annotations, which can include os and architecture information + * Create a new platform linux/amd64 + * @return The platform */ - @JsonCreator - public Platform {} + public static Platform linuxAmd64() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_AMD64); + } /** - * Create a new platform with the given annotations - * @param annotations Platform annotations, which can include os and architecture information + * Create a new platform windows/amd64 * @return The platform */ - public static Platform of(@Nullable Map annotations) { - return new Platform(annotations); + public static Platform windowsAmd64() { + return of(Const.PLATFORM_WINDOWS, Const.PLATFORM_ARCHITECTURE_AMD64); } /** * Create a new platform linux/amd64 * @return The platform */ - public static Platform linuxAmd64() { - return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_AMD64); + public static Platform linux386() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_386); + } + + /** + * Create a new platform linux arm/v6 + * @return The platform + */ + public static Platform linuxArmV6() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_ARM, Const.VARIANT_V6); + } + + /** + * Create a new platform linux arm/v7 + * @return The platform + */ + public static Platform linuxArmV7() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_ARM, Const.VARIANT_V7); + } + + /** + * Create a new platform linux arm64/v8 + * @return The platform + */ + public static Platform linuxArm64V8() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_ARM64, Const.VARIANT_V8); + } + + /** + * Create a new platform ppc64le + * @return The platform + */ + public static Platform linuxPpc64le() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_PPC64LE, null); + } + + /** + * Create a new platform riscv64 + * @return The platform + */ + public static Platform linuxRiscv64() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_RISCV64, null); + } + + /** + * Create a new platform s390x + * @return The platform + */ + public static Platform linuxS390x() { + return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_S390X, null); } /** @@ -67,7 +131,7 @@ public static Platform linuxAmd64() { * @return The platform */ public static Platform empty() { - return new Platform(null); + return new Platform(null, null, null, null, null, null); } /** @@ -85,7 +149,7 @@ public static Platform unknown() { * @return The platform */ public static Platform of(String os, String architecture) { - return new Platform(Map.of(Const.PLATFORM_OS, os, Const.PLATFORM_ARCHITECTURE, architecture)); + return new Platform(os, architecture, null, null, null, null); } /** @@ -96,44 +160,61 @@ public static Platform of(String os, String architecture) { * @return The platform */ public static Platform of(String os, String architecture, @Nullable String variant) { - if (variant == null) { - return of(os, architecture); - } - return new Platform(Map.of( - Const.PLATFORM_OS, os, - Const.PLATFORM_ARCHITECTURE, architecture, - Const.PLATFORM_VARIANT, variant)); + return new Platform(os, architecture, null, variant, null, null); } /** - * Return the architecture of the platform, or "unknown" if not specified - * @return The architecture of the platform + * Return the os of the platform, or "unknown" if the os is null + * @return The os of the platform */ - @JsonGetter + @Override public String os() { - return annotations != null - ? annotations.getOrDefault(Const.PLATFORM_OS, Const.PLATFORM_UNKNOWN) - : Const.PLATFORM_UNKNOWN; + return os != null ? os : Const.PLATFORM_UNKNOWN; } /** - * Return the architecture of the platform, or "unknown" if not specified - * @return The architecture of the platform + * Return the os of the platform, or "unknown" if the os is null + * @return The os of the platform */ - @JsonGetter + @Override public String architecture() { - return annotations != null - ? annotations.getOrDefault(Const.PLATFORM_ARCHITECTURE, Const.PLATFORM_UNKNOWN) - : Const.PLATFORM_UNKNOWN; + return architecture != null ? architecture : Const.PLATFORM_UNKNOWN; + } + + /** + * Create a new platform with the given features + * @param features The features of the platform + * @return The platform + */ + public Platform withFeatures(List features) { + return new Platform(os, architecture, osVersion, variant, features, osFeatures); + } + + /** + * Create a new platform with the given variant + * @param variant The variant of the platform + * @return The platform + */ + public Platform withVariant(String variant) { + return new Platform(os, architecture, osVersion, variant, features, osFeatures); + } + + /** + * Create a new platform with the given os version + * @param osVersion The os version of the platform + * @return The platform + */ + public Platform withOsVersion(String osVersion) { + return new Platform(os, architecture, osVersion, variant, features, osFeatures); } /** - * Return the variant of the platform, or null if not specified - * @return The variant of the platform + * Create a new platform with the given os features + * @param osFeatures The os features of the platform + * @return The platform */ - @JsonGetter - public @Nullable String variant() { - return annotations != null ? annotations.get(Const.PLATFORM_VARIANT) : null; + public Platform withOsFeatures(List osFeatures) { + return new Platform(os, architecture, osVersion, variant, features, osFeatures); } /** @@ -144,4 +225,34 @@ public String architecture() { public static boolean unspecified(Platform platform) { return platform.equals(Platform.empty()) || platform.equals(Platform.unknown()); } + + /** + * Check if 2 platform are matching, which means the os and architecture are the same (including variant) + * @param platform The platform to check + * @param target The target platform to match + * @return True if the platform is matching, false otherwise + */ + public static boolean matches(Platform platform, Platform target) { + return matches(platform, target, false); + } + + /** + * Check if 2 platform are matching, which means the os and architecture are the same (including variant) + * @param platform The platform to check + * @param target The target platform to match + * @param includeVersion Whether to include os version + * @return True if the platform is matching, false otherwise + */ + public static boolean matches(Platform platform, Platform target, boolean includeVersion) { + if (!platform.os().equals(target.os()) || !platform.architecture().equals(target.architecture())) { + return false; + } + if (!Objects.equals(platform.variant(), target.variant())) { + return false; + } + if (includeVersion) { + return Objects.equals(platform.osVersion(), target.osVersion()); + } + return true; + } } diff --git a/src/main/java/land/oras/utils/Const.java b/src/main/java/land/oras/utils/Const.java index d7079354..c5489921 100644 --- a/src/main/java/land/oras/utils/Const.java +++ b/src/main/java/land/oras/utils/Const.java @@ -265,6 +265,21 @@ private Const() { */ public static final String PLATFORM_VARIANT = "variant"; + /** + * The platform OS version key, which can be used in the annotation "os.version" to specify the version of the OS, such as ubuntu 20.04 or alpine 3.14 + */ + public static final String PLATFORM_OS_VERSION = "os.version"; + + /** + * The platform OS features key, which can be used in the annotation "os.features" to specify the features of the OS, such as sse4 or aes + */ + public static final String PLATFORM_OS_FEATURES = "os.features"; + + /** + * The platform features key, which can be used in the annotation "features" to specify the features of the platform, such as gpu or fpga + */ + public static final String PLATFORM_FEATURES = "features"; + /** * The default value for unknown platform information */ @@ -300,6 +315,21 @@ private Const() { */ public static final String PLATFORM_ARCHITECTURE_ARM64 = "arm64"; + /** + * The platform value for ppc64le architecture + */ + public static final String PLATFORM_ARCHITECTURE_PPC64LE = "ppc64le"; + + /** + * The platform value for riscv64 architecture + */ + public static final String PLATFORM_ARCHITECTURE_RISCV64 = "riscv64"; + + /** + * The platform value for s390x architecture + */ + public static final String PLATFORM_ARCHITECTURE_S390X = "s390x"; + /** * The v5 variant for arm architecture, which can be used in the annotation "variant" to specify the variant of the arm architecture */ diff --git a/src/test/java/land/oras/DockerIoITCase.java b/src/test/java/land/oras/DockerIoITCase.java index b4af63dc..4d374a9f 100644 --- a/src/test/java/land/oras/DockerIoITCase.java +++ b/src/test/java/land/oras/DockerIoITCase.java @@ -99,8 +99,35 @@ void shouldCopyTagToInternalRegistry() { ContainerRef containerTarget = ContainerRef.parse("%s/docker/library/alpine:latest".formatted(unsecureRegistry.getRegistry())); - CopyUtils.copy(sourceRegistry, containerSource, targetRegistry, containerTarget, true); - assertTrue(targetRegistry.exists(containerTarget)); + // CopyUtils.copy(sourceRegistry, containerSource, targetRegistry, containerTarget, true); + // assertTrue(targetRegistry.exists(containerTarget)); + + Index index = targetRegistry.getIndex(containerSource); + + // Ensure standard platform matching + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linux386()))); + } + + @Test + void shouldContainsCommonLinuxPlatform() { + + // Source registry + Registry sourceRegistry = Registry.Builder.builder().defaults().build(); + + ContainerRef containerSource = ContainerRef.parse( + "docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"); + + Index index = sourceRegistry.getIndex(containerSource); + + // Ensure standard platform matching + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linux386()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxAmd64()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxArmV6()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxArmV7()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxArm64V8()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxPpc64le()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxS390x()))); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform().equals(Platform.linuxRiscv64()))); } @Test diff --git a/src/test/java/land/oras/ManifestDescriptorTest.java b/src/test/java/land/oras/ManifestDescriptorTest.java index 621d876a..92d0b611 100644 --- a/src/test/java/land/oras/ManifestDescriptorTest.java +++ b/src/test/java/land/oras/ManifestDescriptorTest.java @@ -63,8 +63,8 @@ void shouldReadFromJson() { "sha256:1de5eb4a9a6735adb46b2c9c88674c0cfba3444dd4ac2341b3babf1261700529", descriptor.getAnnotations().get("vnd.docker.reference.digest")); assertEquals("attestation-manifest", descriptor.getAnnotations().get("vnd.docker.reference.type")); - assertEquals("unknown", descriptor.getPlatformAnnotations().get("architecture")); - assertEquals("unknown", descriptor.getPlatformAnnotations().get("os")); + assertEquals("unknown", descriptor.getPlatform().architecture()); + assertEquals("unknown", descriptor.getPlatform().os()); descriptor.toJson(); } @@ -80,7 +80,7 @@ void testEqualsAndHashCode() { void testToString() { ManifestDescriptor descriptor = ManifestDescriptor.fromJson(descriptor()); String expected = - "{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:09c8ec8bf0d43a250ba7fed2eb6f242935b2987be5ed921ee06c93008558f980\",\"size\":838,\"platform\":{\"architecture\":\"unknown\",\"os\":\"unknown\"},\"annotations\":{\"com.docker.official-images.bashbrew.arch\":\"riscv64\",\"vnd.docker.reference.digest\":\"sha256:1de5eb4a9a6735adb46b2c9c88674c0cfba3444dd4ac2341b3babf1261700529\",\"vnd.docker.reference.type\":\"attestation-manifest\"}}"; + "{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:09c8ec8bf0d43a250ba7fed2eb6f242935b2987be5ed921ee06c93008558f980\",\"size\":838,\"platform\":{\"os\":\"unknown\",\"architecture\":\"unknown\"},\"annotations\":{\"com.docker.official-images.bashbrew.arch\":\"riscv64\",\"vnd.docker.reference.digest\":\"sha256:1de5eb4a9a6735adb46b2c9c88674c0cfba3444dd4ac2341b3babf1261700529\",\"vnd.docker.reference.type\":\"attestation-manifest\"}}"; assertEquals(expected, descriptor.toString()); } diff --git a/src/test/java/land/oras/OCILayoutTest.java b/src/test/java/land/oras/OCILayoutTest.java index 0d15da2a..0767c71d 100644 --- a/src/test/java/land/oras/OCILayoutTest.java +++ b/src/test/java/land/oras/OCILayoutTest.java @@ -564,7 +564,6 @@ void shouldPullIndex() throws IOException { assertNotNull(manifestDescriptor.getAnnotations()); assertNotNull(manifestDescriptor.getAnnotations().get(Const.ANNOTATION_CREATED)); assertEquals("latest", manifestDescriptor.getAnnotations().get(Const.ANNOTATION_REF)); - assertNull(manifestDescriptor.getPlatformAnnotations()); assertNotNull(manifestDescriptor.getPlatform()); assertEquals(Platform.empty(), manifestDescriptor.getPlatform()); } diff --git a/src/test/java/land/oras/PlatformTest.java b/src/test/java/land/oras/PlatformTest.java index 1e2e0194..52455d5a 100644 --- a/src/test/java/land/oras/PlatformTest.java +++ b/src/test/java/land/oras/PlatformTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.List; import land.oras.utils.Const; import land.oras.utils.JsonUtils; import org.junit.jupiter.api.Test; @@ -79,13 +80,23 @@ void shouldSerializeToJson() { assertEquals(expected, json); } + @Test + void shouldSerializeToJsonWithOptionalValues() { + Platform platform = + Platform.linuxAmd64().withFeatures(List.of("sse4", "avx2")).withOsFeatures(List.of("linux-gnu")); + String json = JsonUtils.toJson(platform); + // language=json + String expected = + "{\"os\":\"linux\",\"architecture\":\"amd64\",\"os.features\":[\"linux-gnu\"],\"features\":[\"sse4\",\"avx2\"]}"; + assertEquals(expected, json); + } + @Test void shouldTestEmptyPlatform() { Platform platform = Platform.empty(); assertNotEquals(Platform.unknown(), platform); assertEquals(Const.PLATFORM_UNKNOWN, platform.os()); assertEquals(Const.PLATFORM_UNKNOWN, platform.architecture()); - assertNull(platform.annotations()); assertNull(platform.variant()); } @@ -95,7 +106,6 @@ void shouldTestUnknownPlatform() { assertNotEquals(Platform.empty(), platform); assertEquals(Const.PLATFORM_UNKNOWN, platform.os()); assertEquals(Const.PLATFORM_UNKNOWN, platform.architecture()); - assertNotNull(platform.annotations()); assertNull(platform.variant()); } @@ -125,4 +135,57 @@ void shouldReadFromJson() { platform = JsonUtils.fromJson(json, Platform.class); assertEquals(Platform.unknown(), platform); } + + @Test + void shouldReadFromJsonWithOptionalValues() { + // language=json + String json = + """ + { + "architecture": "amd64", + "variant": "v8", + "os.features": ["linux-gnu"], + "os.version": "1.0", + "features": ["sse4", "avx2"], + "os": "linux" + } + """; + Platform platform = JsonUtils.fromJson(json, Platform.class); + assertEquals("amd64", platform.architecture()); + assertEquals("linux", platform.os()); + assertEquals("v8", platform.variant()); + assertEquals(List.of("linux-gnu"), platform.osFeatures()); + assertEquals(List.of("sse4", "avx2"), platform.features()); + assertEquals( + Platform.linuxAmd64() + .withOsVersion("1.0") + .withFeatures(List.of("sse4", "avx2")) + .withOsFeatures(List.of("linux-gnu")) + .withVariant("v8"), + platform); + + json = + """ + { + "architecture": "unknown", + "os": "unknown" + } + """; + platform = JsonUtils.fromJson(json, Platform.class); + assertEquals(Platform.unknown(), platform); + } + + @Test + void shouldCompareTwoPlatformWithoutVersion() { + assertTrue(Platform.matches(Platform.linuxAmd64(), Platform.linuxAmd64().withOsVersion("1.0"))); + assertFalse(Platform.matches(Platform.windowsAmd64(), Platform.linuxAmd64())); + assertFalse( + Platform.matches(Platform.linuxAmd64(), Platform.linuxAmd64().withOsVersion("1.0"), true)); + assertTrue(Platform.matches( + Platform.linuxAmd64().withOsVersion("1.0"), + Platform.linuxAmd64().withOsVersion("1.0"), + true)); + assertTrue(Platform.matches(Platform.linuxArmV6(), Platform.linuxArmV6())); + assertFalse(Platform.matches(Platform.linuxArmV6(), Platform.linuxArmV7())); + } } diff --git a/src/test/java/land/oras/PublicAzureCRITCase.java b/src/test/java/land/oras/PublicAzureCRITCase.java index 4b9be7b3..93eaca39 100644 --- a/src/test/java/land/oras/PublicAzureCRITCase.java +++ b/src/test/java/land/oras/PublicAzureCRITCase.java @@ -21,9 +21,11 @@ package land.oras; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Path; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; @@ -35,6 +37,29 @@ class PublicAzureCRITCase { @TempDir private Path tempDir; + @Test + void shouldContainsCommonWindowsPlatform() { + + // Source registry + Registry sourceRegistry = Registry.Builder.builder().defaults().build(); + + ContainerRef containerSource = ContainerRef.parse( + "mcr.microsoft.com/windows@sha256:755e998e6f63e40f709deb731eee9b1d219673bfb21149cccf29aba5dfd32e0f"); + + Index index = sourceRegistry.getIndex(containerSource); + + assertTrue(index.getManifests().stream() + .anyMatch(m -> m.getPlatform().equals(Platform.windowsAmd64().withOsVersion("10.0.17763.8389")))); + + containerSource = ContainerRef.parse( + "mcr.microsoft.com/windows/servercore@sha256:79aa6a176b2e4f1786eb29c4facd33077769eddde4c4a650aea552f6320893c7"); + index = sourceRegistry.getIndex(containerSource); + assertTrue(index.getManifests().stream().anyMatch(m -> m.getPlatform() + .equals(Platform.windowsAmd64() + .withOsVersion("10.0.26100.32370") + .withOsFeatures(List.of("win32k"))))); + } + @Test void shouldPullAnonymousIndex() { diff --git a/src/test/java/land/oras/PublicECRITCase.java b/src/test/java/land/oras/PublicECRITCase.java index 226afcf7..61d2e88c 100644 --- a/src/test/java/land/oras/PublicECRITCase.java +++ b/src/test/java/land/oras/PublicECRITCase.java @@ -130,6 +130,8 @@ void shouldPullAnonymousIndexAndFilterPlatform() { assertEquals(1, manifests.size()); assertNotNull(manifests.get(0)); + manifests = index.filter(Platform.linuxAmd64(), Platform::matches); + // Find unique ManifestDescriptor manifest = index.findUnique(Platform.linuxAmd64()); assertNotNull(manifest); diff --git a/src/test/java/land/oras/ReferrersTest.java b/src/test/java/land/oras/ReferrersTest.java index e50582ac..105bb05c 100644 --- a/src/test/java/land/oras/ReferrersTest.java +++ b/src/test/java/land/oras/ReferrersTest.java @@ -68,7 +68,7 @@ void testEquals() { void testToString() { Referrers referrers = Referrers.from(List.of(ManifestDescriptor.fromJson(descriptor()))); assertEquals( - "{\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:09c8ec8bf0d43a250ba7fed2eb6f242935b2987be5ed921ee06c93008558f980\",\"size\":838,\"platform\":{\"architecture\":\"unknown\",\"os\":\"unknown\"},\"annotations\":{\"com.docker.official-images.bashbrew.arch\":\"riscv64\",\"vnd.docker.reference.digest\":\"sha256:1de5eb4a9a6735adb46b2c9c88674c0cfba3444dd4ac2341b3babf1261700529\",\"vnd.docker.reference.type\":\"attestation-manifest\"}}]}", + "{\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:09c8ec8bf0d43a250ba7fed2eb6f242935b2987be5ed921ee06c93008558f980\",\"size\":838,\"platform\":{\"os\":\"unknown\",\"architecture\":\"unknown\"},\"annotations\":{\"com.docker.official-images.bashbrew.arch\":\"riscv64\",\"vnd.docker.reference.digest\":\"sha256:1de5eb4a9a6735adb46b2c9c88674c0cfba3444dd4ac2341b3babf1261700529\",\"vnd.docker.reference.type\":\"attestation-manifest\"}}]}", referrers.toString()); } }