diff --git a/Sources/Containerization/Agent/Vminitd.swift b/Sources/Containerization/Agent/Vminitd.swift index fc88b485..5a6dc228 100644 --- a/Sources/Containerization/Agent/Vminitd.swift +++ b/Sources/Containerization/Agent/Vminitd.swift @@ -55,7 +55,7 @@ extension Vminitd: VirtualMachineAgent { try await setenv(key: "PATH", value: Self.defaultPath) - let mounts: [ContainerizationOCI.Mount] = [ + let mounts: [OCIMount] = [ .init(type: "sysfs", source: "sysfs", destination: "/sys"), .init(type: "tmpfs", source: "tmpfs", destination: "/tmp"), .init(type: "devpts", source: "devpts", destination: "/dev/pts", options: ["gid=5", "mode=620", "ptmxmode=666"]), @@ -67,7 +67,7 @@ extension Vminitd: VirtualMachineAgent { } /// Mount a filesystem in the sandbox's environment. - public func mount(_ mount: ContainerizationOCI.Mount) async throws { + public func mount(_ mount: OCIMount) async throws { _ = try await client.mount( .with { $0.type = mount.type @@ -102,7 +102,7 @@ extension Vminitd: VirtualMachineAgent { stdinPort: UInt32?, stdoutPort: UInt32?, stderrPort: UInt32?, - configuration: ContainerizationOCI.Spec, + configuration: OCISpec, options: Data? ) async throws { let enc = JSONEncoder() diff --git a/Sources/Containerization/Image/Image.swift b/Sources/Containerization/Image/Image.swift index 9fc57d18..ba809a7d 100644 --- a/Sources/Containerization/Image/Image.swift +++ b/Sources/Containerization/Image/Image.swift @@ -30,20 +30,20 @@ public struct Image: Sendable { /// The string reference of the image. public let reference: String /// The descriptor identifying the image. - public let descriptor: Descriptor + public let descriptor: OCIDescriptor /// The digest for the image. public var digest: String { descriptor.digest } /// The media type of the image. public var mediaType: String { descriptor.mediaType } - public init(reference: String, descriptor: Descriptor) { + public init(reference: String, descriptor: OCIDescriptor) { self.reference = reference self.descriptor = descriptor } } /// The descriptor for the image. - public var descriptor: Descriptor { description.descriptor } + public var descriptor: OCIDescriptor { description.descriptor } /// The digest of the image. public var digest: String { description.digest } /// The media type of the image. @@ -57,7 +57,7 @@ public struct Image: Sendable { } /// Returns the underlying OCI index for the image. - public func index() async throws -> Index { + public func index() async throws -> OCIIndex { guard let content: Content = try await contentStore.get(digest: digest) else { throw ContainerizationError(.notFound, message: "Content with digest \(digest)") } @@ -65,7 +65,7 @@ public struct Image: Sendable { } /// Returns the manifest for the specified platform. - public func manifest(for platform: Platform) async throws -> Manifest { + public func manifest(for platform: OCIPlatform) async throws -> OCIManifest { let index = try await self.index() let desc = index.manifests.first { desc in desc.platform == platform @@ -81,7 +81,7 @@ public struct Image: Sendable { /// Returns the descriptor for the given platform. If it does not exist /// will throw a ContainerizationError with the code set to .invalidArgument. - public func descriptor(for platform: Platform) async throws -> Descriptor { + public func descriptor(for platform: OCIPlatform) async throws -> OCIDescriptor { let index = try await self.index() let desc = index.manifests.first { $0.platform == platform } guard let desc else { @@ -91,7 +91,7 @@ public struct Image: Sendable { } /// Returns the OCI config for the specified platform. - public func config(for platform: Platform) async throws -> ContainerizationOCI.Image { + public func config(for platform: OCIPlatform) async throws -> OCIImage { let manifest = try await self.manifest(for: platform) let desc = manifest.config guard let content: Content = try await contentStore.get(digest: desc.digest) else { @@ -106,7 +106,7 @@ public struct Image: Sendable { let index = try await self.index() for manifest in index.manifests { referenced.append(manifest.digest.trimmingDigestPrefix) - guard let m: Manifest = try? await contentStore.get(digest: manifest.digest) else { + guard let m: OCIManifest = try? await contentStore.get(digest: manifest.digest) else { // If the requested digest does not exist or is not a manifest. Skip. // Its safe to skip processing this digest as it wont have any child layers. continue diff --git a/Sources/Containerization/Image/ImageStore/ImageStore+Export.swift b/Sources/Containerization/Image/ImageStore/ImageStore+Export.swift index 43eff952..920b8f64 100644 --- a/Sources/Containerization/Image/ImageStore/ImageStore+Export.swift +++ b/Sources/Containerization/Image/ImageStore/ImageStore+Export.swift @@ -40,9 +40,9 @@ extension ImageStore { } @discardableResult - internal func export(index: Descriptor, platforms: (Platform) -> Bool) async throws -> Descriptor { - var pushQueue: [[Descriptor]] = [] - var current: [Descriptor] = [index] + internal func export(index: OCIDescriptor, platforms: (OCIPlatform) -> Bool) async throws -> OCIDescriptor { + var pushQueue: [[OCIDescriptor]] = [] + var current: [OCIDescriptor] = [index] while !current.isEmpty { let children = try await self.getChildren(descs: current) let matches = try filterPlatforms(matcher: platforms, children).uniqued { $0.digest } @@ -78,8 +78,8 @@ extension ImageStore { // Lastly, we need to construct and push a new index, since we may // have pushed content only for specific platforms. let digest = SHA256.hash(data: localIndexData) - let descriptor = Descriptor( - mediaType: MediaTypes.index, + let descriptor = OCIDescriptor( + mediaType: OCIMediaTypes.index, digest: digest.digestString, size: Int64(localIndexData.count)) let stream = ReadStream(data: localIndexData) @@ -87,7 +87,7 @@ extension ImageStore { return descriptor } - private func updatePushProgress(pushQueue: [[Descriptor]], localIndexData: Data) async { + private func updatePushProgress(pushQueue: [[OCIDescriptor]], localIndexData: Data) async { for layerGroup in pushQueue { for desc in layerGroup { await progress?([ @@ -102,13 +102,13 @@ extension ImageStore { ]) } - private func createIndex(from index: Descriptor, matching: (Platform) -> Bool) async throws -> Data { + private func createIndex(from index: OCIDescriptor, matching: (OCIPlatform) -> Bool) async throws -> Data { guard let content = try await self.contentStore.get(digest: index.digest) else { throw ContainerizationError(.notFound, message: "Content with digest \(index.digest)") } - var idx: Index = try content.decode() + var idx: OCIIndex = try content.decode() let manifests = idx.manifests - var matchedManifests: [Descriptor] = [] + var matchedManifests: [OCIDescriptor] = [] var skippedPlatforms = false for manifest in manifests { guard let p = manifest.platform else { @@ -127,7 +127,7 @@ extension ImageStore { return try JSONEncoder().encode(idx) } - private func pushContent(descriptor: Descriptor, stream: ReadStream) async throws { + private func pushContent(descriptor: OCIDescriptor, stream: ReadStream) async throws { do { let generator = { try stream.reset() @@ -151,19 +151,19 @@ extension ImageStore { } } - private func getChildren(descs: [Descriptor]) async throws -> [Descriptor] { - var out: [Descriptor] = [] + private func getChildren(descs: [OCIDescriptor]) async throws -> [OCIDescriptor] { + var out: [OCIDescriptor] = [] for desc in descs { let mediaType = desc.mediaType guard let content = try await self.contentStore.get(digest: desc.digest) else { throw ContainerizationError(.notFound, message: "Content with digest \(desc.digest)") } switch mediaType { - case MediaTypes.index, MediaTypes.dockerManifestList: - let index: Index = try content.decode() + case OCIMediaTypes.index, OCIMediaTypes.dockerManifestList: + let index: OCIIndex = try content.decode() out.append(contentsOf: index.manifests) - case MediaTypes.imageManifest, MediaTypes.dockerManifest: - let manifest: Manifest = try content.decode() + case OCIMediaTypes.imageManifest, OCIMediaTypes.dockerManifest: + let manifest: OCIManifest = try content.decode() out.append(manifest.config) out.append(contentsOf: manifest.layers) default: diff --git a/Sources/Containerization/Image/ImageStore/ImageStore+Import.swift b/Sources/Containerization/Image/ImageStore/ImageStore+Import.swift index 7dc4eb4a..48409ad1 100644 --- a/Sources/Containerization/Image/ImageStore/ImageStore+Import.swift +++ b/Sources/Containerization/Image/ImageStore/ImageStore+Import.swift @@ -14,8 +14,6 @@ // limitations under the License. //===----------------------------------------------------------------------===// -// - import ContainerizationError import ContainerizationExtras import ContainerizationOCI @@ -40,7 +38,7 @@ extension ImageStore { } /// Pull the required image layers for the provided descriptor and platform(s) into the given directory using the provided client. Returns a descriptor to the Index manifest. - internal func `import`(root: Descriptor, matcher: (ContainerizationOCI.Platform) -> Bool) async throws -> Descriptor { + internal func `import`(root: OCIDescriptor, matcher: (OCIPlatform) -> Bool) async throws -> OCIDescriptor { var toProcess = [root] while !toProcess.isEmpty { // Count the total number of blobs and their size @@ -61,14 +59,14 @@ extension ImageStore { toProcess = filtered.uniqued { $0.digest } } - guard root.mediaType != MediaTypes.dockerManifestList && root.mediaType != MediaTypes.index else { + guard root.mediaType != OCIMediaTypes.dockerManifestList && root.mediaType != OCIMediaTypes.index else { return root } // Create an index for the root descriptor and write it to the content store let index = try await self.createIndex(for: root) - // In cases where the root descriptor pointed to `MediaTypes.imageManifest` - // Or `MediaTypes.dockerManifest`, it is required that we check the supported platform + // In cases where the root descriptor pointed to `OCIMediaTypes.imageManifest` + // Or `OCIMediaTypes.dockerManifest`, it is required that we check the supported platform // matches the platforms we were asked to pull. This can be done only after we created // the Index. let supportedPlatforms = index.manifests.compactMap { $0.platform } @@ -77,13 +75,13 @@ extension ImageStore { } let writer = try ContentWriter(for: self.ingestDir) let result = try writer.create(from: index) - return Descriptor( - mediaType: MediaTypes.index, + return OCIDescriptor( + mediaType: OCIMediaTypes.index, digest: result.digest.digestString, size: Int64(result.size)) } - private func getManifestContent(descriptor: Descriptor) async throws -> T { + private func getManifestContent(descriptor: OCIDescriptor) async throws -> T { do { if let content = try await self.contentStore.get(digest: descriptor.digest.trimmingDigestPrefix) { return try content.decode() @@ -97,16 +95,16 @@ extension ImageStore { } } - private func walk(_ descriptors: [Descriptor]) async throws -> [Descriptor] { - var out: [Descriptor] = [] + private func walk(_ descriptors: [OCIDescriptor]) async throws -> [OCIDescriptor] { + var out: [OCIDescriptor] = [] for desc in descriptors { let mediaType = desc.mediaType switch mediaType { - case MediaTypes.index, MediaTypes.dockerManifestList: - let index: Index = try await self.getManifestContent(descriptor: desc) + case OCIMediaTypes.index, OCIMediaTypes.dockerManifestList: + let index: OCIIndex = try await self.getManifestContent(descriptor: desc) out.append(contentsOf: index.manifests) - case MediaTypes.imageManifest, MediaTypes.dockerManifest: - let manifest: Manifest = try await self.getManifestContent(descriptor: desc) + case OCIMediaTypes.imageManifest, OCIMediaTypes.dockerManifest: + let manifest: OCIManifest = try await self.getManifestContent(descriptor: desc) out.append(manifest.config) out.append(contentsOf: manifest.layers) default: @@ -117,7 +115,7 @@ extension ImageStore { return out } - private func fetchAll(_ descriptors: [Descriptor]) async throws { + private func fetchAll(_ descriptors: [OCIDescriptor]) async throws { try await withThrowingTaskGroup(of: Void.self) { group in var iterator = descriptors.makeIterator() for _ in 0..<8 { @@ -137,7 +135,7 @@ extension ImageStore { } } - private func fetch(_ descriptor: Descriptor) async throws { + private func fetch(_ descriptor: OCIDescriptor) async throws { if let found = try await self.contentStore.get(digest: descriptor.digest) { try FileManager.default.copyItem(at: found.path, to: ingestDir.appendingPathComponent(descriptor.digest.trimmingDigestPrefix)) await progress?([ @@ -160,7 +158,7 @@ extension ImageStore { ]) } - private func fetchBlob(_ descriptor: Descriptor) async throws { + private func fetchBlob(_ descriptor: OCIDescriptor) async throws { let id = UUID().uuidString let fm = FileManager.default let tempFile = ingestDir.appendingPathComponent(id) @@ -179,7 +177,7 @@ extension ImageStore { } @discardableResult - private func fetchData(_ descriptor: Descriptor) async throws -> Data { + private func fetchData(_ descriptor: OCIDescriptor) async throws -> Data { let data = try await client.fetchData(name: name, descriptor: descriptor) let writer = try ContentWriter(for: ingestDir) let result = try writer.write(data) @@ -195,11 +193,11 @@ extension ImageStore { return data } - private func createIndex(for root: Descriptor) async throws -> Index { + private func createIndex(for root: OCIDescriptor) async throws -> OCIIndex { switch root.mediaType { - case MediaTypes.index, MediaTypes.dockerManifestList: + case OCIMediaTypes.index, OCIMediaTypes.dockerManifestList: return try await self.getManifestContent(descriptor: root) - case MediaTypes.imageManifest, MediaTypes.dockerManifest: + case OCIMediaTypes.imageManifest, OCIMediaTypes.dockerManifest: let supportedPlatforms = try await getSupportedPlatforms(for: root) guard supportedPlatforms.count == 1 else { throw ContainerizationError( @@ -211,11 +209,11 @@ extension ImageStore { let platform = supportedPlatforms.first! var root = root root.platform = platform - let index = ContainerizationOCI.Index( + let index = OCIIndex( schemaVersion: 2, manifests: [root], annotations: [ // indicate that this is a synthesized index which is not directly user facing - AnnotationKeys.containerizationIndexIndirect: "true" + OCIAnnotationKeys.containerizationIndexIndirect: "true" ]) return index default: @@ -223,8 +221,8 @@ extension ImageStore { } } - private func getSupportedPlatforms(for root: Descriptor) async throws -> [ContainerizationOCI.Platform] { - var supportedPlatforms: [ContainerizationOCI.Platform] = [] + private func getSupportedPlatforms(for root: OCIDescriptor) async throws -> [OCIPlatform] { + var supportedPlatforms: [OCIPlatform] = [] var toProcess = [root] while !toProcess.isEmpty { let children = try await self.walk(toProcess) @@ -234,9 +232,9 @@ extension ImageStore { continue } switch child.mediaType { - case MediaTypes.imageConfig, MediaTypes.dockerImageConfig: - let config: ContainerizationOCI.Image = try await self.getManifestContent(descriptor: child) - let p = ContainerizationOCI.Platform( + case OCIMediaTypes.imageConfig, OCIMediaTypes.dockerImageConfig: + let config: OCIImage = try await self.getManifestContent(descriptor: child) + let p = OCIPlatform( arch: config.architecture, os: config.os, osFeatures: config.osFeatures, variant: config.variant ) supportedPlatforms.append(p) diff --git a/Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift b/Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift index 691021b5..1e236a7b 100644 --- a/Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift +++ b/Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift @@ -30,7 +30,7 @@ extension ImageStore { /// - platform: An optional parameter to indicate the platform to be saved for the images. /// Defaults to `nil` signifying that layers for all supported platforms by the images will be saved. /// - public func save(references: [String], out: URL, platform: Platform? = nil) async throws { + public func save(references: [String], out: URL, platform: OCIPlatform? = nil) async throws { let matcher = createPlatformMatcher(for: platform) let fileManager = FileManager.default let tempDir = fileManager.uniqueTemporaryDirectory() @@ -41,14 +41,14 @@ extension ImageStore { var toSave: [Image] = [] for reference in references { let image = try await self.get(reference: reference) - let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index] + let allowedMediaTypes = [OCIMediaTypes.dockerManifestList, OCIMediaTypes.index] guard allowedMediaTypes.contains(image.mediaType) else { throw ContainerizationError(.internalError, message: "Cannot save image \(image.reference) with Index media type \(image.mediaType)") } toSave.append(image) } let client = try LocalOCILayoutClient(root: out) - var saved: [Descriptor] = [] + var saved: [OCIDescriptor] = [] for image in toSave { let ref = try Reference.parse(image.reference) diff --git a/Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift b/Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift index 77ea73c1..ca54ceb2 100644 --- a/Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift +++ b/Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift @@ -24,7 +24,7 @@ extension ImageStore { internal actor ReferenceManager: Sendable { private let path: URL - private typealias State = [String: Descriptor] + private typealias State = [String: OCIDescriptor] private var images: State public init(path: URL) throws { diff --git a/Sources/Containerization/Image/ImageStore/ImageStore.swift b/Sources/Containerization/Image/ImageStore/ImageStore.swift index fa8b9eb9..02db5a4a 100644 --- a/Sources/Containerization/Image/ImageStore/ImageStore.swift +++ b/Sources/Containerization/Image/ImageStore/ImageStore.swift @@ -152,7 +152,7 @@ extension ImageStore { /// /// - Returns: A `Containerization.Image` object to the newly pulled image. public func pull( - reference: String, platform: Platform? = nil, insecure: Bool = false, + reference: String, platform: OCIPlatform? = nil, insecure: Bool = false, auth: Authentication? = nil, progress: ProgressHandler? = nil ) async throws -> Image { @@ -196,10 +196,10 @@ extension ImageStore { /// Defaults to `nil` meaning no additional credentials are added to any HTTP requests made to the registry. /// - progress: An optional handler over which progress update events about the push operation can be received. /// - public func push(reference: String, platform: Platform? = nil, insecure: Bool = false, auth: Authentication? = nil, progress: ProgressHandler? = nil) async throws { + public func push(reference: String, platform: OCIPlatform? = nil, insecure: Bool = false, auth: Authentication? = nil, progress: ProgressHandler? = nil) async throws { let matcher = createPlatformMatcher(for: platform) let img = try await self.get(reference: reference) - let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index] + let allowedMediaTypes = [OCIMediaTypes.dockerManifestList, OCIMediaTypes.index] guard allowedMediaTypes.contains(img.mediaType) else { throw ContainerizationError(.internalError, message: "Cannot push image \(reference) with Index media type \(img.mediaType)") } diff --git a/Sources/Containerization/Image/InitImage.swift b/Sources/Containerization/Image/InitImage.swift index bf0eb9d5..53ed73c3 100644 --- a/Sources/Containerization/Image/InitImage.swift +++ b/Sources/Containerization/Image/InitImage.swift @@ -43,33 +43,33 @@ extension InitImage { /// Create a new InitImage with the reference as the name. /// The `rootfs` parameter must be a tar.gz file whose contents make up the filesystem for the image. public static func create( - reference: String, rootfs: URL, platform: Platform, + reference: String, rootfs: URL, platform: OCIPlatform, labels: [String: String] = [:], imageStore: ImageStore, contentStore: ContentStore ) async throws -> InitImage { - let indexDescriptorStore = AsyncStore() + let indexDescriptorStore = AsyncStore() try await contentStore.ingest { dir in let writer = try ContentWriter(for: dir) var result = try writer.create(from: rootfs) - let layerDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageLayerGzip, digest: result.digest.digestString, size: result.size) + let layerDescriptor = OCIDescriptor(mediaType: OCIMediaTypes.imageLayerGzip, digest: result.digest.digestString, size: result.size) // TODO: compute and fill in the correct diffID for the above layer // We currently put in the sha of the fully compressed layer, this needs to be replaced with // the sha of the uncompressed layer. - let rootfsConfig = ContainerizationOCI.Rootfs(type: "layers", diffIDs: [result.digest.digestString]) - let runtimeConfig = ContainerizationOCI.ImageConfig(labels: labels) - let imageConfig = ContainerizationOCI.Image(architecture: platform.architecture, os: platform.os, config: runtimeConfig, rootfs: rootfsConfig) + let rootfsConfig = OCIRootfs(type: "layers", diffIDs: [result.digest.digestString]) + let runtimeConfig = OCIImageConfig(labels: labels) + let imageConfig = OCIImage(architecture: platform.architecture, os: platform.os, config: runtimeConfig, rootfs: rootfsConfig) result = try writer.create(from: imageConfig) - let configDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageConfig, digest: result.digest.digestString, size: result.size) + let configDescriptor = OCIDescriptor(mediaType: OCIMediaTypes.imageConfig, digest: result.digest.digestString, size: result.size) - let manifest = Manifest(config: configDescriptor, layers: [layerDescriptor]) + let manifest = OCIManifest(config: configDescriptor, layers: [layerDescriptor]) result = try writer.create(from: manifest) - let manifestDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageManifest, digest: result.digest.digestString, size: result.size, platform: platform) + let manifestDescriptor = OCIDescriptor(mediaType: OCIMediaTypes.imageManifest, digest: result.digest.digestString, size: result.size, platform: platform) - let index = ContainerizationOCI.Index(manifests: [manifestDescriptor]) + let index = OCIIndex(manifests: [manifestDescriptor]) result = try writer.create(from: index) - let indexDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.index, digest: result.digest.digestString, size: result.size) + let indexDescriptor = OCIDescriptor(mediaType: OCIMediaTypes.index, digest: result.digest.digestString, size: result.size) await indexDescriptorStore.set(indexDescriptor) } diff --git a/Sources/Containerization/Image/KernelImage.swift b/Sources/Containerization/Image/KernelImage.swift index 26f3692a..a7a5ca12 100644 --- a/Sources/Containerization/Image/KernelImage.swift +++ b/Sources/Containerization/Image/KernelImage.swift @@ -51,35 +51,35 @@ extension KernelImage { /// This will create a multi arch image containing kernel's for each provided architecture. public static func create(reference: String, binaries: [Kernel], labels: [String: String] = [:], imageStore: ImageStore, contentStore: ContentStore) async throws -> KernelImage { - let indexDescriptorStore = AsyncStore() + let indexDescriptorStore = AsyncStore() try await contentStore.ingest { ingestPath in - var descriptors = [Descriptor]() + var descriptors = [OCIDescriptor]() let writer = try ContentWriter(for: ingestPath) for kernel in binaries { var result = try writer.create(from: kernel.path) let platform = kernel.platform.ociPlatform() - let layerDescriptor = Descriptor( + let layerDescriptor = OCIDescriptor( mediaType: mediaType, digest: result.digest.digestString, size: result.size, platform: platform) - let rootfsConfig = ContainerizationOCI.Rootfs(type: "layers", diffIDs: [result.digest.digestString]) - let runtimeConfig = ContainerizationOCI.ImageConfig(labels: labels) - let imageConfig = ContainerizationOCI.Image(architecture: platform.architecture, os: platform.os, config: runtimeConfig, rootfs: rootfsConfig) + let rootfsConfig = OCIRootfs(type: "layers", diffIDs: [result.digest.digestString]) + let runtimeConfig = OCIImageConfig(labels: labels) + let imageConfig = OCIImage(architecture: platform.architecture, os: platform.os, config: runtimeConfig, rootfs: rootfsConfig) result = try writer.create(from: imageConfig) - let configDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageConfig, digest: result.digest.digestString, size: result.size) + let configDescriptor = OCIDescriptor(mediaType: OCIMediaTypes.imageConfig, digest: result.digest.digestString, size: result.size) - let manifest = Manifest(config: configDescriptor, layers: [layerDescriptor]) + let manifest = OCIManifest(config: configDescriptor, layers: [layerDescriptor]) result = try writer.create(from: manifest) - let manifestDescriptor = Descriptor( - mediaType: ContainerizationOCI.MediaTypes.imageManifest, digest: result.digest.digestString, size: result.size, platform: platform) + let manifestDescriptor = OCIDescriptor( + mediaType: OCIMediaTypes.imageManifest, digest: result.digest.digestString, size: result.size, platform: platform) descriptors.append(manifestDescriptor) } - let index = ContainerizationOCI.Index(manifests: descriptors) + let index = OCIIndex(manifests: descriptors) let result = try writer.create(from: index) - let indexDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.index, digest: result.digest.digestString, size: result.size) + let indexDescriptor = OCIDescriptor(mediaType: OCIMediaTypes.index, digest: result.digest.digestString, size: result.size) await indexDescriptorStore.set(indexDescriptor) } diff --git a/Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift b/Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift index e99d6897..6fa8d9ed 100644 --- a/Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift +++ b/Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift @@ -32,7 +32,7 @@ public struct EXT4Unpacker: Unpacker { self.blockSizeInBytes = blockSizeInBytes } - public func unpack(_ image: Image, for platform: Platform, at path: URL, progress: ProgressHandler? = nil) async throws -> Mount { + public func unpack(_ image: Image, for platform: OCIPlatform, at path: URL, progress: ProgressHandler? = nil) async throws -> Mount { #if !os(macOS) throw ContainerizationError(.unsupported, message: "Cannot unpack an image on current platform") #else @@ -47,9 +47,9 @@ public struct EXT4Unpacker: Unpacker { let compression: ContainerizationArchive.Filter switch layer.mediaType { - case MediaTypes.imageLayer, MediaTypes.dockerImageLayer: + case OCIMediaTypes.imageLayer, OCIMediaTypes.dockerImageLayer: compression = .none - case MediaTypes.imageLayerGzip, MediaTypes.dockerImageLayerGzip: + case OCIMediaTypes.imageLayerGzip, OCIMediaTypes.dockerImageLayerGzip: compression = .gzip default: throw ContainerizationError(.unsupported, message: "Media type \(layer.mediaType) not supported.") diff --git a/Sources/Containerization/Image/Unpacker/Unpacker.swift b/Sources/Containerization/Image/Unpacker/Unpacker.swift index a9a7c978..a14ee668 100644 --- a/Sources/Containerization/Image/Unpacker/Unpacker.swift +++ b/Sources/Containerization/Image/Unpacker/Unpacker.swift @@ -35,6 +35,6 @@ public protocol Unpacker { /// or transformations during the unpacking. /// /// Progress updates can be observed via the optional `progress` handler. - func unpack(_ image: Image, for platform: Platform, at path: URL, progress: ProgressHandler?) async throws -> Mount + func unpack(_ image: Image, for platform: OCIPlatform, at path: URL, progress: ProgressHandler?) async throws -> Mount } diff --git a/Sources/Containerization/LinuxContainer.swift b/Sources/Containerization/LinuxContainer.swift index ceddc97a..98edc50a 100644 --- a/Sources/Containerization/LinuxContainer.swift +++ b/Sources/Containerization/LinuxContainer.swift @@ -41,7 +41,7 @@ public final class LinuxContainer: Container, Sendable { public let rootfs: Mount private struct Configuration { - var spec: Spec + var spec: OCISpec var cpus: Int = 4 var memoryInBytes: UInt64 = 1024.mib() var interfaces: [any Interface] = [] @@ -243,7 +243,7 @@ public final class LinuxContainer: Container, Sendable { self.state = .initialized } - private static func createDefaultRuntimeSpec(_ id: String) -> Spec { + private static func createDefaultRuntimeSpec(_ id: String) -> OCISpec { .init( process: .init( cwd: "/", @@ -400,7 +400,7 @@ extension LinuxContainer { } /// The User the container should execute under. - public var user: ContainerizationOCI.User { + public var user: OCIUser { get { config.withLock { $0.spec.process!.user } } @@ -430,7 +430,7 @@ extension LinuxContainer { } /// Rlimits for the container. - public var rlimits: [POSIXRlimit] { + public var rlimits: [OCIRlimit] { get { config.withLock { $0.spec.process!.rlimits } } @@ -501,8 +501,8 @@ extension LinuxContainer { } } - public func setProcessConfig(from imageConfig: ImageConfig) { - let process = ContainerizationOCI.Process(from: imageConfig) + public func setProcessConfig(from imageConfig: OCIImageConfig) { + let process = OCIProcess(from: imageConfig) self.config.withLock { $0.spec.process = process } } @@ -596,6 +596,46 @@ extension LinuxContainer { } } + public func pause() async throws { + let vm = try state.setStarting() + + let agent = try await vm.dialAgent() + do { + var specCopy = config.withLock { $0.spec } + // We don't need the rootfs, nor do OCI runtimes want it included. + specCopy.mounts = vm.mounts.dropFirst().map { $0.to } + + let stdio = Self.setupIO( + portAllocator: self.hostVsockPorts, + stdin: self.stdin, + stdout: self.stdout, + stderr: self.stderr + ) + + let process = LinuxProcess( + self.id, + containerID: self.id, + spec: specCopy, + io: stdio, + agent: agent, + vm: vm, + logger: self.logger + ) + try await process.start() + + try state.setStarted(process: process) + } catch { + try? await agent.close() + + state.errored(error: error) + throw error + } + } + + public func resume() async throws { + + } + private static func setupIO( portAllocator: borrowing Atomic, stdin: ReaderStream?, @@ -716,7 +756,7 @@ extension LinuxContainer { /// Execute a new process in the container. public func exec( _ id: String, - configuration: ContainerizationOCI.Process, + configuration: OCIProcess, stdin: ReaderStream? = nil, stdout: Writer? = nil, stderr: Writer? = nil @@ -815,7 +855,7 @@ extension VirtualMachineInstance { } extension AttachedFilesystem { - fileprivate var to: ContainerizationOCI.Mount { + fileprivate var to: OCIMount { .init( type: self.type, source: self.source, diff --git a/Sources/Containerization/LinuxProcess.swift b/Sources/Containerization/LinuxProcess.swift index ea574f50..952215e3 100644 --- a/Sources/Containerization/LinuxProcess.swift +++ b/Sources/Containerization/LinuxProcess.swift @@ -90,7 +90,7 @@ public final class LinuxProcess: Sendable { } private struct State { - var spec: ContainerizationOCI.Spec + var spec: OCISpec var pid: Int32 var stdio: StdioHandles var stdinRelay: Task<(), Never>? @@ -144,13 +144,13 @@ public final class LinuxProcess: Sendable { } /// The User a Process should execute under. - public var user: ContainerizationOCI.User { + public var user: OCIUser { get { state.withLock { $0.spec.process!.user } } set { state.withLock { $0.spec.process!.user = newValue } } } /// Rlimits for the Process. - public var rlimits: [POSIXRlimit] { + public var rlimits: [OCIRlimit] { get { state.withLock { $0.spec.process!.rlimits } } set { state.withLock { $0.spec.process!.rlimits = newValue } } } @@ -164,7 +164,7 @@ public final class LinuxProcess: Sendable { init( _ id: String, containerID: String? = nil, - spec: Spec, + spec: OCISpec, io: Stdio, agent: any VirtualMachineAgent, vm: any VirtualMachineInstance, diff --git a/Sources/Containerization/SystemPlatform.swift b/Sources/Containerization/SystemPlatform.swift index a45d8ef3..b1f28bf6 100644 --- a/Sources/Containerization/SystemPlatform.swift +++ b/Sources/Containerization/SystemPlatform.swift @@ -32,8 +32,8 @@ public struct SystemPlatform: Sendable, Codable { } public let architecture: Architecture - public func ociPlatform() -> ContainerizationOCI.Platform { - ContainerizationOCI.Platform(arch: architecture.rawValue, os: os.rawValue) + public func ociPlatform() -> OCIPlatform { + OCIPlatform(arch: architecture.rawValue, os: os.rawValue) } public static var linuxArm: SystemPlatform { .init(os: .linux, architecture: .arm64) } diff --git a/Sources/Containerization/VirtualMachineAgent.swift b/Sources/Containerization/VirtualMachineAgent.swift index b4dc1a89..ee2f5bc7 100644 --- a/Sources/Containerization/VirtualMachineAgent.swift +++ b/Sources/Containerization/VirtualMachineAgent.swift @@ -31,7 +31,7 @@ public protocol VirtualMachineAgent: Sendable { // POSIX func getenv(key: String) async throws -> String func setenv(key: String, value: String) async throws - func mount(_ mount: ContainerizationOCI.Mount) async throws + func mount(_ mount: OCIMount) async throws func umount(path: String, flags: Int32) async throws func mkdir(path: String, all: Bool, perms: UInt32) async throws @discardableResult @@ -44,7 +44,7 @@ public protocol VirtualMachineAgent: Sendable { stdinPort: UInt32?, stdoutPort: UInt32?, stderrPort: UInt32?, - configuration: ContainerizationOCI.Spec, + configuration: OCISpec, options: Data? ) async throws func startProcess(id: String, containerID: String?) async throws -> Int32 diff --git a/Sources/ContainerizationOCI/AnnotationKeys.swift b/Sources/ContainerizationOCI/AnnotationKeys.swift index de8d9df2..c2ee161d 100644 --- a/Sources/ContainerizationOCI/AnnotationKeys.swift +++ b/Sources/ContainerizationOCI/AnnotationKeys.swift @@ -16,7 +16,8 @@ /// AnnotationKeys contains a subset of "dictionary keys" for commonly used annotations in an OCI Image Descriptor /// https://github.com/opencontainers/image-spec/blob/main/annotations.md -public struct AnnotationKeys: Codable, Sendable { + +public struct OCIAnnotationKeys: Codable, Sendable { public static let containerizationIndexIndirect = "com.apple.containerization.index.indirect" public static let containerizationImageName = "com.apple.containerization.image.name" public static let containerdImageName = "io.containerd.image.name" diff --git a/Sources/ContainerizationOCI/Bundle.swift b/Sources/ContainerizationOCI/Bundle.swift index 5a780c27..6b878d7c 100644 --- a/Sources/ContainerizationOCI/Bundle.swift +++ b/Sources/ContainerizationOCI/Bundle.swift @@ -29,7 +29,7 @@ private let _umount = Glibc.umount2 /// `Bundle` represents an OCI runtime spec bundle for running /// a container. -public struct Bundle: Sendable { +public struct OCIBundle: Sendable { /// The path to the bundle. public let path: URL @@ -49,7 +49,7 @@ public struct Bundle: Sendable { /// - path: A URL pointing to where to create the bundle on the filesystem. /// - spec: A data blob that should contain an OCI runtime spec. This will be written /// to the bundle as a "config.json" file. - public static func create(path: URL, spec: Data) throws -> Bundle { + public static func create(path: URL, spec: Data) throws -> OCIBundle { try self.init(path: path, spec: spec) } @@ -59,7 +59,7 @@ public struct Bundle: Sendable { /// - path: A URL pointing to where to create the bundle on the filesystem. /// - spec: An OCI runtime spec that will be written to the bundle as a "config.json" /// file. - public static func create(path: URL, spec: ContainerizationOCI.Spec) throws -> Bundle { + public static func create(path: URL, spec: OCISpec) throws -> OCIBundle { try self.init(path: path, spec: spec) } @@ -67,7 +67,7 @@ public struct Bundle: Sendable { /// /// - Parameters: /// - path: A URL pointing to where to load the bundle from on the filesystem. - public static func load(path: URL) throws -> Bundle { + public static func load(path: URL) throws -> OCIBundle { try self.init(path: path) } @@ -93,7 +93,7 @@ public struct Bundle: Sendable { try spec.write(to: self.configPath) } - private init(path: URL, spec: ContainerizationOCI.Spec) throws { + private init(path: URL, spec: OCISpec) throws { self.path = path let fm = FileManager.default @@ -121,8 +121,8 @@ public struct Bundle: Sendable { } /// Load and return the OCI runtime spec written to the bundle. - public func loadConfig() throws -> ContainerizationOCI.Spec { + public func loadConfig() throws -> OCISpec { let data = try Data(contentsOf: self.configPath) - return try JSONDecoder().decode(ContainerizationOCI.Spec.self, from: data) + return try JSONDecoder().decode(OCISpec.self, from: data) } } diff --git a/Sources/ContainerizationOCI/Client/LocalOCILayoutClient.swift b/Sources/ContainerizationOCI/Client/LocalOCILayoutClient.swift index 7a433ca4..93585220 100644 --- a/Sources/ContainerizationOCI/Client/LocalOCILayoutClient.swift +++ b/Sources/ContainerizationOCI/Client/LocalOCILayoutClient.swift @@ -35,12 +35,12 @@ package final class LocalOCILayoutClient: ContentClient { return c } - package func fetch(name: String, descriptor: Descriptor) async throws -> T { + package func fetch(name: String, descriptor: OCIDescriptor) async throws -> T { let c = try await self._fetch(digest: descriptor.digest) return try c.decode() } - package func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) { + package func fetchBlob(name: String, descriptor: OCIDescriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) { let c = try await self._fetch(digest: descriptor.digest) let fileManager = FileManager.default let filePath = file.absolutePath() @@ -59,7 +59,7 @@ package final class LocalOCILayoutClient: ContentClient { return (size, digest) } - package func fetchData(name: String, descriptor: Descriptor) async throws -> Data { + package func fetchData(name: String, descriptor: OCIDescriptor) async throws -> Data { let c = try await self._fetch(digest: descriptor.digest) return try c.data() } @@ -67,7 +67,7 @@ package final class LocalOCILayoutClient: ContentClient { package func push( name: String, ref: String, - descriptor: Descriptor, + descriptor: OCIDescriptor, streamGenerator: () throws -> T, progress: ProgressHandler? ) async throws where T.Element == ByteBuffer { @@ -103,7 +103,7 @@ extension LocalOCILayoutClient { private static let ociLayoutVersionString = "imageLayoutVersion" private static let ociLayoutIndexFileName = "index.json" - package func loadIndexFromOCILayout(directory: URL) throws -> ContainerizationOCI.Index { + package func loadIndexFromOCILayout(directory: URL) throws -> OCIIndex { let fm = FileManager.default let decoder = JSONDecoder() @@ -122,11 +122,11 @@ extension LocalOCILayoutClient { throw ContainerizationError(.notFound, message: indexFile.absolutePath()) } data = try Data(contentsOf: indexFile) - let index = try decoder.decode(ContainerizationOCI.Index.self, from: data) + let index = try decoder.decode(OCIIndex.self, from: data) return index } - package func createOCILayoutStructure(directory: URL, manifests: [Descriptor]) throws { + package func createOCILayoutStructure(directory: URL, manifests: [OCIDescriptor]) throws { let fm = FileManager.default let encoder = JSONEncoder() encoder.outputFormatting = [.withoutEscapingSlashes] @@ -142,7 +142,7 @@ extension LocalOCILayoutClient { guard fm.createFile(atPath: p, contents: data) else { throw ContainerizationError(.internalError, message: "failed to create file \(p)") } - let idx = ContainerizationOCI.Index(schemaVersion: 2, manifests: manifests) + let idx = OCIIndex(schemaVersion: 2, manifests: manifests) data = try encoder.encode(idx) p = directory.appendingPathComponent(Self.ociLayoutIndexFileName).absolutePath() guard fm.createFile(atPath: p, contents: data) else { @@ -150,15 +150,15 @@ extension LocalOCILayoutClient { } } - package func setImageReferenceAnnotation(descriptor: inout Descriptor, reference: String) { + package func setImageReferenceAnnotation(descriptor: inout OCIDescriptor, reference: String) { var annotations = descriptor.annotations ?? [:] - annotations[AnnotationKeys.containerizationImageName] = reference - annotations[AnnotationKeys.containerdImageName] = reference - annotations[AnnotationKeys.openContainersImageName] = reference + annotations[OCIAnnotationKeys.containerizationImageName] = reference + annotations[OCIAnnotationKeys.containerdImageName] = reference + annotations[OCIAnnotationKeys.openContainersImageName] = reference descriptor.annotations = annotations } - package func getImageReferencefromDescriptor(descriptor: Descriptor) -> String? { + package func getImageReferencefromDescriptor(descriptor: OCIDescriptor) -> String? { let annotations = descriptor.annotations guard let annotations else { return nil @@ -173,13 +173,13 @@ 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] { + if let name = annotations[OCIAnnotationKeys.containerizationImageName] { return name } - if let name = annotations[AnnotationKeys.containerdImageName] { + if let name = annotations[OCIAnnotationKeys.containerdImageName] { return name } - if let name = annotations[AnnotationKeys.openContainersImageName] { + if let name = annotations[OCIAnnotationKeys.openContainersImageName] { return name } return nil diff --git a/Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift b/Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift index 286c42cc..23315d14 100644 --- a/Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift +++ b/Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift @@ -28,7 +28,7 @@ import NIOFileSystem extension RegistryClient { /// Resolve sends a HEAD request to the registry to find root manifest descriptor. /// This descriptor serves as an entry point to retrieve resources from the registry. - public func resolve(name: String, tag: String) async throws -> Descriptor { + public func resolve(name: String, tag: String) async throws -> OCIDescriptor { var components = base // Make HEAD request to retrieve the digest header @@ -36,10 +36,10 @@ extension RegistryClient { // The client should include an Accept header indicating which manifest content types it supports. let mediaTypes = [ - MediaTypes.dockerManifest, - MediaTypes.dockerManifestList, - MediaTypes.imageManifest, - MediaTypes.index, + OCIMediaTypes.dockerManifest, + OCIMediaTypes.dockerManifestList, + OCIMediaTypes.imageManifest, + OCIMediaTypes.index, "*/*", ] @@ -70,19 +70,19 @@ extension RegistryClient { throw ContainerizationError(.invalidArgument, message: "Cannot convert \(sizeStr) to Int64") } - return Descriptor(mediaType: type, digest: digest, size: size) + return OCIDescriptor(mediaType: type, digest: digest, size: size) } } /// Fetch resource (either manifest or blob) to memory with JSON decoding. - public func fetch(name: String, descriptor: Descriptor) async throws -> T { + public func fetch(name: String, descriptor: OCIDescriptor) async throws -> T { var components = base let manifestTypes = [ - MediaTypes.dockerManifest, - MediaTypes.dockerManifestList, - MediaTypes.imageManifest, - MediaTypes.index, + OCIMediaTypes.dockerManifest, + OCIMediaTypes.dockerManifestList, + OCIMediaTypes.imageManifest, + OCIMediaTypes.index, ] let isManifest = manifestTypes.contains(where: { $0 == descriptor.mediaType }) @@ -103,14 +103,14 @@ extension RegistryClient { } /// Fetch resource (either manifest or blob) to memory as raw `Data`. - public func fetchData(name: String, descriptor: Descriptor) async throws -> Data { + public func fetchData(name: String, descriptor: OCIDescriptor) async throws -> Data { var components = base let manifestTypes = [ - MediaTypes.dockerManifest, - MediaTypes.dockerManifestList, - MediaTypes.imageManifest, - MediaTypes.index, + OCIMediaTypes.dockerManifest, + OCIMediaTypes.dockerManifestList, + OCIMediaTypes.imageManifest, + OCIMediaTypes.index, ] let isManifest = manifestTypes.contains(where: { $0 == descriptor.mediaType }) @@ -134,7 +134,7 @@ extension RegistryClient { /// This method is suitable for streaming data. public func fetchBlob( name: String, - descriptor: Descriptor, + descriptor: OCIDescriptor, closure: (Int64, HTTPClientResponse.Body) async throws -> Void ) async throws { var components = base @@ -167,7 +167,7 @@ extension RegistryClient { #if os(macOS) /// Fetch a blob from remote registry and write the contents into a file in the provided directory. - public func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) { + public func fetchBlob(name: String, descriptor: OCIDescriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) { var hasher = SHA256() var received: Int64 = 0 let fs = NIOFileSystem.FileSystem.shared @@ -203,7 +203,7 @@ extension RegistryClient { } #else /// Fetch a blob from remote registry and write the contents into a file in the provided directory. - public func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) { + public func fetchBlob(name: String, descriptor: OCIDescriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) { var hasher = SHA256() var received: Int64 = 0 guard FileManager.default.createFile(atPath: file.path, contents: nil) else { diff --git a/Sources/ContainerizationOCI/Client/RegistryClient+Push.swift b/Sources/ContainerizationOCI/Client/RegistryClient+Push.swift index 22717856..6e99671b 100644 --- a/Sources/ContainerizationOCI/Client/RegistryClient+Push.swift +++ b/Sources/ContainerizationOCI/Client/RegistryClient+Push.swift @@ -42,7 +42,7 @@ extension RegistryClient { public func push( name: String, ref tag: String, - descriptor: Descriptor, + descriptor: OCIDescriptor, streamGenerator: () throws -> T, progress: ProgressHandler? ) async throws where T.Element == ByteBuffer { @@ -57,7 +57,7 @@ extension RegistryClient { var existCheck: [String] = [] switch mediaType { - case MediaTypes.dockerManifest, MediaTypes.dockerManifestList, MediaTypes.imageManifest, MediaTypes.index: + case OCIMediaTypes.dockerManifest, OCIMediaTypes.dockerManifestList, OCIMediaTypes.imageManifest, OCIMediaTypes.index: isManifest = true existCheck = self.getManifestPath(tag: tag, digest: descriptor.digest) default: diff --git a/Sources/ContainerizationOCI/Content/Content.swift b/Sources/ContainerizationOCI/Content/Content.swift index ad63850d..5a69bc8c 100644 --- a/Sources/ContainerizationOCI/Content/Content.swift +++ b/Sources/ContainerizationOCI/Content/Content.swift @@ -42,16 +42,16 @@ public protocol Content: Sendable { /// Protocol defining methods to fetch and push OCI content public protocol ContentClient: Sendable { - func fetch(name: String, descriptor: Descriptor) async throws -> T + func fetch(name: String, descriptor: OCIDescriptor) async throws -> T - func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) + func fetchBlob(name: String, descriptor: OCIDescriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) - func fetchData(name: String, descriptor: Descriptor) async throws -> Data + func fetchData(name: String, descriptor: OCIDescriptor) async throws -> Data func push( name: String, ref: String, - descriptor: Descriptor, + descriptor: OCIDescriptor, streamGenerator: () throws -> T, progress: ProgressHandler? ) async throws where T.Element == ByteBuffer diff --git a/Sources/ContainerizationOCI/Descriptor.swift b/Sources/ContainerizationOCI/Descriptor.swift index 648dc12c..d8f5fc60 100644 --- a/Sources/ContainerizationOCI/Descriptor.swift +++ b/Sources/ContainerizationOCI/Descriptor.swift @@ -21,7 +21,7 @@ import Foundation /// Descriptor describes the disposition of targeted content. /// This structure provides `application/vnd.oci.descriptor.v1+json` mediatype /// when marshalled to JSON. -public struct Descriptor: Codable, Sendable, Equatable { +public struct OCIDescriptor: Codable, Sendable, Equatable { /// mediaType is the media type of the object this schema refers to. public let mediaType: String @@ -40,11 +40,11 @@ public struct Descriptor: Codable, Sendable, Equatable { /// platform describes the platform which the image in the manifest runs on. /// /// This should only be used when referring to a manifest. - public var platform: Platform? + public var platform: OCIPlatform? public init( mediaType: String, digest: String, size: Int64, urls: [String]? = nil, annotations: [String: String]? = nil, - platform: Platform? = nil + platform: OCIPlatform? = nil ) { self.mediaType = mediaType self.digest = digest diff --git a/Sources/ContainerizationOCI/ImageConfig.swift b/Sources/ContainerizationOCI/ImageConfig.swift index 77f5d431..5b12a568 100644 --- a/Sources/ContainerizationOCI/ImageConfig.swift +++ b/Sources/ContainerizationOCI/ImageConfig.swift @@ -19,7 +19,7 @@ import Foundation /// ImageConfig defines the execution parameters which should be used as a base when running a container using an image. -public struct ImageConfig: Codable, Sendable { +public struct OCIImageConfig: Codable, Sendable { enum CodingKeys: String, CodingKey { case user = "User" case env = "Env" @@ -66,7 +66,7 @@ public struct ImageConfig: Codable, Sendable { } /// RootFS describes a layer content addresses -public struct Rootfs: Codable, Sendable { +public struct OCIRootfs: Codable, Sendable { enum CodingKeys: String, CodingKey { case type case diffIDs = "diff_ids" @@ -85,7 +85,7 @@ public struct Rootfs: Codable, Sendable { } /// History describes the history of a layer. -public struct History: Codable, Sendable { +public struct OCIHistory: Codable, Sendable { enum CodingKeys: String, CodingKey { case created case createdBy = "created_by" @@ -123,7 +123,7 @@ public struct History: Codable, Sendable { /// Image is the JSON structure which describes some basic information about the image. /// This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON. -public struct Image: Codable, Sendable { +public struct OCIImage: Codable, Sendable { /// created is the combined date and time at which the image was created, formatted as defined by RFC 3339, section 5.6. public let created: String? @@ -146,18 +146,18 @@ public struct Image: Codable, Sendable { public let variant: String? /// config defines the execution parameters which should be used as a base when running a container using the image. - public let config: ImageConfig? + public let config: OCIImageConfig? /// rootfs references the layer content addresses used by the image. - public let rootfs: Rootfs + public let rootfs: OCIRootfs /// history describes the history of each layer. - public let history: [History]? + public let history: [OCIHistory]? public init( created: String? = nil, author: String? = nil, architecture: String, os: String, osVersion: String? = nil, - osFeatures: [String]? = nil, variant: String? = nil, config: ImageConfig? = nil, rootfs: Rootfs, - history: [History]? = nil + osFeatures: [String]? = nil, variant: String? = nil, config: OCIImageConfig? = nil, rootfs: OCIRootfs, + history: [OCIHistory]? = nil ) { self.created = created self.author = author diff --git a/Sources/ContainerizationOCI/Index.swift b/Sources/ContainerizationOCI/Index.swift index ce6d81e1..89a0ff7b 100644 --- a/Sources/ContainerizationOCI/Index.swift +++ b/Sources/ContainerizationOCI/Index.swift @@ -20,7 +20,7 @@ import Foundation /// Index references manifests for various platforms. /// This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON. -public struct Index: Codable, Sendable { +public struct OCIIndex: Codable, Sendable { /// schemaVersion is the image manifest schema that this image follows public let schemaVersion: Int @@ -28,13 +28,13 @@ public struct Index: Codable, Sendable { public let mediaType: String /// manifests references platform specific manifests. - public var manifests: [Descriptor] + public var manifests: [OCIDescriptor] /// annotations contains arbitrary metadata for the image index. public var annotations: [String: String]? public init( - schemaVersion: Int = 2, mediaType: String = MediaTypes.index, manifests: [Descriptor], + schemaVersion: Int = 2, mediaType: String = OCIMediaTypes.index, manifests: [OCIDescriptor], annotations: [String: String]? = nil ) { self.schemaVersion = schemaVersion diff --git a/Sources/ContainerizationOCI/Manifest.swift b/Sources/ContainerizationOCI/Manifest.swift index 890c834b..6d483665 100644 --- a/Sources/ContainerizationOCI/Manifest.swift +++ b/Sources/ContainerizationOCI/Manifest.swift @@ -19,7 +19,7 @@ import Foundation /// Manifest provides `application/vnd.oci.image.manifest.v1+json` mediatype structure when marshalled to JSON. -public struct Manifest: Codable, Sendable { +public struct OCIManifest: Codable, Sendable { /// `schemaVersion` is the image manifest schema that this image follows. public let schemaVersion: Int @@ -28,16 +28,16 @@ public struct Manifest: Codable, Sendable { /// `config` references a configuration object for a container, by digest. /// The referenced configuration object is a JSON blob that the runtime uses to set up the container. - public let config: Descriptor + public let config: OCIDescriptor /// `layers` is an indexed list of layers referenced by the manifest. - public let layers: [Descriptor] + public let layers: [OCIDescriptor] /// `annotations` contains arbitrary metadata for the image manifest. public let annotations: [String: String]? public init( - schemaVersion: Int = 2, mediaType: String = MediaTypes.imageManifest, config: Descriptor, layers: [Descriptor], + schemaVersion: Int = 2, mediaType: String = OCIMediaTypes.imageManifest, config: OCIDescriptor, layers: [OCIDescriptor], annotations: [String: String]? = nil ) { self.schemaVersion = schemaVersion diff --git a/Sources/ContainerizationOCI/MediaType.swift b/Sources/ContainerizationOCI/MediaType.swift index 059b7150..36143cdb 100644 --- a/Sources/ContainerizationOCI/MediaType.swift +++ b/Sources/ContainerizationOCI/MediaType.swift @@ -16,9 +16,9 @@ import Foundation -/// MediaTypes represent all supported OCI image content types for both metadata and layer formats. +/// OCIMediaTypes represent all supported OCI image content types for both metadata and layer formats. /// Follows all distributable media types in: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/mediatype.go -public struct MediaTypes: Codable, Sendable { +public struct OCIMediaTypes: Codable, Sendable { /// Specifies the media type for a content descriptor. public static let descriptor = "application/vnd.oci.descriptor.v1+json" diff --git a/Sources/ContainerizationOCI/Platform.swift b/Sources/ContainerizationOCI/Platform.swift index 90e8190d..9b9c021c 100644 --- a/Sources/ContainerizationOCI/Platform.swift +++ b/Sources/ContainerizationOCI/Platform.swift @@ -20,7 +20,7 @@ import ContainerizationError import Foundation /// Platform describes the platform which the image in the manifest runs on. -public struct Platform: Sendable, Equatable { +public struct OCIPlatform: Sendable, Equatable { public static var current: Self { var systemInfo = utsname() uname(&systemInfo) @@ -94,7 +94,7 @@ public struct Platform: Sendable, Equatable { /// - platform: A `string` value representing the platform. /// ```swift /// // Create a new `ImagePlatform` from string. - /// let platform = try Platform(from: "linux/amd64") + /// let platform = try OCIPlatformfrom: "linux/amd64") /// ``` /// ## Throws ## /// - Throws: `Error.missingOS` if input is empty @@ -193,7 +193,7 @@ public struct Platform: Sendable, Equatable { } -extension Platform: Hashable { +extension OCIPlatform: Hashable { /** `~=` compares two platforms to check if **lhs** platform images are compatible with **rhs** platform This operator can be used to check if an image of **lhs** platform can run on **rhs**: @@ -208,7 +208,7 @@ extension Platform: Hashable { - rhs: platform against which compatibility is being checked - Returns: `true | false` */ - public static func ~= (lhs: Platform, rhs: Platform) -> Bool { + public static func ~= (lhs: OCIPlatform, rhs: OCIPlatform) -> Bool { if lhs.os == rhs.os { if lhs._rawArch == rhs._rawArch { switch rhs._rawArch { @@ -256,7 +256,7 @@ extension Platform: Hashable { } /// `==` compares if **lhs** and **rhs** are the exact same platforms. - public static func == (lhs: Platform, rhs: Platform) -> Bool { + public static func == (lhs: OCIPlatform, rhs: OCIPlatform) -> Bool { // NOTE: // If the platform struct was created by setting the fields directly and not using (from: String) // then, there is a possibility that for arm64 architecture, the variant may be set to nil @@ -283,7 +283,7 @@ extension Platform: Hashable { } } -extension Platform: Codable { +extension OCIPlatform: Codable { enum CodingKeys: String, CodingKey { case os = "os" @@ -313,7 +313,7 @@ extension Platform: Codable { } } -public func createPlatformMatcher(for platform: Platform?) -> @Sendable (Platform) -> Bool { +public func createPlatformMatcher(for platform: OCIPlatform?) -> @Sendable (OCIPlatform) -> Bool { if let platform { return { other in platform == other @@ -324,8 +324,8 @@ public func createPlatformMatcher(for platform: Platform?) -> @Sendable (Platfor } } -public func filterPlatforms(matcher: (Platform) -> Bool, _ descriptors: [Descriptor]) throws -> [Descriptor] { - var outDescriptors: [Descriptor] = [] +public func filterPlatforms(matcher: (OCIPlatform) -> Bool, _ descriptors: [OCIDescriptor]) throws -> [OCIDescriptor] { + var outDescriptors: [OCIDescriptor] = [] for desc in descriptors { guard let p = desc.platform else { // pass along descriptor if the platform is not defined diff --git a/Sources/ContainerizationOCI/Spec.swift b/Sources/ContainerizationOCI/RuntimeSpec.swift similarity index 71% rename from Sources/ContainerizationOCI/Spec.swift rename to Sources/ContainerizationOCI/RuntimeSpec.swift index e033219e..c27ff13f 100644 --- a/Sources/ContainerizationOCI/Spec.swift +++ b/Sources/ContainerizationOCI/RuntimeSpec.swift @@ -18,26 +18,26 @@ /// have been left off, and some APIs for Linux aren't present. This was manually ported starting /// at the v1.2.0 release. -public struct Spec: Codable, Sendable { +public struct OCISpec: Codable, Sendable { public var version: String - public var hooks: Hook? - public var process: Process? + public var hooks: OCIHook? + public var process: OCIProcess? public var hostname, domainname: String - public var mounts: [Mount] + public var mounts: [OCIMount] public var annotations: [String: String]? - public var root: Root? - public var linux: Linux? + public var root: OCIRoot? + public var linux: OCILinux? public init( version: String = "", - hooks: Hook? = nil, - process: Process? = nil, + hooks: OCIHook? = nil, + process: OCIProcess? = nil, hostname: String = "", domainname: String = "", - mounts: [Mount] = [], + mounts: [OCIMount] = [], annotations: [String: String]? = nil, - root: Root? = nil, - linux: Linux? = nil + root: OCIRoot? = nil, + linux: OCILinux? = nil ) { self.version = version self.hooks = hooks @@ -63,18 +63,18 @@ public struct Spec: Codable, Sendable { } } -public struct Process: Codable, Sendable { +public struct OCIProcess: Codable, Sendable { public var cwd: String public var env: [String] - public var consoleSize: Box? + public var consoleSize: OCIBox? public var selinuxLabel: String public var noNewPrivileges: Bool public var commandLine: String public var oomScoreAdj: Int? - public var capabilities: LinuxCapabilities? + public var capabilities: OCILinuxCapabilities? public var apparmorProfile: String - public var user: User - public var rlimits: [POSIXRlimit] + public var user: OCIUser + public var rlimits: [OCIRlimit] public var args: [String] public var terminal: Bool @@ -82,15 +82,15 @@ public struct Process: Codable, Sendable { args: [String] = [], cwd: String = "/", env: [String] = [], - consoleSize: Box? = nil, + consoleSize: OCIBox? = nil, selinuxLabel: String = "", noNewPrivileges: Bool = false, commandLine: String = "", oomScoreAdj: Int? = nil, - capabilities: LinuxCapabilities? = nil, + capabilities: OCILinuxCapabilities? = nil, apparmorProfile: String = "", - user: User = .init(), - rlimits: [POSIXRlimit] = [], + user: OCIUser = .init(), + rlimits: [OCIRlimit] = [], terminal: Bool = false ) { self.cwd = cwd @@ -108,21 +108,21 @@ public struct Process: Codable, Sendable { self.terminal = terminal } - public init(from config: ImageConfig) { + public init(from config: OCIImageConfig) { let cwd = config.workingDir ?? "/" let env = config.env ?? [] let args = (config.entrypoint ?? []) + (config.cmd ?? []) - let user: User = { + let user: OCIUser = { if let rawString = config.user { - return User(username: rawString) + return OCIUser(username: rawString) } - return User() + return OCIUser() }() self.init(args: args, cwd: cwd, env: env, user: user) } } -public struct LinuxCapabilities: Codable, Sendable { +public struct OCILinuxCapabilities: Codable, Sendable { public var bounding: [String] public var effective: [String] public var inheritable: [String] @@ -144,7 +144,7 @@ public struct LinuxCapabilities: Codable, Sendable { } } -public struct Box: Codable, Sendable { +public struct OCIBox: Codable, Sendable { var height, width: UInt public init(height: UInt, width: UInt) { @@ -153,7 +153,7 @@ public struct Box: Codable, Sendable { } } -public struct User: Codable, Sendable { +public struct OCIUser: Codable, Sendable { public var uid: UInt32 public var gid: UInt32 public var umask: UInt32? @@ -175,7 +175,7 @@ public struct User: Codable, Sendable { } } -public struct Root: Codable, Sendable { +public struct OCIRoot: Codable, Sendable { public var path: String public var readonly: Bool @@ -185,22 +185,22 @@ public struct Root: Codable, Sendable { } } -public struct Mount: Codable, Sendable { +public struct OCIMount: Codable, Sendable { public var type: String public var source: String public var destination: String public var options: [String] - public var uidMappings: [LinuxIDMapping] - public var gidMappings: [LinuxIDMapping] + public var uidMappings: [OCILinuxIDMapping] + public var gidMappings: [OCILinuxIDMapping] public init( type: String, source: String, destination: String, options: [String] = [], - uidMappings: [LinuxIDMapping] = [], - gidMappings: [LinuxIDMapping] = [] + uidMappings: [OCILinuxIDMapping] = [], + gidMappings: [OCILinuxIDMapping] = [] ) { self.destination = destination self.type = type @@ -211,7 +211,7 @@ public struct Mount: Codable, Sendable { } } -public struct Hook: Codable, Sendable { +public struct OCIHook: Codable, Sendable { public var path: String public var args: [String] public var env: [String] @@ -225,21 +225,21 @@ public struct Hook: Codable, Sendable { } } -public struct Hooks: Codable, Sendable { - public var prestart: [Hook] - public var createRuntime: [Hook] - public var createContainer: [Hook] - public var startContainer: [Hook] - public var poststart: [Hook] - public var poststop: [Hook] +public struct OCIHooks: Codable, Sendable { + public var prestart: [OCIHook] + public var createRuntime: [OCIHook] + public var createContainer: [OCIHook] + public var startContainer: [OCIHook] + public var poststart: [OCIHook] + public var poststop: [OCIHook] public init( - prestart: [Hook], - createRuntime: [Hook], - createContainer: [Hook], - startContainer: [Hook], - poststart: [Hook], - poststop: [Hook] + prestart: [OCIHook], + createRuntime: [OCIHook], + createContainer: [OCIHook], + startContainer: [OCIHook], + poststart: [OCIHook], + poststop: [OCIHook] ) { self.prestart = prestart self.createRuntime = createRuntime @@ -250,35 +250,35 @@ public struct Hooks: Codable, Sendable { } } -public struct Linux: Codable, Sendable { - public var uidMappings: [LinuxIDMapping] - public var gidMappings: [LinuxIDMapping] +public struct OCILinux: Codable, Sendable { + public var uidMappings: [OCILinuxIDMapping] + public var gidMappings: [OCILinuxIDMapping] public var sysctl: [String: String]? - public var resources: LinuxResources? + public var resources: OCILinuxResources? public var cgroupsPath: String - public var namespaces: [LinuxNamespace] - public var devices: [LinuxDevice] - public var seccomp: LinuxSeccomp? + public var namespaces: [OCILinuxNamespace] + public var devices: [OCILinuxDevice] + public var seccomp: OCILinuxSeccomp? public var rootfsPropagation: String public var maskedPaths: [String] public var readonlyPaths: [String] public var mountLabel: String - public var personality: LinuxPersonality? + public var personality: OCILinuxPersonality? public init( - uidMappings: [LinuxIDMapping] = [], - gidMappings: [LinuxIDMapping] = [], + uidMappings: [OCILinuxIDMapping] = [], + gidMappings: [OCILinuxIDMapping] = [], sysctl: [String: String]? = nil, - resources: LinuxResources? = nil, + resources: OCILinuxResources? = nil, cgroupsPath: String = "", - namespaces: [LinuxNamespace] = [], - devices: [LinuxDevice] = [], - seccomp: LinuxSeccomp? = nil, + namespaces: [OCILinuxNamespace] = [], + devices: [OCILinuxDevice] = [], + seccomp: OCILinuxSeccomp? = nil, rootfsPropagation: String = "", maskedPaths: [String] = [], readonlyPaths: [String] = [], mountLabel: String = "", - personality: LinuxPersonality? = nil + personality: OCILinuxPersonality? = nil ) { self.uidMappings = uidMappings self.gidMappings = gidMappings @@ -296,17 +296,17 @@ public struct Linux: Codable, Sendable { } } -public struct LinuxNamespace: Codable, Sendable { - public var type: LinuxNamespaceType +public struct OCILinuxNamespace: Codable, Sendable { + public var type: OCILinuxNamespaceType public var path: String - public init(type: LinuxNamespaceType, path: String = "") { + public init(type: OCILinuxNamespaceType, path: String = "") { self.type = type self.path = path } } -public enum LinuxNamespaceType: String, Codable, Sendable { +public enum OCILinuxNamespaceType: String, Codable, Sendable { case pid case network case uts @@ -316,7 +316,7 @@ public enum LinuxNamespaceType: String, Codable, Sendable { case cgroup } -public struct LinuxIDMapping: Codable, Sendable { +public struct OCILinuxIDMapping: Codable, Sendable { public var containerID: UInt32 public var hostID: UInt32 public var size: UInt32 @@ -328,7 +328,7 @@ public struct LinuxIDMapping: Codable, Sendable { } } -public struct POSIXRlimit: Codable, Sendable { +public struct OCIRlimit: Codable, Sendable { public var type: String public var hard: UInt64 public var soft: UInt64 @@ -340,7 +340,7 @@ public struct POSIXRlimit: Codable, Sendable { } } -public struct LinuxHugepageLimit: Codable, Sendable { +public struct OCILinuxHugepageLimit: Codable, Sendable { public var pagesize: String public var limit: UInt64 @@ -350,7 +350,7 @@ public struct LinuxHugepageLimit: Codable, Sendable { } } -public struct LinuxInterfacePriority: Codable, Sendable { +public struct OCILinuxInterfacePriority: Codable, Sendable { public var name: String public var priority: UInt32 @@ -360,7 +360,7 @@ public struct LinuxInterfacePriority: Codable, Sendable { } } -public struct LinuxBlockIODevice: Codable, Sendable { +public struct OCILinuxBlockIODevice: Codable, Sendable { public var major: Int64 public var minor: Int64 @@ -370,7 +370,7 @@ public struct LinuxBlockIODevice: Codable, Sendable { } } -public struct LinuxWeightDevice: Codable, Sendable { +public struct OCILinuxWeightDevice: Codable, Sendable { public var major: Int64 public var minor: Int64 public var weight: UInt16? @@ -384,7 +384,7 @@ public struct LinuxWeightDevice: Codable, Sendable { } } -public struct LinuxThrottleDevice: Codable, Sendable { +public struct OCILinuxThrottleDevice: Codable, Sendable { public var major: Int64 public var minor: Int64 public var rate: UInt64 @@ -396,23 +396,23 @@ public struct LinuxThrottleDevice: Codable, Sendable { } } -public struct LinuxBlockIO: Codable, Sendable { +public struct OCILinuxBlockIO: Codable, Sendable { public var weight: UInt16? public var leafWeight: UInt16? - public var weightDevice: [LinuxWeightDevice] - public var throttleReadBpsDevice: [LinuxThrottleDevice] - public var throttleWriteBpsDevice: [LinuxThrottleDevice] - public var throttleReadIOPSDevice: [LinuxThrottleDevice] - public var throttleWriteIOPSDevice: [LinuxThrottleDevice] + public var weightDevice: [OCILinuxWeightDevice] + public var throttleReadBpsDevice: [OCILinuxThrottleDevice] + public var throttleWriteBpsDevice: [OCILinuxThrottleDevice] + public var throttleReadIOPSDevice: [OCILinuxThrottleDevice] + public var throttleWriteIOPSDevice: [OCILinuxThrottleDevice] public init( weight: UInt16?, leafWeight: UInt16?, - weightDevice: [LinuxWeightDevice], - throttleReadBpsDevice: [LinuxThrottleDevice], - throttleWriteBpsDevice: [LinuxThrottleDevice], - throttleReadIOPSDevice: [LinuxThrottleDevice], - throttleWriteIOPSDevice: [LinuxThrottleDevice] + weightDevice: [OCILinuxWeightDevice], + throttleReadBpsDevice: [OCILinuxThrottleDevice], + throttleWriteBpsDevice: [OCILinuxThrottleDevice], + throttleReadIOPSDevice: [OCILinuxThrottleDevice], + throttleWriteIOPSDevice: [OCILinuxThrottleDevice] ) { self.weight = weight self.leafWeight = leafWeight @@ -424,7 +424,7 @@ public struct LinuxBlockIO: Codable, Sendable { } } -public struct LinuxMemory: Codable, Sendable { +public struct OCILinuxMemory: Codable, Sendable { public var limit: Int64? public var reservation: Int64? public var swap: Int64? @@ -458,7 +458,7 @@ public struct LinuxMemory: Codable, Sendable { } } -public struct LinuxCPU: Codable, Sendable { +public struct OCILinuxCPU: Codable, Sendable { public var shares: UInt64? public var quota: Int64? public var burst: UInt64? @@ -492,7 +492,7 @@ public struct LinuxCPU: Codable, Sendable { } } -public struct LinuxPids: Codable, Sendable { +public struct OCILinuxPids: Codable, Sendable { public var limit: Int64 public init(limit: Int64) { @@ -500,17 +500,17 @@ public struct LinuxPids: Codable, Sendable { } } -public struct LinuxNetwork: Codable, Sendable { +public struct OCILinuxNetwork: Codable, Sendable { public var classID: UInt32? - public var priorities: [LinuxInterfacePriority] + public var priorities: [OCILinuxInterfacePriority] - public init(classID: UInt32?, priorities: [LinuxInterfacePriority]) { + public init(classID: UInt32?, priorities: [OCILinuxInterfacePriority]) { self.classID = classID self.priorities = priorities } } -public struct LinuxRdma: Codable, Sendable { +public struct OCILinuxRdma: Codable, Sendable { public var hcsHandles: UInt32? public var hcaObjects: UInt32? @@ -520,26 +520,26 @@ public struct LinuxRdma: Codable, Sendable { } } -public struct LinuxResources: Codable, Sendable { - public var devices: [LinuxDeviceCgroup] - public var memory: LinuxMemory? - public var cpu: LinuxCPU? - public var pids: LinuxPids? - public var blockIO: LinuxBlockIO? - public var hugepageLimits: [LinuxHugepageLimit] - public var network: LinuxNetwork? - public var rdma: [String: LinuxRdma]? +public struct OCILinuxResources: Codable, Sendable { + public var devices: [OCILinuxDeviceCgroup] + public var memory: OCILinuxMemory? + public var cpu: OCILinuxCPU? + public var pids: OCILinuxPids? + public var blockIO: OCILinuxBlockIO? + public var hugepageLimits: [OCILinuxHugepageLimit] + public var network: OCILinuxNetwork? + public var rdma: [String: OCILinuxRdma]? public var unified: [String: String]? public init( - devices: [LinuxDeviceCgroup] = [], - memory: LinuxMemory? = nil, - cpu: LinuxCPU? = nil, - pids: LinuxPids? = nil, - blockIO: LinuxBlockIO? = nil, - hugepageLimits: [LinuxHugepageLimit] = [], - network: LinuxNetwork? = nil, - rdma: [String: LinuxRdma]? = nil, + devices: [OCILinuxDeviceCgroup] = [], + memory: OCILinuxMemory? = nil, + cpu: OCILinuxCPU? = nil, + pids: OCILinuxPids? = nil, + blockIO: OCILinuxBlockIO? = nil, + hugepageLimits: [OCILinuxHugepageLimit] = [], + network: OCILinuxNetwork? = nil, + rdma: [String: OCILinuxRdma]? = nil, unified: [String: String] = [:] ) { self.devices = devices @@ -554,7 +554,7 @@ public struct LinuxResources: Codable, Sendable { } } -public struct LinuxDevice: Codable, Sendable { +public struct OCILinuxDevice: Codable, Sendable { public var path: String public var type: String public var major: Int64 @@ -582,7 +582,7 @@ public struct LinuxDevice: Codable, Sendable { } } -public struct LinuxDeviceCgroup: Codable, Sendable { +public struct OCILinuxDeviceCgroup: Codable, Sendable { public var allow: Bool public var type: String public var major: Int64? @@ -598,38 +598,38 @@ public struct LinuxDeviceCgroup: Codable, Sendable { } } -public enum LinuxPersonalityDomain: String, Codable, Sendable { +public enum OCILinuxPersonalityDomain: String, Codable, Sendable { case perLinux = "LINUX" case perLinux32 = "LINUX32" } -public struct LinuxPersonality: Codable, Sendable { - public var domain: LinuxPersonalityDomain +public struct OCILinuxPersonality: Codable, Sendable { + public var domain: OCILinuxPersonalityDomain public var flags: [String] - public init(domain: LinuxPersonalityDomain, flags: [String]) { + public init(domain: OCILinuxPersonalityDomain, flags: [String]) { self.domain = domain self.flags = flags } } -public struct LinuxSeccomp: Codable, Sendable { - public var defaultAction: LinuxSeccompAction +public struct OCILinuxSeccomp: Codable, Sendable { + public var defaultAction: OCILinuxSeccompAction public var defaultErrnoRet: UInt? - public var architectures: [Arch] - public var flags: [LinuxSeccompFlag] + public var architectures: [OCIArch] + public var flags: [OCILinuxSeccompFlag] public var listenerPath: String public var listenerMetadata: String - public var syscalls: [LinuxSyscall] + public var syscalls: [OCILinuxSyscall] public init( - defaultAction: LinuxSeccompAction, + defaultAction: OCILinuxSeccompAction, defaultErrnoRet: UInt?, - architectures: [Arch], - flags: [LinuxSeccompFlag], + architectures: [OCIArch], + flags: [OCILinuxSeccompFlag], listenerPath: String, listenerMetadata: String, - syscalls: [LinuxSyscall] + syscalls: [OCILinuxSyscall] ) { self.defaultAction = defaultAction self.defaultErrnoRet = defaultErrnoRet @@ -641,13 +641,13 @@ public struct LinuxSeccomp: Codable, Sendable { } } -public enum LinuxSeccompFlag: String, Codable, Sendable { +public enum OCILinuxSeccompFlag: String, Codable, Sendable { case flagLog = "SECCOMP_FILTER_FLAG_LOG" case flagSpecAllow = "SECCOMP_FILTER_FLAG_SPEC_ALLOW" case flagWaitKillableRecv = "SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV" } -public enum Arch: String, Codable, Sendable { +public enum OCIArch: String, Codable, Sendable { case archX86 = "SCMP_ARCH_X86" case archX86_64 = "SCMP_ARCH_X86_64" case archX32 = "SCMP_ARCH_X32" @@ -669,7 +669,7 @@ public enum Arch: String, Codable, Sendable { case archRISCV64 = "SCMP_ARCH_RISCV64" } -public enum LinuxSeccompAction: String, Codable, Sendable { +public enum OCILinuxSeccompAction: String, Codable, Sendable { case actKill = "SCMP_ACT_KILL" case actKillProcess = "SCMP_ACT_KILL_PROCESS" case actKillThread = "SCMP_ACT_KILL_THREAD" @@ -681,7 +681,7 @@ public enum LinuxSeccompAction: String, Codable, Sendable { case actNotify = "SCMP_ACT_NOTIFY" } -public enum LinuxSeccompOperator: String, Codable, Sendable { +public enum OCILinuxSeccompOperator: String, Codable, Sendable { case opNotEqual = "SCMP_CMP_NE" case opLessThan = "SCMP_CMP_LT" case opLessEqual = "SCMP_CMP_LE" @@ -691,13 +691,13 @@ public enum LinuxSeccompOperator: String, Codable, Sendable { case opMaskedEqual = "SCMP_CMP_MASKED_EQ" } -public struct LinuxSeccompArg: Codable, Sendable { +public struct OCILinuxSeccompArg: Codable, Sendable { public var index: UInt public var value: UInt64 public var valueTwo: UInt64 - public var op: LinuxSeccompOperator + public var op: OCILinuxSeccompOperator - public init(index: UInt, value: UInt64, valueTwo: UInt64, op: LinuxSeccompOperator) { + public init(index: UInt, value: UInt64, valueTwo: UInt64, op: OCILinuxSeccompOperator) { self.index = index self.value = value self.valueTwo = valueTwo @@ -705,17 +705,17 @@ public struct LinuxSeccompArg: Codable, Sendable { } } -public struct LinuxSyscall: Codable, Sendable { +public struct OCILinuxSyscall: Codable, Sendable { public var names: [String] - public var action: LinuxSeccompAction + public var action: OCILinuxSeccompAction public var errnoRet: UInt? - public var args: [LinuxSeccompArg] + public var args: [OCILinuxSeccompArg] public init( names: [String], - action: LinuxSeccompAction, + action: OCILinuxSeccompAction, errnoRet: UInt?, - args: [LinuxSeccompArg] + args: [OCILinuxSeccompArg] ) { self.names = names self.action = action diff --git a/Sources/ContainerizationOCI/State.swift b/Sources/ContainerizationOCI/State.swift index 43042fac..33ef1a52 100644 --- a/Sources/ContainerizationOCI/State.swift +++ b/Sources/ContainerizationOCI/State.swift @@ -14,18 +14,18 @@ // limitations under the License. //===----------------------------------------------------------------------===// -public enum ContainerState: String, Codable, Sendable { +public enum OCIContainerState: String, Codable, Sendable { case creating case created case running case stopped } -public struct State: Codable, Sendable { +public struct OCIState: Codable, Sendable { public init( version: String, id: String, - status: ContainerState, + status: OCIContainerState, pid: Int, bundle: String, annotations: [String: String]? @@ -38,7 +38,7 @@ public struct State: Codable, Sendable { self.annotations = annotations } - public init(instance: State) { + public init(instance: OCIState) { self.ociVersion = instance.ociVersion self.id = instance.id self.status = instance.status @@ -49,7 +49,7 @@ public struct State: Codable, Sendable { public let ociVersion: String public let id: String - public let status: ContainerState + public let status: OCIContainerState public let pid: Int public let bundle: String public var annotations: [String: String]? @@ -57,8 +57,8 @@ public struct State: Codable, Sendable { public let seccompFdName: String = "seccompFd" -public struct ContainerProcessState: Codable, Sendable { - public init(version: String, fds: [String], pid: Int, metadata: String, state: State) { +public struct OCIContainerProcessState: Codable, Sendable { + public init(version: String, fds: [String], pid: Int, metadata: String, state: OCIState) { self.ociVersion = version self.fds = fds self.pid = pid @@ -66,7 +66,7 @@ public struct ContainerProcessState: Codable, Sendable { self.state = state } - public init(instance: ContainerProcessState) { + public init(instance: OCIContainerProcessState) { self.ociVersion = instance.ociVersion self.fds = instance.fds self.pid = instance.pid @@ -78,5 +78,5 @@ public struct ContainerProcessState: Codable, Sendable { public var fds: [String] public let pid: Int public let metadata: String - public let state: State + public let state: OCIState } diff --git a/Sources/ContainerizationOCI/Version.swift b/Sources/ContainerizationOCI/Version.swift index a24659ed..eaa3b722 100644 --- a/Sources/ContainerizationOCI/Version.swift +++ b/Sources/ContainerizationOCI/Version.swift @@ -14,11 +14,11 @@ // limitations under the License. //===----------------------------------------------------------------------===// -public struct RuntimeSpecVersion: Sendable { +public struct OCIRuntimeSpecVersion: Sendable { public let major, minor, patch: Int public let dev: String - public static let current = RuntimeSpecVersion( + public static let current = OCIRuntimeSpecVersion( major: 1, minor: 0, patch: 2, diff --git a/Sources/Integration/ProcessTests.swift b/Sources/Integration/ProcessTests.swift index a1f291ff..383d214f 100644 --- a/Sources/Integration/ProcessTests.swift +++ b/Sources/Integration/ProcessTests.swift @@ -134,7 +134,7 @@ extension IntegrationSuite { try await container.create() try await container.start() - let execConfig = ContainerizationOCI.Process( + let execConfig = OCIProcess( args: ["/bin/true"], env: ["PATH=\(LinuxContainer.defaultPath)"] ) @@ -185,7 +185,7 @@ extension IntegrationSuite { try await container.create() try await container.start() - let baseExecConfig = ContainerizationOCI.Process( + let baseExecConfig = OCIProcess( args: ["sh", "-c", "dd if=/dev/random of=/tmp/bytes bs=1M count=20 status=none ; sha256sum /tmp/bytes"], env: ["PATH=\(LinuxContainer.defaultPath)"] ) @@ -203,7 +203,7 @@ extension IntegrationSuite { let output = String(data: buffer.data, encoding: .utf8)! let expected = String(output.split(separator: " ").first!) try await withThrowingTaskGroup(of: Void.self) { group in - let execConfig = ContainerizationOCI.Process( + let execConfig = OCIProcess( args: ["cat", "/tmp/bytes"], env: ["PATH=\(LinuxContainer.defaultPath)"] ) diff --git a/Sources/Integration/Suite.swift b/Sources/Integration/Suite.swift index 21d545e0..0ad186b3 100644 --- a/Sources/Integration/Suite.swift +++ b/Sources/Integration/Suite.swift @@ -120,7 +120,7 @@ struct IntegrationSuite: AsyncParsableCommand { var testKernel = Kernel(path: .init(filePath: kernel), platform: .linuxArm) testKernel.commandLine.addDebug() let image = try await Self.fetchImage(reference: reference, store: store) - let platform = Platform(arch: "arm64", os: "linux", variant: "v8") + let platform = OCIPlatform(arch: "arm64", os: "linux", variant: "v8") let fs: Containerization.Mount = try await { let fsPath = Self.testDir.appending(component: "rootfs.ext4") diff --git a/Sources/cctl/ImageCommand.swift b/Sources/cctl/ImageCommand.swift index b164974f..5e08ed64 100644 --- a/Sources/cctl/ImageCommand.swift +++ b/Sources/cctl/ImageCommand.swift @@ -85,7 +85,7 @@ extension Application { struct ImageDisplay: Codable { let reference: String - let index: Index + let index: OCIIndex } struct Pull: AsyncParsableCommand { @@ -109,9 +109,9 @@ extension Application { func run() async throws { let imageStore = Application.imageStore - let platform: Platform? = try { + let platform: OCIPlatform? = try { if let platformString { - return try Platform(from: platformString) + return try OCIPlatform(from: platformString) } return nil }() @@ -177,9 +177,9 @@ extension Application { func run() async throws { let imageStore = Application.imageStore - let platform: Platform? = try { + let platform: OCIPlatform? = try { if let platformString { - return try Platform(from: platformString) + return try OCIPlatform(from: platformString) } return nil }() @@ -212,9 +212,9 @@ extension Application { @Argument var reference: [String] func run() async throws { - var p: Platform? = nil + var p: OCIPlatform? = nil if let platform { - p = try Platform(from: platform) + p = try OCIPlatform(from: platform) } let store = Application.imageStore let tempDir = FileManager.default.uniqueTemporaryDirectory() diff --git a/Sources/cctl/RootfsCommand.swift b/Sources/cctl/RootfsCommand.swift index 2445741e..b69ca843 100644 --- a/Sources/cctl/RootfsCommand.swift +++ b/Sources/cctl/RootfsCommand.swift @@ -41,7 +41,7 @@ extension Application { var vmexec: String @Option(name: .long, help: "Platform of the built binaries being packaged into the block") - var platformString: String = Platform.current.description + var platformString: String = OCIPlatform.current.description @Option(name: .long, help: "Labels to add to the built image of the form =, [=,...]") var labels: [String] = [] @@ -64,7 +64,7 @@ extension Application { func run() async throws { try await writeArchive() - let p = try Platform(from: platformString) + let p = try OCIPlatform(from: platformString) let rootfs = URL(filePath: rootfsPath) let labels = Application.parseKeyValuePairs(from: labels) _ = try await InitImage.create( diff --git a/Sources/cctl/cctl+Utils.swift b/Sources/cctl/cctl+Utils.swift index bee8937f..7e747724 100644 --- a/Sources/cctl/cctl+Utils.swift +++ b/Sources/cctl/cctl+Utils.swift @@ -46,8 +46,8 @@ extension Application { } } -extension ContainerizationOCI.Platform { - static var arm64: ContainerizationOCI.Platform { +extension OCIPlatform { + static var arm64: OCIPlatform { .init(arch: "arm64", os: "linux", variant: "v8") } } diff --git a/Tests/ContainerizationOCITests/OCIImageTests.swift b/Tests/ContainerizationOCITests/OCIImageTests.swift index 66f8ddf8..e5f0a9d5 100644 --- a/Tests/ContainerizationOCITests/OCIImageTests.swift +++ b/Tests/ContainerizationOCITests/OCIImageTests.swift @@ -22,43 +22,43 @@ import Testing struct OCITests { @Test func config() { - let config = ContainerizationOCI.ImageConfig() - let rootfs = ContainerizationOCI.Rootfs(type: "foo", diffIDs: ["diff1", "diff2"]) - let history = ContainerizationOCI.History() + let config = OCIImageConfig() + let rootfs = OCIRootfs(type: "foo", diffIDs: ["diff1", "diff2"]) + let history = OCIHistory() - let image = ContainerizationOCI.Image(architecture: "arm64", os: "linux", config: config, rootfs: rootfs, history: [history]) + let image = OCIImage(architecture: "arm64", os: "linux", config: config, rootfs: rootfs, history: [history]) #expect(image.rootfs.type == "foo") } @Test func descriptor() { - let platform = ContainerizationOCI.Platform(arch: "arm64", os: "linux") - let descriptor = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: "123", size: 0, platform: platform) + let platform = OCIPlatform(arch: "arm64", os: "linux") + let descriptor = OCIDescriptor(mediaType: OCIMediaTypes.descriptor, digest: "123", size: 0, platform: platform) #expect(descriptor.platform?.architecture == "arm64") #expect(descriptor.platform?.os == "linux") } @Test func index() { - var descriptors: [ContainerizationOCI.Descriptor] = [] + var descriptors: [OCIDescriptor] = [] for i in 0..<5 { - let descriptor = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: "\(i)", size: Int64(i)) + let descriptor = OCIDescriptor(mediaType: OCIMediaTypes.descriptor, digest: "\(i)", size: Int64(i)) descriptors.append(descriptor) } - let index = ContainerizationOCI.Index(schemaVersion: 1, manifests: descriptors) + let index = OCIIndex(schemaVersion: 1, manifests: descriptors) #expect(index.manifests.count == 5) } @Test func manifests() { - var descriptors: [ContainerizationOCI.Descriptor] = [] + var descriptors: [OCIDescriptor] = [] for i in 0..<5 { - let descriptor = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: "\(i)", size: Int64(i)) + let descriptor = OCIDescriptor(mediaType: OCIMediaTypes.descriptor, digest: "\(i)", size: Int64(i)) descriptors.append(descriptor) } - let config = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: "123", size: 0) + let config = OCIDescriptor(mediaType: OCIMediaTypes.descriptor, digest: "123", size: 0) - let manifest = ContainerizationOCI.Manifest(schemaVersion: 1, config: config, layers: descriptors) + let manifest = OCIManifest(schemaVersion: 1, config: config, layers: descriptors) #expect(manifest.config.digest == "123") #expect(manifest.layers.count == 5) } diff --git a/Tests/ContainerizationOCITests/OCIPlatformTests.swift b/Tests/ContainerizationOCITests/OCIPlatformTests.swift index f8021164..64da6064 100644 --- a/Tests/ContainerizationOCITests/OCIPlatformTests.swift +++ b/Tests/ContainerizationOCITests/OCIPlatformTests.swift @@ -22,48 +22,48 @@ import Testing struct OCIPlatformTests { @Test func identicalPlatforms() { - let amd64lhs = Platform(arch: "amd64", os: "linux") - let amd64rhs = Platform(arch: "amd64", os: "linux") + let amd64lhs = OCIPlatform(arch: "amd64", os: "linux") + let amd64rhs = OCIPlatform(arch: "amd64", os: "linux") #expect(amd64lhs == amd64rhs, "amd64 platforms should be equal") - let arm64lhs = Platform(arch: "arm64", os: "linux") - let arm64rhs = Platform(arch: "arm64", os: "linux") + let arm64lhs = OCIPlatform(arch: "arm64", os: "linux") + let arm64rhs = OCIPlatform(arch: "arm64", os: "linux") #expect(arm64lhs == arm64rhs, "arm64 platforms should be equal") } @Test func differentOS() { - let lhs = Platform(arch: "arm64", os: "linux") - let rhs = Platform(arch: "arm64", os: "darwin") + let lhs = OCIPlatform(arch: "arm64", os: "linux") + let rhs = OCIPlatform(arch: "arm64", os: "darwin") #expect(lhs != rhs, "Different OS should not be equal") } @Test func differentArch() { - let lhs = Platform(arch: "amd64", os: "linux") - let rhs = Platform(arch: "arm64", os: "linux") + let lhs = OCIPlatform(arch: "amd64", os: "linux") + let rhs = OCIPlatform(arch: "arm64", os: "linux") #expect(lhs != rhs, "Different arch should not be equal") } @Test func arm64_sameVariant() { - let lhs = Platform(arch: "arm64", os: "linux", variant: "v8") - let rhs = Platform(arch: "arm64", os: "linux", variant: "v8") + let lhs = OCIPlatform(arch: "arm64", os: "linux", variant: "v8") + let rhs = OCIPlatform(arch: "arm64", os: "linux", variant: "v8") #expect(lhs == rhs, "Both OS arm64, same arch, same variant => equal") } @Test func arm64_nilAndV8() { - let lhs = Platform(arch: "arm64", os: "linux", variant: nil) - let rhs = Platform(arch: "arm64", os: "linux", variant: "v8") + let lhs = OCIPlatform(arch: "arm64", os: "linux", variant: nil) + let rhs = OCIPlatform(arch: "arm64", os: "linux", variant: "v8") #expect(lhs == rhs, "One variant nil and other v8 => equal under special arm64 rule") } @Test func arm64_nilAndV7() { - let lhs = Platform(arch: "arm64", os: "linux", variant: nil) - let rhs = Platform(arch: "arm64", os: "linux", variant: "v7") + let lhs = OCIPlatform(arch: "arm64", os: "linux", variant: nil) + let rhs = OCIPlatform(arch: "arm64", os: "linux", variant: "v7") #expect(lhs != rhs, "nil vs v7 is not covered by the special rule => not equal") } @Test func arm64_bothNil() { - let lhs = Platform(arch: "arm64", os: "linux", variant: nil) - let rhs = Platform(arch: "arm64", os: "linux", variant: nil) + let lhs = OCIPlatform(arch: "arm64", os: "linux", variant: nil) + let rhs = OCIPlatform(arch: "arm64", os: "linux", variant: nil) #expect(lhs == rhs, "Both nil variants => variantEqual is true => overall equal") } } diff --git a/Tests/ContainerizationOCITests/RegistryClientTests.swift b/Tests/ContainerizationOCITests/RegistryClientTests.swift index c0567157..3f9398e6 100644 --- a/Tests/ContainerizationOCITests/RegistryClientTests.swift +++ b/Tests/ContainerizationOCITests/RegistryClientTests.swift @@ -103,7 +103,7 @@ struct OCIClientTests: ~Copyable { @Test func resolve() async throws { let client = RegistryClient(host: "ghcr.io") let descriptor = try await client.resolve(name: "apple/containerization/dockermanifestimage", tag: "0.0.2") - #expect(descriptor.mediaType == MediaTypes.dockerManifest) + #expect(descriptor.mediaType == OCIMediaTypes.dockerManifest) #expect(descriptor.size != 0) #expect(!descriptor.digest.isEmpty) } @@ -114,7 +114,7 @@ struct OCIClientTests: ~Copyable { name: "apple/containerization/dockermanifestimage", tag: "sha256:c8d344d228b7d9a702a95227438ec0d71f953a9a483e28ffabc5704f70d2b61e") let namedDescriptor = try await client.resolve(name: "apple/containerization/dockermanifestimage", tag: "0.0.2") #expect(descriptor == namedDescriptor) - #expect(descriptor.mediaType == MediaTypes.dockerManifest) + #expect(descriptor.mediaType == OCIMediaTypes.dockerManifest) #expect(descriptor.size != 0) #expect(!descriptor.digest.isEmpty) } @@ -122,7 +122,7 @@ struct OCIClientTests: ~Copyable { @Test func fetchManifest() async throws { let client = RegistryClient(host: "ghcr.io") let descriptor = try await client.resolve(name: "apple/containerization/dockermanifestimage", tag: "0.0.2") - let manifest: Manifest = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: descriptor) + let manifest: OCIManifest = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: descriptor) #expect(manifest.schemaVersion == 2) #expect(manifest.layers.count == 1) } @@ -138,8 +138,8 @@ struct OCIClientTests: ~Copyable { @Test func fetchConfig() async throws { let client = RegistryClient(host: "ghcr.io") let descriptor = try await client.resolve(name: "apple/containerization/dockermanifestimage", tag: "0.0.2") - let manifest: Manifest = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: descriptor) - let image: Image = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: manifest.config) + let manifest: OCIManifest = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: descriptor) + let image: OCIImage = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: manifest.config) // This is an empty image -- check that the image label is present in the image config #expect(image.config?.labels?["org.opencontainers.image.source"] == "https://github.com/apple/containerization") #expect(image.rootfs.diffIDs.count == 1) @@ -148,7 +148,7 @@ struct OCIClientTests: ~Copyable { @Test func fetchBlob() async throws { let client = RegistryClient(host: "ghcr.io") let descriptor = try await client.resolve(name: "apple/containerization/dockermanifestimage", tag: "0.0.2") - let manifest: Manifest = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: descriptor) + let manifest: OCIManifest = try await client.fetch(name: "apple/containerization/dockermanifestimage", descriptor: descriptor) var called = false var done = false try await client.fetchBlob(name: "apple/containerization/dockermanifestimage", descriptor: manifest.layers.first!) { (expected, body) in @@ -170,11 +170,11 @@ struct OCIClientTests: ~Copyable { func pushIndex() async throws { let client = RegistryClient(host: "ghcr.io", authentication: Self.authentication) let indexDescriptor = try await client.resolve(name: "apple/containerization/emptyimage", tag: "0.0.1") - let index: Index = try await client.fetch(name: "apple/containerization/emptyimage", descriptor: indexDescriptor) + let index: OCIIndex = try await client.fetch(name: "apple/containerization/emptyimage", descriptor: indexDescriptor) - let platform = Platform(arch: "amd64", os: "linux") + let platform = OCIPlatform(arch: "amd64", os: "linux") - var manifestDescriptor: Descriptor? + var manifestDescriptor: OCIDescriptor? for m in index.manifests where m.platform == platform { manifestDescriptor = m break @@ -182,8 +182,8 @@ struct OCIClientTests: ~Copyable { #expect(manifestDescriptor != nil) - let manifest: Manifest = try await client.fetch(name: "apple/containerization/emptyimage", descriptor: manifestDescriptor!) - let imgConfig: Image = try await client.fetch(name: "apple/containerization/emptyimage", descriptor: manifest.config) + let manifest: OCIManifest = try await client.fetch(name: "apple/containerization/emptyimage", descriptor: manifestDescriptor!) + let imgConfig: OCIImage = try await client.fetch(name: "apple/containerization/emptyimage", descriptor: manifest.config) let layer = try #require(manifest.layers.first) let blobPath = contentPath.appendingPathComponent(layer.digest) @@ -227,7 +227,7 @@ struct OCIClientTests: ~Copyable { } // Push the image configuration. - var imgConfigDesc: Descriptor? + var imgConfigDesc: OCIDescriptor? do { imgConfigDesc = try await self.pushDescriptor( client: client, @@ -244,7 +244,7 @@ struct OCIClientTests: ~Copyable { } // Push the image manifest. - let newManifest = Manifest( + let newManifest = OCIManifest( schemaVersion: manifest.schemaVersion, mediaType: manifest.mediaType!, config: imgConfigDesc!, @@ -260,7 +260,7 @@ struct OCIClientTests: ~Copyable { ) // Push the index. - let newIndex = Index( + let newIndex = OCIIndex( schemaVersion: index.schemaVersion, mediaType: index.mediaType, manifests: [manifestDesc], @@ -320,11 +320,11 @@ struct OCIClientTests: ~Copyable { name: String, ref: String, content: T, - baseDescriptor: Descriptor - ) async throws -> Descriptor { + baseDescriptor: OCIDescriptor + ) async throws -> OCIDescriptor { let encoded = try self.encoder.encode(content) let digest = SHA256.hash(data: encoded) - let descriptor = Descriptor( + let descriptor = OCIDescriptor( mediaType: baseDescriptor.mediaType, digest: digest.digest, size: Int64(encoded.count), diff --git a/Tests/ContainerizationTests/ImageTests/ImageStoreImagePullTests.swift b/Tests/ContainerizationTests/ImageTests/ImageStoreImagePullTests.swift index 045bdd8f..e3b15c89 100644 --- a/Tests/ContainerizationTests/ImageTests/ImageStoreImagePullTests.swift +++ b/Tests/ContainerizationTests/ImageTests/ImageStoreImagePullTests.swift @@ -47,15 +47,15 @@ final class ImageStoreImagePullTests { let img = try await self.store.pull(reference: "ghcr.io/apple/containerization/dockermanifestimage:0.0.2") let rootDescriptor = img.descriptor - let index: ContainerizationOCI.Index = try await contentStore.get(digest: rootDescriptor.digest)! + let index: OCIIndex = try await contentStore.get(digest: rootDescriptor.digest)! #expect(index.manifests.count == 1) let desc = index.manifests.first! #expect(desc.platform!.architecture == "amd64") await #expect(throws: Never.self) { - let manifest: ContainerizationOCI.Manifest = try await self.contentStore.get(digest: desc.digest)! - let _: ContainerizationOCI.Image = try await self.contentStore.get(digest: manifest.config.digest)! + let manifest: OCIManifest = try await self.contentStore.get(digest: desc.digest)! + let _: OCIImage = try await self.contentStore.get(digest: manifest.config.digest)! for layer in manifest.layers { _ = try await self.contentStore.get(digest: layer.digest)! } @@ -64,15 +64,15 @@ final class ImageStoreImagePullTests { @Test( arguments: [ - (Platform(arch: "arm64", os: "linux", variant: "v8"), imagePullArm64Layers), - (Platform(arch: "amd64", os: "linux"), imagePullAmd64Layers), + (OCIPlatform(arch: "arm64", os: "linux", variant: "v8"), imagePullArm64Layers), + (OCIPlatform(arch: "amd64", os: "linux"), imagePullAmd64Layers), (nil, imagePullTestAllLayers), ]) - func testPullSinglePlatform(platform: Platform?, expectLayers: [String]) async throws { + func testPullSinglePlatform(platform: OCIPlatform?, expectLayers: [String]) async throws { let img = try await self.store.pull( reference: "ghcr.io/linuxcontainers/alpine:3.20@sha256:0a6a86d44d7f93c4f2b8dea7f0eee64e72cb98635398779f3610949632508d57", platform: platform) let rootDescriptor = img.descriptor - let index: ContainerizationOCI.Index = try await contentStore.get(digest: rootDescriptor.digest)! + let index: OCIIndex = try await contentStore.get(digest: rootDescriptor.digest)! var foundMatch = false for desc in index.manifests { if let platform { @@ -82,8 +82,8 @@ final class ImageStoreImagePullTests { } foundMatch = true await #expect(throws: Never.self) { - let manifest: ContainerizationOCI.Manifest = try await self.contentStore.get(digest: desc.digest)! - let _: ContainerizationOCI.Image = try await self.contentStore.get(digest: manifest.config.digest)! + let manifest: OCIManifest = try await self.contentStore.get(digest: desc.digest)! + let _: OCIImage = try await self.contentStore.get(digest: manifest.config.digest)! for layer in manifest.layers { _ = try await self.contentStore.get(digest: layer.digest)! } diff --git a/vminitd/Sources/vmexec/ExecCommand.swift b/vminitd/Sources/vmexec/ExecCommand.swift index 545935c2..5ba8ea09 100644 --- a/vminitd/Sources/vmexec/ExecCommand.swift +++ b/vminitd/Sources/vmexec/ExecCommand.swift @@ -40,7 +40,7 @@ struct ExecCommand: ParsableCommand { let src = URL(fileURLWithPath: processPath) let processBytes = try Data(contentsOf: src) let process = try JSONDecoder().decode( - ContainerizationOCI.Process.self, + OCIProcess.self, from: processBytes ) try execInNamespaces(process: process, log: log) @@ -59,7 +59,7 @@ struct ExecCommand: ParsableCommand { } private func execInNamespaces( - process: ContainerizationOCI.Process, + process: OCIProcess, log: Logger ) throws { // CLOEXEC the pipe fd that signals process readiness. diff --git a/vminitd/Sources/vmexec/Mount.swift b/vminitd/Sources/vmexec/Mount.swift index e3d52107..1c3b3adb 100644 --- a/vminitd/Sources/vmexec/Mount.swift +++ b/vminitd/Sources/vmexec/Mount.swift @@ -20,10 +20,10 @@ import Foundation import Musl struct ContainerMount { - private let mounts: [ContainerizationOCI.Mount] + private let mounts: [OCIMount] private let rootfs: String - init(rootfs: String, mounts: [ContainerizationOCI.Mount]) { + init(rootfs: String, mounts: [OCIMount]) { self.rootfs = rootfs self.mounts = mounts } @@ -55,9 +55,9 @@ struct ContainerMount { } } -extension ContainerizationOCI.Mount { - func toOSMount() -> ContainerizationOS.Mount { - ContainerizationOS.Mount( +extension OCIMount { + func toOSMount() -> Mount { + Mount( type: self.type, source: self.source, target: self.destination, diff --git a/vminitd/Sources/vmexec/RunCommand.swift b/vminitd/Sources/vmexec/RunCommand.swift index 42ba3114..5e69f9d6 100644 --- a/vminitd/Sources/vmexec/RunCommand.swift +++ b/vminitd/Sources/vmexec/RunCommand.swift @@ -34,12 +34,12 @@ struct RunCommand: ParsableCommand { LoggingSystem.bootstrap(App.standardError) let log = Logger(label: "vmexec") - let bundle = try ContainerizationOCI.Bundle.load(path: URL(filePath: bundlePath)) + let bundle = try OCIBundle.load(path: URL(filePath: bundlePath)) let ociSpec = try bundle.loadConfig() try execInNamespace(spec: ociSpec, log: log) } - private func childRootSetup(rootfs: ContainerizationOCI.Root, mounts: [ContainerizationOCI.Mount], log: Logger) throws { + private func childRootSetup(rootfs: OCIRoot, mounts: [OCIMount], log: Logger) throws { // setup rootfs try prepareRoot(rootfs: rootfs.path) try mountRootfs(rootfs: rootfs.path, mounts: mounts) @@ -49,7 +49,7 @@ struct RunCommand: ParsableCommand { try reOpenDevNull() } - private func execInNamespace(spec: ContainerizationOCI.Spec, log: Logger) throws { + private func execInNamespace(spec: OCISpec, log: Logger) throws { guard let process = spec.process else { fatalError("no process configuration found in runtime spec") } @@ -138,7 +138,7 @@ struct RunCommand: ParsableCommand { } } - private func mountRootfs(rootfs: String, mounts: [ContainerizationOCI.Mount]) throws { + private func mountRootfs(rootfs: String, mounts: [OCIMount]) throws { let containerMount = ContainerMount(rootfs: rootfs, mounts: mounts) try containerMount.mountToRootfs() try containerMount.configureConsole() diff --git a/vminitd/Sources/vmexec/vmexec.swift b/vminitd/Sources/vmexec/vmexec.swift index ab383ace..6254bec8 100644 --- a/vminitd/Sources/vmexec/vmexec.swift +++ b/vminitd/Sources/vmexec/vmexec.swift @@ -68,7 +68,7 @@ extension App { } } - static func exec(process: ContainerizationOCI.Process) throws { + static func exec(process: OCIProcess) throws { let executable = strdup(process.args[0]) var argv = process.args.map { strdup($0) } argv += [nil] @@ -87,7 +87,7 @@ extension App { fatalError("execvpe failed") } - static func setPermissions(user: ContainerizationOCI.User) throws { + static func setPermissions(user: OCIUser) throws { if user.additionalGids.count > 0 { guard setgroups(user.additionalGids.count, user.additionalGids) == 0 else { throw App.Errno(stage: "setgroups()") @@ -104,7 +104,7 @@ extension App { } } - static func fixStdioPerms(user: ContainerizationOCI.User) throws { + static func fixStdioPerms(user: OCIUser) throws { for i in 0...2 { var fdStat = stat() try withUnsafeMutablePointer(to: &fdStat) { pointer in @@ -122,7 +122,7 @@ extension App { } } - static func setRLimits(rlimits: [ContainerizationOCI.POSIXRlimit]) throws { + static func setRLimits(rlimits: [OCIRlimit]) throws { for rl in rlimits { var limit = rlimit(rlim_cur: rl.soft, rlim_max: rl.hard) let resource: Int32 diff --git a/vminitd/Sources/vminitd/ManagedContainer.swift b/vminitd/Sources/vminitd/ManagedContainer.swift index ce6ade9c..7d239ab7 100644 --- a/vminitd/Sources/vminitd/ManagedContainer.swift +++ b/vminitd/Sources/vminitd/ManagedContainer.swift @@ -25,7 +25,7 @@ actor ManagedContainer { let initProcess: ManagedProcess private let _log: Logger - private let _bundle: ContainerizationOCI.Bundle + private let _bundle: OCIBundle private var _execs: [String: ManagedProcess] = [:] var pid: Int32 { @@ -35,10 +35,10 @@ actor ManagedContainer { init( id: String, stdio: HostStdio, - spec: ContainerizationOCI.Spec, + spec: OCISpec, log: Logger ) throws { - let bundle = try ContainerizationOCI.Bundle.create( + let bundle = try OCIBundle.create( path: Self.craftBundlePath(id: id), spec: spec ) @@ -73,7 +73,7 @@ extension ManagedContainer { func createExec( id: String, stdio: HostStdio, - process: ContainerizationOCI.Process + process: OCIProcess ) throws { // Write the process config to the bundle, and pass this on // over to ManagedProcess to deal with. @@ -144,8 +144,8 @@ extension ManagedContainer { } } -extension ContainerizationOCI.Bundle { - func createExecSpec(id: String, process: ContainerizationOCI.Process) throws { +extension OCIBundle { + func createExecSpec(id: String, process: OCIProcess) throws { let specDir = self.path.appending(path: "execs/\(id)") let fm = FileManager.default diff --git a/vminitd/Sources/vminitd/ManagedProcess.swift b/vminitd/Sources/vminitd/ManagedProcess.swift index ebe4327b..6aa4a159 100644 --- a/vminitd/Sources/vminitd/ManagedProcess.swift +++ b/vminitd/Sources/vminitd/ManagedProcess.swift @@ -65,7 +65,7 @@ final class ManagedProcess: Sendable { init( id: String, stdio: HostStdio, - bundle: ContainerizationOCI.Bundle, + bundle: OCIBundle, owningPid: Int32? = nil, log: Logger ) throws { diff --git a/vminitd/Sources/vminitd/Server+GRPC.swift b/vminitd/Sources/vminitd/Server+GRPC.swift index f49a78b6..7d378496 100644 --- a/vminitd/Sources/vminitd/Server+GRPC.swift +++ b/vminitd/Sources/vminitd/Server+GRPC.swift @@ -257,7 +257,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid ]) do { - let mnt = ContainerizationOS.Mount( + let mnt = Mount( type: request.type, source: request.source, target: request.destination, @@ -376,7 +376,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid do { var ociSpec = try JSONDecoder().decode( - ContainerizationOCI.Spec.self, + OCISpec.self, from: request.configuration ) @@ -833,7 +833,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid } extension Initd { - func ociAlterations(ociSpec: inout ContainerizationOCI.Spec) throws { + func ociAlterations(ociSpec: inout OCISpec) throws { guard var process = ociSpec.process else { throw ContainerizationError( .invalidArgument,