Skip to content
Merged
Show file tree
Hide file tree
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
29 changes: 29 additions & 0 deletions src/main/java/land/oras/ManifestDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Objects;
import land.oras.utils.Const;
import land.oras.utils.JsonUtils;
import land.oras.utils.SupportedAlgorithm;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

Expand Down Expand Up @@ -210,6 +211,34 @@ public static ManifestDescriptor of(Descriptor descriptor) {
return of(descriptor, descriptor.getDigest());
}

/**
* Utility method. Useful when assembly manifest to be added to an Index using no platform, empty annotations and default supported algorithm
* @param manifest The manifest
* @return The manifest descriptor
*/
public static ManifestDescriptor of(Manifest manifest) {
return of(manifest, Platform.empty(), Annotations.empty(), SupportedAlgorithm.getDefault());
}

/**
* Utility method. Useful when assembly manifest to be added to an Index
* @param manifest The manifest
* @param platform The platform
* @param annotations The annotations
* @param supportedAlgorithm The supported algorithm to calculate the digest of the manifest
* @return The manifest descriptor
*/
public static ManifestDescriptor of(
Manifest manifest, Platform platform, Annotations annotations, SupportedAlgorithm supportedAlgorithm) {
String json = manifest.toJson();
String digest = supportedAlgorithm.digest(json.getBytes());
long size = json.length();
return ManifestDescriptor.of(manifest.getMediaType(), digest, size)
.withAnnotations(annotations.manifestAnnotations())
.withPlatform(platform)
.withArtifactType(manifest.getArtifactTypeAsString());
}

/**
* Create a manifest descriptor with the given digest
* @param descriptor The descriptor
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/land/oras/Registry.java
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public Manifest pushManifest(ContainerRef containerRef, Manifest manifest) {
}
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
if (ref.isInsecure(this) && !this.isInsecure()) {
return asInsecure().pushManifest(containerRef, manifest);
return asInsecure().pushManifest(ref, manifest);
}
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
byte[] manifestData = manifest.getJson() != null
Expand All @@ -284,7 +284,7 @@ public Manifest pushManifest(ContainerRef containerRef, Manifest manifest) {
"Subject was set on manifest but not OCI subject header was returned. Legacy flow not implemented");
}
}
return getManifest(containerRef);
return getManifest(ref);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/land/oras/HarborS3ITCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ void shouldPushFluxArtifact() {

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

Expand Down
13 changes: 13 additions & 0 deletions src/test/java/land/oras/ManifestDescriptorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,22 @@
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

/**
* Test for {@link ManifestDescriptor}
*/
@Execution(ExecutionMode.CONCURRENT)
class ManifestDescriptorTest {

@Test
void shouldBuildDescriptorFromManifest() {
Manifest manifest = Manifest.empty();
ManifestDescriptor descriptor = ManifestDescriptor.of(manifest);
assertEquals("sha256:961dcd96e41989cc3cbf17141e0a9b3d39447cdcf2540b844e22b4f207a2e1f1", descriptor.getDigest());
assertEquals(253, descriptor.getSize());
assertEquals(Platform.empty(), descriptor.getPlatform());
assertEquals(Map.of(), descriptor.getAnnotations());
}

@Test
void shouldSetAnnotations() {
Manifest manifest = Manifest.empty();
Expand Down
97 changes: 97 additions & 0 deletions src/test/java/land/oras/OpenTofuITCase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*-
* =LICENSE=
* ORAS Java SDK
* ===
* Copyright (C) 2024 - 2026 ORAS
* ===
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =LICENSEEND=
*/

package land.oras;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import land.oras.utils.SupportedAlgorithm;
import land.oras.utils.ZotUnsecureContainer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@Execution(ExecutionMode.CONCURRENT)
class OpenTofuITCase {

@Container
private final ZotUnsecureContainer unsecureRegistry = new ZotUnsecureContainer().withStartupAttempts(3);

/**
* This test demonstrate how to assemble a Flux CD OCI Artifact
*/
@Test
void shouldAssembleProviderArtifact() {

// The compressed manifests
Path archive =
Paths.get("src/test/resources/archives").resolve("terraform-provider-random_3.8.1_linux_amd64.zip");

Annotations annotations = Annotations.empty();
ArtifactType indexArtifactType = ArtifactType.from("application/vnd.opentofu.provider");
ArtifactType manifestArtifactType = ArtifactType.from("application/vnd.opentofu.provider-target");
String contentMediaType = "archive/zip";

Path image = Paths.get("src/test/resources/img").resolve("flux-cd.png");

Platform linuxAmd64 = Platform.linuxAmd64();

// Create objects
Config config = Config.empty();
Layer layer = Layer.fromFile(archive).withMediaType(contentMediaType);
Layer imageLayer = Layer.fromFile(image)
.withMediaType("image/png")
.withAnnotations(Map.of("io.goharbor.artifact.v1alpha1.icon", ""));
Manifest manifest = Manifest.empty()
.withArtifactType(manifestArtifactType)
.withConfig(config)
.withLayers(List.of(layer, imageLayer));

// Index with given platform
ManifestDescriptor manifestDescriptor =
ManifestDescriptor.of(manifest, linuxAmd64, annotations, SupportedAlgorithm.SHA256);
Index index = Index.fromManifests(List.of(manifestDescriptor)).withArtifactType(indexArtifactType);

// Push config, layers and manifest to registry
Registry registry = Registry.builder()
.defaults()
.insecure()
.withRegistry(unsecureRegistry.getRegistry())
.build();
ContainerRef containerRef = ContainerRef.parse("oras/opentofu-providers/terraform-provider-random:3.8.1");

registry.pushConfig(containerRef, config);
registry.pushBlob(containerRef, archive);
registry.pushBlob(containerRef, image);
registry.pushManifest(containerRef.withDigest(manifestDescriptor.getDigest()), manifest);
registry.pushIndex(containerRef, index);

// Ensure we can pull
Index createdIndex = registry.getIndex(containerRef);
assertNotNull(createdIndex);
}
}
Binary file added src/test/resources/img/opentofu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.