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
22 changes: 0 additions & 22 deletions Sources/ContainerRegistry/Blobs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,6 @@ public extension RegistryClient {
.data
}

/// Fetches a blob and tries to decode it as a JSON object.
///
/// - Parameters:
/// - repository: Name of the repository containing the blob.
/// - digest: Digest of the blob.
/// - Returns: The decoded object.
/// - Throws: If the blob download fails or the blob cannot be decoded.
///
/// Some JSON objects, such as ImageConfiguration, are stored
/// in the registry as plain blobs with MIME type "application/octet-stream".
/// This function attempts to decode the received data without reference
/// to the MIME type.
func getBlob<Response: Decodable>(repository: ImageReference.Repository, digest: ImageReference.Digest) async throws
-> Response
{
try await executeRequestThrowing(
.get(repository, path: "blobs/\(digest)", accepting: ["application/octet-stream"]),
decodingErrors: [.notFound]
)
.data
}

/// Uploads a blob to the registry.
///
/// This function uploads a blob of unstructured data to the registry.
Expand Down
26 changes: 13 additions & 13 deletions Sources/ContainerRegistry/Manifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ public extension RegistryClient {
repository: ImageReference.Repository,
reference: any ImageReference.Reference,
manifest: ImageManifest
) async throws
-> String
{
) async throws -> String {
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
let httpResponse = try await executeRequestThrowing(
// All blob uploads have Content-Type: application/octet-stream on the wire, even if mediatype is different
Expand All @@ -44,11 +42,12 @@ public extension RegistryClient {
.absoluteString
}

func getManifest(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws
-> ImageManifest
{
func getManifest(
repository: ImageReference.Repository,
reference: any ImageReference.Reference
) async throws -> ImageManifest {
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
try await executeRequestThrowing(
let (data, _) = try await executeRequestThrowing(
.get(
repository,
path: "manifests/\(reference)",
Expand All @@ -59,14 +58,15 @@ public extension RegistryClient {
),
decodingErrors: [.notFound]
)
.data
return try decoder.decode(ImageManifest.self, from: data)
}

func getIndex(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws
-> ImageIndex
{
func getIndex(
repository: ImageReference.Repository,
reference: any ImageReference.Reference
) async throws -> ImageIndex {
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
try await executeRequestThrowing(
let (data, _) = try await executeRequestThrowing(
.get(
repository,
path: "manifests/\(reference)",
Expand All @@ -77,6 +77,6 @@ public extension RegistryClient {
),
decodingErrors: [.notFound]
)
.data
return try decoder.decode(ImageIndex.self, from: data)
}
}
17 changes: 10 additions & 7 deletions Sources/ContainerRegistry/RegistryClient+ImageConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ extension RegistryClient {
/// - Throws: If the blob cannot be decoded as an `ImageConfiguration`.
///
/// Image configuration records are stored as blobs in the registry. This function retrieves the requested blob and tries to decode it as a configuration record.
public func getImageConfiguration(forImage image: ImageReference, digest: ImageReference.Digest) async throws
-> ImageConfiguration
{
try await getBlob(repository: image.repository, digest: digest)
public func getImageConfiguration(
forImage image: ImageReference,
digest: ImageReference.Digest
) async throws -> ImageConfiguration {
let data = try await getBlob(repository: image.repository, digest: digest)
return try decoder.decode(ImageConfiguration.self, from: data)
}

/// Upload an image configuration record to the registry.
Expand All @@ -35,9 +37,10 @@ extension RegistryClient {
/// - Throws: If the blob upload fails.
///
/// Image configuration records are stored as blobs in the registry. This function encodes the provided configuration record and stores it as a blob in the registry.
public func putImageConfiguration(forImage image: ImageReference, configuration: ImageConfiguration) async throws
-> ContentDescriptor
{
public func putImageConfiguration(
forImage image: ImageReference,
configuration: ImageConfiguration
) async throws -> ContentDescriptor {
try await putBlob(
repository: image.repository,
mediaType: "application/vnd.oci.image.config.v1+json",
Expand Down
24 changes: 0 additions & 24 deletions Sources/ContainerRegistry/RegistryClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,27 +308,6 @@ extension RegistryClient {
}
}

/// Execute an HTTP request with no request body, decoding the JSON response
/// - Parameters:
/// - request: The HTTP request to execute.
/// - success: The HTTP status code expected if the request is successful.
/// - errors: Expected error codes for which the registry sends structured error messages.
/// - Returns: An asynchronously-delivered tuple that contains the raw response body as a Data instance, and a HTTPURLResponse.
/// - Throws: If the server response is unexpected or indicates that an error occurred.
func executeRequestThrowing<Response: Decodable>(
_ request: RegistryOperation,
expectingStatus success: HTTPResponse.Status = .ok,
decodingErrors errors: [HTTPResponse.Status]
) async throws -> (data: Response, response: HTTPResponse) {
let (data, httpResponse) = try await executeRequestThrowing(
request,
expectingStatus: success,
decodingErrors: errors
)
let decoded = try decoder.decode(Response.self, from: data)
return (decoded, httpResponse)
}

/// Execute an HTTP request uploading a request body.
/// - Parameters:
/// - operation: The Registry operation to execute.
Expand All @@ -337,9 +316,6 @@ extension RegistryClient {
/// - errors: Expected error codes for which the registry sends structured error messages.
/// - Returns: An asynchronously-delivered tuple that contains the raw response body as a Data instance, and a HTTPURLResponse.
/// - Throws: If the server response is unexpected or indicates that an error occurred.
///
/// A plain Data version of this function is required because Data is Encodable and encodes to base64.
/// Accidentally encoding data blobs will cause digests to fail and runtimes to be unable to run the images.
func executeRequestThrowing(
_ operation: RegistryOperation,
uploading payload: Data,
Expand Down
6 changes: 5 additions & 1 deletion Sources/ContainerRegistry/Tags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
public extension RegistryClient {
func getTags(repository: ImageReference.Repository) async throws -> Tags {
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-tags
try await executeRequestThrowing(.get(repository, path: "tags/list"), decodingErrors: [.notFound]).data
let (data, _) = try await executeRequestThrowing(
.get(repository, path: "tags/list"),
decodingErrors: [.notFound]
)
return try decoder.decode(Tags.self, from: data)
}
}