diff --git a/Sources/Containerization/ContainerManager.swift b/Sources/Containerization/ContainerManager.swift index 27e9fbe4..7c230912 100644 --- a/Sources/Containerization/ContainerManager.swift +++ b/Sources/Containerization/ContainerManager.swift @@ -205,6 +205,7 @@ public struct ContainerManager: Sendable { writableLayerSizeInBytes: UInt64? = nil, readOnly: Bool = false, networking: Bool = true, + platform: Platform = .current, progress: ProgressHandler? = nil, configuration: (inout LinuxContainer.Configuration) throws -> Void ) async throws -> LinuxContainer { @@ -216,6 +217,7 @@ public struct ContainerManager: Sendable { writableLayerSizeInBytes: writableLayerSizeInBytes, readOnly: readOnly, networking: networking, + platform: platform, progress: progress, configuration: configuration ) @@ -231,6 +233,11 @@ public struct ContainerManager: Sendable { /// - readOnly: Whether to mount the root filesystem as read-only. /// - networking: Whether to create a network interface for this container. Defaults to `true`. /// When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call. + /// - platform: Which platform's image variant to use. Defaults to `.current`, preserving the + /// previous behaviour. A caller running a FOREIGN-architecture image (e.g. an x86_64 image + /// on an arm64 host under Rosetta) must pass that image's platform: resolving the config + /// and unpacking the rootfs both need the variant that actually exists in the image, and + /// `.current` fails before any translation layer is consulted. /// - progress: Optional handler for tracking rootfs unpacking progress. public mutating func create( _ id: String, @@ -239,6 +246,7 @@ public struct ContainerManager: Sendable { writableLayerSizeInBytes: UInt64? = nil, readOnly: Bool = false, networking: Bool = true, + platform: Platform = .current, progress: ProgressHandler? = nil, configuration: (inout LinuxContainer.Configuration) throws -> Void ) async throws -> LinuxContainer { @@ -248,6 +256,7 @@ public struct ContainerManager: Sendable { image: image, destination: path.appendingPathComponent("rootfs.ext4"), size: rootfsSizeInBytes, + platform: platform, progress: progress ) if readOnly { @@ -269,6 +278,7 @@ public struct ContainerManager: Sendable { rootfs: rootfs, writableLayer: writableLayer, networking: networking, + platform: platform, configuration: configuration ) } @@ -290,9 +300,10 @@ public struct ContainerManager: Sendable { rootfs: Mount, writableLayer: Mount? = nil, networking: Bool = true, + platform: Platform = .current, configuration: (inout LinuxContainer.Configuration) throws -> Void ) async throws -> LinuxContainer { - let imageConfig = try await image.config(for: .current).config + let imageConfig = try await image.config(for: platform).config return try LinuxContainer( id, rootfs: rootfs, @@ -340,10 +351,13 @@ public struct ContainerManager: Sendable { return path } - private func unpack(image: Image, destination: URL, size: UInt64, progress: ProgressHandler? = nil) async throws -> Mount { + private func unpack( + image: Image, destination: URL, size: UInt64, platform: Platform = .current, + progress: ProgressHandler? = nil + ) async throws -> Mount { do { let unpacker = EXT4Unpacker(capacityInBytes: size) - return try await unpacker.unpack(image, for: .current, at: destination, progress: progress) + return try await unpacker.unpack(image, for: platform, at: destination, progress: progress) } catch let err as ContainerizationError { if err.code == .exists { return .block(