Fix: Allow OCI archives without manifest annotations (#397)

Fixes #369

Per the OCI Image Spec, manifest descriptor annotations are optional.
Previously, archives without annotations would fail to import with
"Failed to import image".

**Changes:**
- Modified `getImageReferencefromDescriptor` to return digest-based
references (`untagged@sha256:...`) when annotations are missing
- Removed guard that skipped manifests without annotations
- Added test case with `scratch_no_annotations.tar`

**Testing:**
All 167 tests pass, including new test for images without annotations.
This commit is contained in:
RahulThennarasu
2025-11-15 00:32:15 -03:00
committed by GitHub
parent 4c761d50c6
commit 4d47c58a3d
5 changed files with 42 additions and 17 deletions
+4 -1
View File
@@ -90,7 +90,10 @@ let package = Package(
name: "ContainerizationUnitTests",
dependencies: ["Containerization"],
path: "Tests/ContainerizationTests",
resources: [.copy("ImageTests/Resources/scratch.tar")]
resources: [
.copy("ImageTests/Resources/scratch.tar"),
.copy("ImageTests/Resources/scratch_no_annotations.tar"),
]
),
.target(
name: "ContainerizationEXT4",
@@ -80,9 +80,7 @@ extension ImageStore {
let (id, tempDir) = try await self.contentStore.newIngestSession()
do {
for descriptor in index.manifests {
guard let reference = client.getImageReferencefromDescriptor(descriptor: descriptor) else {
continue
}
let reference = client.getImageReferencefromDescriptor(descriptor: descriptor)
let ref = try Reference.parse(reference)
let name = ref.path
let operation = ImportOperation(name: name, contentStore: self.contentStore, client: client, ingestDir: tempDir, progress: progress)
@@ -205,11 +205,8 @@ extension LocalOCILayoutClient {
descriptor.annotations = annotations
}
package func getImageReferencefromDescriptor(descriptor: Descriptor) -> String? {
package func getImageReferencefromDescriptor(descriptor: Descriptor) -> String {
let annotations = descriptor.annotations
guard let annotations else {
return nil
}
// Annotations here do not conform to the OCI image specification.
// The interpretation of the annotations "org.opencontainers.image.ref.name" and
@@ -220,16 +217,21 @@ extension LocalOCILayoutClient {
// https://github.com/moby/buildkit/issues/4615#issuecomment-2521810830
// Until a consensus is reached, the preference is given to "com.apple.containerization.image.name" and then to
// using "io.containerd.image.name" as it is the next safest choice
if let name = annotations[AnnotationKeys.containerizationImageName] {
return name
if let annotations {
if let name = annotations[AnnotationKeys.containerizationImageName] {
return name
}
if let name = annotations[AnnotationKeys.containerdImageName] {
return name
}
if let name = annotations[AnnotationKeys.openContainersImageName] {
return name
}
}
if let name = annotations[AnnotationKeys.containerdImageName] {
return name
}
if let name = annotations[AnnotationKeys.openContainersImageName] {
return name
}
return nil
// Fallback: Generate digest-based reference for images without annotations
// This makes sure OCI spec compliance as annotations are optional
return "untagged@\(descriptor.digest)"
}
package enum Error: Swift.Error {
@@ -87,4 +87,26 @@ public class ImageStoreTests: ContainsAuth {
let _ = try await self.store.tag(existing: imageReference, new: upstreamTag)
try await self.store.push(reference: upstreamTag, auth: authentication)
}
@Test func testLoadImageWithoutAnnotations() async throws {
let fileManager = FileManager.default
let tempDir = fileManager.uniqueTemporaryDirectory()
defer {
try? fileManager.removeItem(at: tempDir)
}
let tarPath = Foundation.Bundle.module.url(forResource: "scratch_no_annotations", withExtension: "tar")!
let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)
try reader.extractContents(to: tempDir)
let loaded = try await self.store.load(from: tempDir)
#expect(loaded.count == 1)
let reference = loaded.first!.reference
#expect(reference.hasPrefix("untagged@sha256:"))
let retrieved = try await self.store.get(reference: reference)
#expect(retrieved.reference == reference)
}
}