Skip to content

Commit 66860a6

Browse files
committed
ContainerRegistry: Return a ContentDescriptor from putManifest()
Returning a ContentDescriptor provides the information needed to add the manifest to an image index, making it possible to build multi-architecture images.
1 parent 792ca55 commit 66860a6

File tree

4 files changed

+29
-58
lines changed

4 files changed

+29
-58
lines changed

Sources/ContainerRegistry/ImageManifest+Digest.swift

Lines changed: 0 additions & 26 deletions
This file was deleted.

Sources/ContainerRegistry/Manifests.swift

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,28 @@
1515
public extension RegistryClient {
1616
func putManifest(
1717
repository: ImageReference.Repository,
18-
reference: any ImageReference.Reference,
18+
reference: (any ImageReference.Reference)? = nil,
1919
manifest: ImageManifest
2020
) async throws
21-
-> String
21+
-> ContentDescriptor
2222
{
23+
let encoded = try encoder.encode(manifest)
24+
let digest = digest(of: encoded)
25+
let mediaType = manifest.mediaType ?? "application/vnd.oci.image.manifest.v1+json"
26+
2327
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
24-
let httpResponse = try await executeRequestThrowing(
25-
// All blob uploads have Content-Type: application/octet-stream on the wire, even if mediatype is different
28+
let _ = try await executeRequestThrowing(
2629
.put(
2730
repository,
28-
path: "manifests/\(reference)",
29-
contentType: manifest.mediaType ?? "application/vnd.oci.image.manifest.v1+json"
31+
path: "manifests/\(reference ?? digest)",
32+
contentType: mediaType
3033
),
31-
uploading: manifest,
34+
uploading: encoded,
3235
expectingStatus: .created,
3336
decodingErrors: [.notFound]
3437
)
3538

36-
// The distribution spec says the response MUST contain a Location header
37-
// providing a URL from which the saved manifest can be downloaded.
38-
// However some registries return URLs which cannot be fetched, and
39-
// ECR does not set this header at all.
40-
// If the header is not present, create a suitable value.
41-
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
42-
return httpResponse.response.headerFields[.location]
43-
?? registryURL.distributionEndpoint(forRepository: repository, andEndpoint: "manifests/\(manifest.digest)")
44-
.absoluteString
39+
return .init(mediaType: mediaType, digest: "\(digest)", size: Int64(encoded.count))
4540
}
4641

4742
func getManifest(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws

Sources/containertool/containertool.swift

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ extension RegistryClient {
254254
forImage: baseImage,
255255
architecture: architecture
256256
)
257-
log("Found base image manifest: \(baseImageManifest.digest)")
257+
//try log("Found base image manifest: \(digest(of: baseImageManifest))")
258258

259259
baseImageConfiguration = try await source.getImageConfiguration(
260260
forImage: baseImage,
@@ -371,22 +371,25 @@ extension RegistryClient {
371371
// Use the manifest's digest if the user did not provide a human-readable tag
372372
// To support multiarch images, we should also create an an index pointing to
373373
// this manifest.
374-
let reference: ImageReference.Reference
375-
if let tag {
376-
reference = try ImageReference.Tag(tag)
377-
} else {
378-
reference = manifest.digest
379-
}
380-
let location = try await self.putManifest(
374+
let reference = try tag.map { try ImageReference.Tag($0) }
375+
376+
let manifestDescriptor = try await self.putManifest(
381377
repository: destinationImage.repository,
382-
reference: destinationImage.reference,
378+
reference: reference,
383379
manifest: manifest
384380
)
385381

386-
if verbose { log(location) }
382+
if verbose {
383+
log("manifest: \(manifestDescriptor.digest) (\(manifestDescriptor.size) bytes)")
384+
}
387385

388-
var result = destinationImage
389-
result.reference = reference
390-
return result
386+
// If the user supplied a tag, use it in place of the hash in the image reference
387+
if tag != nil {
388+
return destinationImage
389+
} else {
390+
var result = destinationImage
391+
result.reference = try ImageReference.Digest(manifestDescriptor.digest)
392+
return result
393+
}
391394
}
392395
}

Tests/ContainerRegistryTests/SmokeTests.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,14 @@ struct SmokeTests {
181181
layers: [image_descriptor]
182182
)
183183

184-
let _ = try await client.putManifest(
184+
let descriptor = try await client.putManifest(
185185
repository: repository,
186-
reference: test_manifest.digest,
187186
manifest: test_manifest
188187
)
189188

190189
let manifest = try await client.getManifest(
191190
repository: repository,
192-
reference: test_manifest.digest
191+
reference: ImageReference.Digest(descriptor.digest)
193192
)
194193
#expect(manifest.schemaVersion == 2)
195194
#expect(manifest.config.mediaType == "application/vnd.docker.container.image.v1+json")

0 commit comments

Comments
 (0)