Warning
The Oras Java SDK is currently in alpha state.
It's configuration and APIs might change in future releases
OCI Registry as Storage enables libraries to push OCI Artifacts to OCI Conformant registries. This is a Java SDK for Java developers to empower them to do this in their applications.
SNAPSHOT for version 0.2.x are published on GitHub Maven packages. SNAPSHOT for version 0.3.x and above are published on Maven Central at: https://central.sonatype.com/repository/maven-snapshots/
Releases are published on Maven Central since version 0.2.x.
Javadoc is published from main branch into: https://oras-project.github.io/oras-java/
<dependency>
<groupId>land.oras</groupId>
<artifactId>oras-java-sdk</artifactId>
<version>VERSION_HERE</version>
</dependency>Quarkus users can use the extension quarkus-oras to use the SDK in their applications.
Follow the Quarkus ORAS documentation to get started with Quarkus.
Then on your pom.xml
<repositories>
<repository>
<id>central-snapshots</id>
<name>ORAS Maven Central SNAPSHOTS</name>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>Using existing credentials from ~/.docker/config.json or $XDG_RUNTIME_DIR/containers/auth.json:
Registry registry = Registry.builder().defaults().build();Using a username and password:
Registry registry = Registry.builder().defaults("username", "password").build();Registry registry = Registry.builder().insecure().build();
LocalPath artifact = LocalPath.of(Path.of("my-file.txt"));
Manifest manifest = registry.pushArtifact(ContainerRef.parse("localhost:5000/hello:v1"), artifact);Push several files at once with a custom artifact type, per-file media types, and manifest-level annotations:
Registry registry = Registry.builder().insecure().build();
Annotations annotations = Annotations.ofManifest(Map.of("build-tool", "maven"))
.withFileAnnotations("pom.xml", Map.of("format", "xml"));
Manifest manifest = registry.pushArtifact(
ContainerRef.parse("localhost:5000/my-app:v1"),
ArtifactType.from("application/vnd.maven+type"),
annotations,
LocalPath.of(Path.of("pom.xml"), "application/xml"),
LocalPath.of(Path.of("target/app.jar"), "application/java-archive"));The resulting manifest will contain one layer per file, each annotated with its filename via org.opencontainers.image.title.
Directories are automatically compressed as a tar+gzip archive and tagged with
org.opencontainers.image.title set to the directory name. The io.deis.oras.content.unpack
annotation is set to true so the SDK automatically extracts the archive on pull.
Registry registry = Registry.builder().insecure().build();
Manifest manifest = registry.pushArtifact(
ContainerRef.parse("localhost:5000/my-configs:v1"),
LocalPath.of(Path.of("config-dir")));To push a directory as a plain zip instead:
Manifest manifest = registry.pushArtifact(
ContainerRef.parse("localhost:5000/my-configs:v1"),
LocalPath.of(Path.of("config-dir"), "application/zip"));Files are automatically written using the org.opencontainers.image.title layer annotation as the filename.
The third argument controls whether existing files are overwritten:
Registry registry = Registry.builder().insecure().build();
registry.pullArtifact(ContainerRef.parse("localhost:5000/hello:v1"), Path.of("output-dir"), true);Attach a signature or attestation to an already-pushed artifact. The attached manifest references the
original via its subject field and is discoverable through the Referrers API:
Registry registry = Registry.builder().insecure().build();
ContainerRef ref = ContainerRef.parse("localhost:5000/my-app:v1");
// Push the main artifact first
Manifest manifest = registry.pushArtifact(ref,
ArtifactType.from("application/vnd.maven+type"),
LocalPath.of(Path.of("pom.xml"), "application/xml"));
// Attach a signature as a referrer
Manifest signatureManifest = registry.attachArtifact(
ref,
ArtifactType.from("application/vnd.example.signature"),
LocalPath.of(Path.of("pom.xml.asc")));
// List all referrers for the artifact
Referrers referrers = registry.getReferrers(
ref.withDigest(manifest.getDescriptor().getDigest()), null);For fine-grained control, push blobs and configs individually before assembling and pushing the manifest:
Registry registry = Registry.builder().insecure().build();
ContainerRef ref = ContainerRef.parse("localhost:5000/my-app:v1");
// Push individual layers
Layer layer1 = registry.pushBlob(ref, Files.readAllBytes(Path.of("schema.json")))
.withAnnotations(Map.of(Const.ANNOTATION_TITLE, "schema.json"));
Layer layer2 = registry.pushBlob(ref, Files.readAllBytes(Path.of("data.csv")))
.withAnnotations(Map.of(Const.ANNOTATION_TITLE, "data.csv"));
// Push a custom config
Config config = registry.pushConfig(ref, Config.empty().withMediaType("application/vnd.example.config+json"));
// Assemble and push the manifest
Manifest manifest = Manifest.empty()
.withConfig(config)
.withLayers(List.of(layer1, layer2));
registry.pushManifest(ref, manifest);Copy a tagged artifact — including all its blobs — from one registry to another:
Registry source = Registry.builder().defaults("user", "pass").insecure().build();
Registry target = Registry.builder().defaults("user", "pass").build();
ContainerRef from = ContainerRef.parse("localhost:5000/my-app:v1");
ContainerRef to = ContainerRef.parse("registry.example.com/my-app:v1");
CopyUtils.copy(source, from, target, to, CopyUtils.CopyOptions.shallow());OCI Layout lets you work with artifacts stored on disk in the OCI Image Layout format.
Push to an OCI Layout directory:
LayoutRef ref = LayoutRef.parse("/tmp/my-layout:latest");
OCILayout ociLayout = OCILayout.Builder.builder().defaults(Path.of("/tmp/my-layout")).build();
Manifest manifest = ociLayout.pushArtifact(
ref,
ArtifactType.from("application/vnd.example.type"),
Annotations.empty(),
LocalPath.of(Path.of("my-file.txt"), "text/plain"));Pull from an OCI Layout directory:
LayoutRef ref = LayoutRef.parse("/tmp/my-layout:latest");
OCILayout ociLayout = OCILayout.Builder.builder().defaults(Path.of("/tmp/my-layout")).build();
ociLayout.pullArtifact(ref, Path.of("output-dir"), false);Tar-backed OCI Layout (single-file, portable archive):
LayoutRef ref = LayoutRef.parse("/tmp/my-layout.tar:latest");
OCILayout ociLayout = OCILayout.Builder.builder().defaults(Path.of("/tmp/my-layout.tar")).build();
ociLayout.pushArtifact(ref, ArtifactType.from("application/vnd.example.type"),
Annotations.empty(), LocalPath.of(Path.of("my-file.txt"), "text/plain"));
// Pull from the same tar
ociLayout.pullArtifact(ref, Path.of("output-dir"), false);Copy from OCI Layout to a registry:
LayoutRef layoutRef = LayoutRef.parse("/tmp/my-layout:latest");
OCILayout ociLayout = OCILayout.Builder.builder().defaults(Path.of("/tmp/my-layout")).build();
Registry registry = Registry.builder().defaults("user", "pass").build();
ContainerRef target = ContainerRef.parse("registry.example.com/my-app:v1");
CopyUtils.copy(ociLayout, layoutRef, registry, target, CopyUtils.CopyOptions.shallow());Since version 0.7.0 the ORAS Java SDK supports the registries.conf format
(see the containers/image documentation).
The SDK reads configuration from the following locations, in order (later entries override earlier ones):
/etc/containers/registries.conf/etc/containers/registries.conf.d/*.conf(alphabetical)$HOME/.config/containers/registries.conf$HOME/.config/containers/registries.conf.d/*.conf(alphabetical)
Set the CONTAINERS_REGISTRIES_CONF environment variable to use a single file exclusively.
# Short-name resolution mode (enforcing is the default)
short-name-mode = "enforcing"
unqualified-search-registries = ["docker.io"]
# Rewrite a location via a prefix
[[registry]]
prefix = "docker.io/bitnami"
location = "docker.io/bitnamilegacy"
# Block a registry
[[registry]]
prefix = "gcr.io"
blocked = true
# Mark a registry as insecure
[[registry]]
location = "localhost:5000"
insecure = true
# Mirrors — tried in order before falling back to the upstream registry
[[registry]]
prefix = "registry.example.com"
location = "registry.example.com"
mirror-by-digest-only = false # set to true to restrict all mirrors to digest-only pulls
[[registry.mirror]]
location = "mirror1.example.com"
insecure = false
pull-from-mirror = "all" # "all" (default) | "tag-only" | "digest-only"
[[registry.mirror]]
location = "mirror2.example.com"
insecure = true
pull-from-mirror = "digest-only"SNAPSHOTS are automatically deployed when the main branch is updated. See the GitHub Actions for more details.
- Ensure the draft release version correspond to the version on the
pom.xml. Specially if changing the major or minor version. Patch releases are automatically updated. - Run the release workflow
Please note that this project has adopted the CNCF Code of Conduct. Please follow it in all your interactions with the project members and users.
This code is licensed under the Apache 2.0 LICENSE.