From 00da297add6aba0fbd7be56b834274fe7a626d39 Mon Sep 17 00:00:00 2001 From: Danny Canter Date: Wed, 9 Jul 2025 00:15:45 -0700 Subject: [PATCH] ContainerizationOCI: Add OCI prefix to types A large majority of the types in this package were extremely generically named making it common to have to reference the types as ContainerizationOCI.TheType. This change prefixes all of the core runtime spec, and image spec types with OCI, and then adjusts our code to reference these new names so we still build :) This is a breaking change, but I'd rather pull the bandaid now. --- Sources/Containerization/Agent/Vminitd.swift | 6 +- Sources/Containerization/Image/Image.swift | 16 +- .../Image/ImageStore/ImageStore+Export.swift | 32 +-- .../Image/ImageStore/ImageStore+Import.swift | 56 ++-- .../ImageStore/ImageStore+OCILayout.swift | 6 +- .../ImageStore+ReferenceManager.swift | 2 +- .../Image/ImageStore/ImageStore.swift | 6 +- .../Containerization/Image/InitImage.swift | 22 +- .../Containerization/Image/KernelImage.swift | 24 +- .../Image/Unpacker/EXT4Unpacker.swift | 6 +- .../Image/Unpacker/Unpacker.swift | 2 +- Sources/Containerization/LinuxContainer.swift | 56 +++- Sources/Containerization/LinuxProcess.swift | 8 +- Sources/Containerization/SystemPlatform.swift | 4 +- .../VirtualMachineAgent.swift | 4 +- .../ContainerizationOCI/AnnotationKeys.swift | 3 +- Sources/ContainerizationOCI/Bundle.swift | 14 +- .../Client/LocalOCILayoutClient.swift | 32 +-- .../Client/RegistryClient+Fetch.swift | 38 +-- .../Client/RegistryClient+Push.swift | 4 +- .../ContainerizationOCI/Content/Content.swift | 8 +- Sources/ContainerizationOCI/Descriptor.swift | 6 +- Sources/ContainerizationOCI/ImageConfig.swift | 18 +- Sources/ContainerizationOCI/Index.swift | 6 +- Sources/ContainerizationOCI/Manifest.swift | 8 +- Sources/ContainerizationOCI/MediaType.swift | 4 +- Sources/ContainerizationOCI/Platform.swift | 18 +- .../{Spec.swift => RuntimeSpec.swift} | 270 +++++++++--------- Sources/ContainerizationOCI/State.swift | 18 +- Sources/ContainerizationOCI/Version.swift | 4 +- Sources/Integration/ProcessTests.swift | 6 +- Sources/Integration/Suite.swift | 2 +- Sources/cctl/ImageCommand.swift | 14 +- Sources/cctl/RootfsCommand.swift | 4 +- Sources/cctl/cctl+Utils.swift | 4 +- .../OCIImageTests.swift | 26 +- .../OCIPlatformTests.swift | 32 +-- .../RegistryClientTests.swift | 34 +-- .../ImageTests/ImageStoreImagePullTests.swift | 18 +- vminitd/Sources/vmexec/ExecCommand.swift | 4 +- vminitd/Sources/vmexec/Mount.swift | 10 +- vminitd/Sources/vmexec/RunCommand.swift | 8 +- vminitd/Sources/vmexec/vmexec.swift | 8 +- .../Sources/vminitd/ManagedContainer.swift | 12 +- vminitd/Sources/vminitd/ManagedProcess.swift | 2 +- vminitd/Sources/vminitd/Server+GRPC.swift | 6 +- 46 files changed, 465 insertions(+), 426 deletions(-) rename Sources/ContainerizationOCI/{Spec.swift => RuntimeSpec.swift} (71%) 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,