Skip to content

Commit 83852d0

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 83852d0

File tree

5 files changed

+34
-65
lines changed

5 files changed

+34
-65
lines changed

Sources/ContainerRegistry/ImageManifest+Digest.swift

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

Sources/ContainerRegistry/Manifests.swift

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,38 +15,33 @@
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
20-
) async throws
21-
-> String
22-
{
20+
) async throws -> ContentDescriptor {
2321
// 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
22+
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+
27+
let _ = try await executeRequestThrowing(
2628
.put(
2729
repository,
28-
path: "manifests/\(reference)",
29-
contentType: manifest.mediaType ?? "application/vnd.oci.image.manifest.v1+json"
30+
path: "manifests/\(reference ?? digest)",
31+
contentType: mediaType
3032
),
31-
uploading: manifest,
33+
uploading: encoded,
3234
expectingStatus: .created,
3335
decodingErrors: [.notFound]
3436
)
3537

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
38+
return .init(mediaType: mediaType, digest: "\(digest)", size: Int64(encoded.count))
4539
}
4640

47-
func getManifest(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws
48-
-> ImageManifest
49-
{
41+
func getManifest(
42+
repository: ImageReference.Repository,
43+
reference: any ImageReference.Reference
44+
) async throws -> ImageManifest {
5045
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
5146
try await executeRequestThrowing(
5247
.get(
@@ -62,9 +57,10 @@ public extension RegistryClient {
6257
.data
6358
}
6459

65-
func getIndex(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws
66-
-> ImageIndex
67-
{
60+
func getIndex(
61+
repository: ImageReference.Repository,
62+
reference: any ImageReference.Reference
63+
) async throws -> ImageIndex {
6864
// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
6965
try await executeRequestThrowing(
7066
.get(

Sources/containertool/Extensions/RegistryClient+Layers.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import ContainerRegistry
1717

1818
extension RegistryClient {
1919
func getImageManifest(forImage image: ImageReference, architecture: String) async throws -> ImageManifest {
20-
// We pushed the amd64 tag but it points to a single-architecture index, not directly to a manifest
21-
// if we get an index we should get a manifest, otherwise we might get a manifest directly
22-
2320
do {
2421
// Try to retrieve a manifest. If the object with this reference is actually an index, the content-type will not match and
2522
// an error will be thrown.

Sources/containertool/containertool.swift

Lines changed: 12 additions & 9 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,
@@ -368,22 +368,25 @@ extension RegistryClient {
368368

369369
// MARK: Upload application manifest
370370

371+
let manifestDescriptor = try await self.putManifest(
372+
repository: destinationImage.repository,
373+
reference: destinationImage.reference,
374+
manifest: manifest
375+
)
376+
377+
if verbose {
378+
log("manifest: \(manifestDescriptor.digest) (\(manifestDescriptor.size) bytes)")
379+
}
380+
371381
// Use the manifest's digest if the user did not provide a human-readable tag
372382
// To support multiarch images, we should also create an an index pointing to
373383
// this manifest.
374384
let reference: ImageReference.Reference
375385
if let tag {
376386
reference = try ImageReference.Tag(tag)
377387
} else {
378-
reference = manifest.digest
388+
reference = try ImageReference.Digest(manifestDescriptor.digest)
379389
}
380-
let location = try await self.putManifest(
381-
repository: destinationImage.repository,
382-
reference: destinationImage.reference,
383-
manifest: manifest
384-
)
385-
386-
if verbose { log(location) }
387390

388391
var result = destinationImage
389392
result.reference = reference

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)