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
14 changes: 12 additions & 2 deletions src/main/java/land/oras/Layer.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,26 @@ public static Layer fromJson(String json) {
}

/**
* Create a layer from a file
* Create a layer from a file using default digest
* @param file The file
* @return The layer
*/
public static Layer fromFile(Path file) {
return fromFile(file, SupportedAlgorithm.getDefault());
}

/**
* Create a layer from a file using a specific algorithm
* @param file The file
* @param algorithm The algorithm
* @return The layer
*/
public static Layer fromFile(Path file, SupportedAlgorithm algorithm) {
Map<String, String> annotations =
Map.of(Const.ANNOTATION_TITLE, file.getFileName().toString());
return new Layer(
Const.DEFAULT_BLOB_MEDIA_TYPE,
SupportedAlgorithm.getDefault().digest(file),
algorithm.digest(file),
file.toFile().length(),
file,
annotations);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/land/oras/LayoutRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,9 @@ public SupportedAlgorithm getAlgorithm() {
// See https://github.com/opencontainers/image-spec/blob/main/descriptor.md#digests
return SupportedAlgorithm.fromDigest(tag);
}

@Override
public String getRepository() {
return getFolder().toString();
}
}
4 changes: 2 additions & 2 deletions src/main/java/land/oras/OCILayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ public Layer pushBlob(LayoutRef ref, Path blob, Map<String, String> annotations)
try {
if (Files.exists(blobPath)) {
LOG.debug("Blob already exists: {}", blobPath);
return Layer.fromFile(blobPath).withAnnotations(annotations);
return Layer.fromFile(blobPath, ref.getAlgorithm()).withAnnotations(annotations);
}
Files.copy(blob, blobPath);
return Layer.fromFile(blobPath).withAnnotations(annotations);
return Layer.fromFile(blobPath, ref.getAlgorithm()).withAnnotations(annotations);
} catch (IOException e) {
throw new OrasException("Failed to push blob", e);
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/land/oras/Ref.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,10 @@ protected Ref(String tag) {
* @return The algorithm
*/
public abstract SupportedAlgorithm getAlgorithm();

/**
* Get the repository where to find the ref
* @return The repository
*/
public abstract String getRepository();
}
6 changes: 3 additions & 3 deletions src/main/java/land/oras/Registry.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ public Layer pushBlob(ContainerRef containerRef, Path blob, Map<String, String>
LOG.debug("Digest: {}", digest);
if (hasBlob(containerRef.withDigest(digest))) {
LOG.info("Blob already exists: {}", digest);
return Layer.fromFile(blob).withAnnotations(annotations);
return Layer.fromFile(blob, containerRef.getAlgorithm()).withAnnotations(annotations);
}
URI uri = URI.create(
"%s://%s".formatted(getScheme(), containerRef.withDigest(digest).getBlobsUploadDigestPath()));
Expand All @@ -414,7 +414,7 @@ public Layer pushBlob(ContainerRef containerRef, Path blob, Map<String, String>

// Accepted single POST push
if (response.statusCode() == 201) {
return Layer.fromFile(blob).withAnnotations(annotations);
return Layer.fromFile(blob, containerRef.getAlgorithm()).withAnnotations(annotations);
}

// We need to push via PUT
Expand All @@ -439,7 +439,7 @@ public Layer pushBlob(ContainerRef containerRef, Path blob, Map<String, String>
}

handleError(response);
return Layer.fromFile(blob).withAnnotations(annotations);
return Layer.fromFile(blob, containerRef.getAlgorithm()).withAnnotations(annotations);
}

@Override
Expand Down
23 changes: 23 additions & 0 deletions src/test/java/land/oras/LayerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@

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

import java.nio.file.Files;
import java.nio.file.Path;
import land.oras.utils.SupportedAlgorithm;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

@Execution(ExecutionMode.CONCURRENT)
public class LayerTest {

@TempDir
public static Path tempDir;

@Test
void shouldReadLayer() {
String json = sampleLayer();
Expand All @@ -39,6 +46,22 @@ void shouldReadLayer() {
assertEquals(json, layer.toJson());
}

@Test
void shouldReadLayerFromFile() throws Exception {
Path file = tempDir.resolve("hi.txt");
Files.writeString(file, "hi");
Layer layer = Layer.fromFile(file);
assertEquals("application/vnd.oci.image.layer.v1.tar", layer.getMediaType());
assertEquals("sha256:8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4", layer.getDigest());
assertEquals(2, layer.getSize());
layer = Layer.fromFile(file, SupportedAlgorithm.SHA512);
assertEquals("application/vnd.oci.image.layer.v1.tar", layer.getMediaType());
assertEquals(
"sha512:150a14ed5bea6cc731cf86c41566ac427a8db48ef1b9fd626664b3bfbb99071fa4c922f33dde38719b8c8354e2b7ab9d77e0e67fc12843920a712e73d558e197",
layer.getDigest());
assertEquals(2, layer.getSize());
}

@Test
void shouldHaveEmptyLayer() {
String json = emptyLayer();
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/land/oras/LayoutRefTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ void shouldParseLayoutWithAllParts() {
LayoutRef layoutRef = LayoutRef.parse("%s:v1".formatted(ociLayout));
assertEquals("v1", layoutRef.getTag());
assertEquals(ociLayout, layoutRef.getFolder().toString());
assertEquals(ociLayout, layoutRef.getRepository());
}

@Test
Expand All @@ -49,6 +50,7 @@ void shouldParseLayoutWithDigest() {
LayoutRef layoutRef = LayoutRef.parse("%s@sha256:12345".formatted(ociLayout));
assertEquals("sha256:12345", layoutRef.getTag());
assertEquals(ociLayout, layoutRef.getFolder().toString());
assertEquals(ociLayout, layoutRef.getRepository());
}

@Test
Expand Down
41 changes: 40 additions & 1 deletion src/test/java/land/oras/RegistryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ void shouldFailToPushDirectoryWithInvalidCompression() throws IOException {
}

@Test
void shouldPushAndGetBlobStream() throws IOException {
void shouldPushAndGetBlobStreamSha256() throws IOException {
Registry registry = Registry.Builder.builder()
.defaults("myuser", "mypass")
.withInsecure(true)
Expand All @@ -824,6 +824,45 @@ void shouldPushAndGetBlobStream() throws IOException {
layer = registry.pushBlobStream(containerRef, inputStream, fileSize);

// Verify the digest matches SHA-256 of content
assertEquals(SupportedAlgorithm.SHA256, containerRef.getAlgorithm());
assertEquals(containerRef.getAlgorithm().digest(testFile), layer.getDigest());
assertEquals(fileSize, layer.getSize());
}

// Test getBlobStream
try (InputStream resultStream = registry.getBlobStream(containerRef.withDigest(layer.getDigest()))) {
String result = new String(resultStream.readAllBytes());
assertEquals(testData, result);
}

// Clean up
Files.delete(testFile);
registry.deleteBlob(containerRef.withDigest(layer.getDigest()));
}

@Test
void shouldPushAndGetBlobStreamWithSha512() throws IOException {
Registry registry = Registry.Builder.builder()
.defaults("myuser", "mypass")
.withInsecure(true)
.build();
ContainerRef containerRef = ContainerRef.parse(
"%s/library/artifact-stream-sha512@sha512:ea0d8750d01f5fbd0da5d020d981b377fa2177874751063cb3da2117e481720774c0d985845a56c32ee6dde144901d92b2bdc8d0cb02373da141241aa2409859"
.formatted(this.registry.getRegistry()));

// Create a file with test data to get accurate stream size
Path testFile = Files.createTempFile("test-data-", ".tmp");
String testData = "Hello World Stream Test";
Files.writeString(testFile, testData);
long fileSize = Files.size(testFile);

// Test pushBlobStream using file input stream
Layer layer;
try (InputStream inputStream = Files.newInputStream(testFile)) {
layer = registry.pushBlobStream(containerRef, inputStream, fileSize);

// Verify the digest matches SHA-512 of content
assertEquals(SupportedAlgorithm.SHA512, containerRef.getAlgorithm());
assertEquals(containerRef.getAlgorithm().digest(testFile), layer.getDigest());
assertEquals(fileSize, layer.getSize());
}
Expand Down