OCI.Registry (oci v0.0.5)
View SourceRegistry wraps storage and auth adapters and handles common logic for OCI like validating manifests and tags.
Summary
Functions
Extracts the module name from a struct.
Returns the API version string used in OCI registry paths.
Generates the path for a blob with the given digest in a repository.
Generates the path for an ongoing blob upload session.
Calculates the range of a chunk of data.
Gets the status of an ongoing blob upload.
Initializes a new registry instance with the given configuration.
Initiates a new blob upload for the given repository. Returns {:ok, location} on success or {:error, reason} on failure. The location is the full path where the blob should be uploaded.
Generates the path for a manifest reference in the OCI registry.
Mounts a blob from one repository to another. Returns {:ok, location} on success, {:error, :BLOB_UNKNOWN} if the source blob doesn't exist.
Parses a Content-Range header value into start and end positions.
Calculates the SHA-256 hash of the given data and returns it as a lowercase hexadecimal string.
Uploads a chunk of data to an existing blob upload.
Verifies that the given data matches the provided digest.
Verifies that a chunk upload is in the correct order.
Types
@type t() :: %OCI.Registry{ auth: module(), enable_blob_deletion: boolean(), enable_manifest_deletion: boolean(), max_blob_upload_chunk_size: pos_integer(), max_manifest_size: pos_integer(), realm: String.t(), repo_name_pattern: Regex.t(), storage: module() }
Functions
Extracts the module name from a struct.
Examples
iex> OCI.Registry.adapter(%OCI.Storage.Local{path: "/tmp"})
OCI.Storage.Local
@spec api_version() :: String.t()
Returns the API version string used in OCI registry paths.
This is used to construct paths for all registry operations. Currently returns "v2" as per the OCI Distribution Specification.
Examples
iex> OCI.Registry.api_version()
"v2"
Generates the path for a blob with the given digest in a repository.
Parameters
- repo: The repository name
- digest: The digest of the blob (e.g. "sha256:abc123...")
Returns
A string representing the full path to the blob.
Examples
iex> OCI.Registry.blobs_digest_path("myrepo", "sha256:abc123")
"/v2/myrepo/blobs/sha256:abc123"
Generates the path for an ongoing blob upload session.
Parameters
- repo: The repository name
- uuid: The unique identifier for the upload session
Returns
A string representing the full path to the upload session.
Examples
iex> OCI.Registry.blobs_uploads_path("myrepo", "123e4567-e89b-12d3-a456-426614174000")
"/v2/myrepo/blobs/uploads/123e4567-e89b-12d3-a456-426614174000"
@spec calculate_range(bitstring(), non_neg_integer() | nil) :: nonempty_binary()
Calculates the range of a chunk of data.
Examples
iex> OCI.Registry.calculate_range("hello", 0) "0-4" iex> OCI.Registry.calculate_range("hello", 1) "1-5"
Gets the status of an ongoing blob upload.
Parameters
- registry: The registry instance
- repo: The repository name
- uuid: The upload session ID
Returns
{:ok, range}
where range is the current range of uploaded bytes{:error, :BLOB_UPLOAD_UNKNOWN}
if the upload doesn't exist
Initializes a new registry instance with the given configuration.
Initiates a new blob upload for the given repository. Returns {:ok, location} on success or {:error, reason} on failure. The location is the full path where the blob should be uploaded.
Generates the path for a manifest reference in the OCI registry.
The path follows the OCI Distribution Specification format:
/v2/:repository/manifests/:reference
Examples
iex> OCI.Registry.manifests_reference_path("myorg/myrepo", "latest")
"/v2/myorg/myrepo/manifests/latest"
iex> OCI.Registry.manifests_reference_path("library/alpine", "sha256:24dda0a1be6293020e5355d4a09b9a8bb72a8b44c27b0ca8560669b8ed52d3ec")
"/v2/library/alpine/manifests/sha256:24dda0a1be6293020e5355d4a09b9a8bb72a8b44c27b0ca8560669b8ed52d3ec"
Mounts a blob from one repository to another. Returns {:ok, location} on success, {:error, :BLOB_UNKNOWN} if the source blob doesn't exist.
@spec parse_range(String.t()) :: {non_neg_integer(), non_neg_integer()}
Parses a Content-Range header value into start and end positions.
Parameters
- range: A string in the format "start-end" (e.g. "0-1023")
Returns
A tuple of {start, end} integers
Examples
iex> OCI.Registry.parse_range("0-1023")
{0, 1023}
iex> OCI.Registry.parse_range("1024-2047")
{1024, 2047}
Calculates the SHA-256 hash of the given data and returns it as a lowercase hexadecimal string.
Parameters
- data: The binary data to hash
Returns
A lowercase hexadecimal string representing the SHA-256 hash.
Examples
iex> OCI.Registry.sha256("hello")
"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
Uploads a chunk of data to an existing blob upload.
Note: The Content-Range header is not always present in chunk uploads:
- For PATCH requests, Content-Range is required (validated at plug level)
- For POST (initial) and PUT (final) requests, Content-Range is optional
- For monolithic uploads, Content-Range may be omitted entirely
The maybe_chunk_range
parameter reflects this variability in the protocol.
We cannot assume or calculate the range ourselves because:
- Previous chunks may have been uploaded out of order
- The client may be using a different upload strategy
- The range is only meaningful when provided by the client
Parameters
- registry: The registry instance
- repo: The repository name
- uuid: The upload session ID
- chunk: The binary data chunk to upload
- maybe_chunk_range: Optional Content-Range header value
Returns
{:ok, location, range}
where location is the URL for the next chunk upload and range is the current range of uploaded bytes{:error, reason}
if the upload fails
Verifies that the given data matches the provided digest.
Parameters
- data: The binary data to verify
- digest: The digest to verify against (must start with "sha256:")
Returns
:ok
if the data matches the digest{:error, :DIGEST_INVALID}
if the digest is invalid or doesn't match
Examples
iex> OCI.Registry.verify_digest("hello", "sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
:ok
iex> OCI.Registry.verify_digest("hello", "sha256:wronghash")
{:error, :DIGEST_INVALID, %{digest: "sha256:wronghash", computed: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", msg: "Digest mismatch"}}
iex> OCI.Registry.verify_digest("hello", "invalid-digest")
{:error, :DIGEST_INVALID, %{digest: "invalid-digest", msg: "Invalid digest format"}}
@spec verify_upload_order(non_neg_integer(), nil | String.t()) :: :ok | {:error, :EXT_BLOB_UPLOAD_OUT_OF_ORDER, map()}
Verifies that a chunk upload is in the correct order.
When no range is provided (nil), the upload is considered valid. This is used for initial POST requests and final PUT requests where ranges are not required.
Content-Range header requirements for chunk uploads:
- Required for PATCH requests (validated at plug level)
- Must be inclusive on both ends (e.g. "0-1023")
- First chunk must begin with 0
- Chunks must be uploaded in order
- Not required for initial POST or final PUT requests
Note: While nil ranges are valid for POST/PUT, this is a potential security concern
as it could allow empty chunk uploads. This is handled by requiring Content-Range
for PATCH requests at the plug level, preventing empty chunk uploads before they
reach this verification step.
Parameters
- current_size: The current size of uploaded data in bytes
- range: The Content-Range header value or nil
Returns
:ok
if the upload is valid{:error, :EXT_BLOB_UPLOAD_OUT_OF_ORDER}
if the chunk is out of order
Examples
iex> OCI.Registry.verify_upload_order(0, nil)
:ok
iex> OCI.Registry.verify_upload_order(1024, "1024-2047")
:ok
iex> OCI.Registry.verify_upload_order(1024, "2048-3071")
{:error, :EXT_BLOB_UPLOAD_OUT_OF_ORDER, %{current_size: 1024, range: "2048-3071"}}