Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ let package = Package(
"ContainerAPIClient",
"ContainerResource",
"ContainerRuntimeClient",
"ContainerRuntimeLinuxClient",
"ContainerXPC",
"MachineAPIClient",
],
Expand Down
3 changes: 0 additions & 3 deletions Sources/APIServer/APIServer+Start.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ extension APIServer {
try await initializePlugins(pluginLoader: pluginLoader, log: log, routes: &routes, debug: debug)
let containersService = try initializeContainersService(
pluginLoader: pluginLoader,
containerSystemConfig: containerSystemConfig,
log: log,
routes: &routes
)
Expand Down Expand Up @@ -273,7 +272,6 @@ extension APIServer {

private func initializeContainersService(
pluginLoader: PluginLoader,
containerSystemConfig: ContainerSystemConfig,
log: Logger,
routes: inout [XPCRoute: XPCServer.RouteHandler]
) throws -> ContainersService {
Expand All @@ -284,7 +282,6 @@ extension APIServer {
let service = try ContainersService(
appRoot: appRootURL,
pluginLoader: pluginLoader,
containerSystemConfig: containerSystemConfig,
log: log,
debugHelpers: debug
)
Expand Down
34 changes: 25 additions & 9 deletions Sources/ContainerCommands/Builder/BuilderStart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ContainerBuild
import ContainerPersistence
import ContainerPlugin
import ContainerResource
import ContainerRuntimeLinuxClient
import Containerization
import ContainerizationError
import ContainerizationExtras
Expand Down Expand Up @@ -276,24 +277,39 @@ extension Application {
options: dnsOptions
)

let kernel = try await {
await progressUpdate([
.setDescription("Fetching kernel"),
.setItemsName("binary"),
])
await progressUpdate([
.setDescription("Fetching kernel"),
.setItemsName("binary"),
])
let kernel = try await ClientKernel.getDefaultKernel(for: .current)

let kernel = try await ClientKernel.getDefaultKernel(for: .current)
return kernel
}()
// Ensure the init image is present locally and resolve it to a canonical
// reference for the runtime to load at bootstrap.
await progressUpdate([
.setDescription("Fetching init image"),
.setItemsName("blobs"),
])
let initFetchTask = await taskManager.startTask()
let initImage = try await ClientImage.fetch(
reference: containerSystemConfig.vminit.image,
platform: .current,
containerSystemConfig: containerSystemConfig,
progressUpdate: ProgressTaskCoordinator.handler(for: initFetchTask, from: progressUpdate)
)

await progressUpdate([
.setDescription("Starting BuildKit container")
])

let runtimeData = try LinuxRuntimeData.encodeData(
kernelPath: kernel.path.path,
initImageRef: initImage.reference
)

try await client.create(
configuration: config,
options: .default,
kernel: kernel
runtimeData: runtimeData
)

try await startBuildKit(client: client, id: Builder.builderContainerId, progressUpdate, taskManager)
Expand Down
17 changes: 15 additions & 2 deletions Sources/ContainerCommands/Container/ContainerCreate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ContainerAPIClient
import ContainerPersistence
import ContainerPlugin
import ContainerResource
import ContainerRuntimeLinuxClient
import ContainerizationError
import Foundation
import TerminalProgress
Expand Down Expand Up @@ -72,7 +73,7 @@ extension Application {
let id = Utility.createContainerID(name: self.managementFlags.name)
try Utility.validEntityName(id)

let ck = try await Utility.containerConfigFromFlags(
let (config, kernel, initImageRef) = try await Utility.containerConfigFromFlags(
id: id,
image: image,
arguments: arguments,
Expand All @@ -86,9 +87,21 @@ extension Application {
log: log
)

guard let kernel else {
throw ContainerizationError(.internalError, message: "failed to resolve kernel")
}
guard let initImageRef else {
throw ContainerizationError(.internalError, message: "failed to resolve init image")
}

let runtimeData = try LinuxRuntimeData.encodeData(
kernelPath: kernel.path.path,
initImageRef: initImageRef
)

let options = ContainerCreateOptions(autoRemove: managementFlags.remove)
let client = ContainerClient()
try await client.create(configuration: ck.0, options: options, kernel: ck.1, initImage: ck.2)
try await client.create(configuration: config, options: options, runtimeData: runtimeData)

if !self.managementFlags.cidfile.isEmpty {
let path = self.managementFlags.cidfile
Expand Down
22 changes: 15 additions & 7 deletions Sources/ContainerCommands/Container/ContainerRun.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ContainerAPIClient
import ContainerPersistence
import ContainerPlugin
import ContainerResource
import ContainerRuntimeLinuxClient
import Containerization
import ContainerizationError
import ContainerizationExtras
Expand Down Expand Up @@ -92,7 +93,7 @@ extension Application {
)
}

let ck = try await Utility.containerConfigFromFlags(
let (config, kernel, initImageRef) = try await Utility.containerConfigFromFlags(
id: id,
image: image,
arguments: arguments,
Expand All @@ -106,15 +107,22 @@ extension Application {
log: log
)

guard let kernel else {
throw ContainerizationError(.internalError, message: "failed to resolve kernel")
}
guard let initImageRef else {
throw ContainerizationError(.internalError, message: "failed to resolve init image")
}

let runtimeData = try LinuxRuntimeData.encodeData(
kernelPath: kernel.path.path,
initImageRef: initImageRef
)

progress.set(description: "Starting container")

let options = ContainerCreateOptions(autoRemove: managementFlags.remove)
try await client.create(
configuration: ck.0,
options: options,
kernel: ck.1,
initImage: ck.2
)
try await client.create(configuration: config, options: options, runtimeData: runtimeData)

let detach = self.managementFlags.detach
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import ArgumentParser
import ContainerLog
import ContainerPersistence
import ContainerPlugin
import ContainerXPC
import Foundation
Expand Down Expand Up @@ -60,7 +61,13 @@ extension MachineAPIServer {
log.info("configuring XPC server")

let resourceRoot = FilePath(resources)
let service = try MachinesService(appRoot: pluginStateRoot, resourceRoot: resourceRoot, log: log)
let containerSystemConfig: ContainerSystemConfig = try await ConfigurationLoader.load()
let service = try MachinesService(
appRoot: pluginStateRoot,
resourceRoot: resourceRoot,
containerSystemConfig: containerSystemConfig,
log: log
)
let harness = MachinesHarness(service: service)

let server = XPCServer(
Expand Down
16 changes: 2 additions & 14 deletions Sources/Services/ContainerAPIService/Client/ContainerClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import ContainerResource
import ContainerXPC
import Containerization
import ContainerizationError
import ContainerizationOCI
import Foundation
Expand Down Expand Up @@ -48,27 +47,16 @@ public struct ContainerClient: Sendable {
public func create(
configuration: ContainerConfiguration,
options: ContainerCreateOptions = .default,
kernel: Kernel,
initImage: String? = nil,
runtimeData: Data? = nil
runtimeData: Data
) async throws {
do {
let request = XPCMessage(route: .containerCreate)

let data = try JSONEncoder().encode(configuration)
let kdata = try JSONEncoder().encode(kernel)
let odata = try JSONEncoder().encode(options)
request.set(key: .containerConfig, value: data)
request.set(key: .kernel, value: kdata)
request.set(key: .containerOptions, value: odata)

if let initImage {
request.set(key: .initImage, value: initImage)
}

if let runtimeData {
request.set(key: .runtimeData, value: runtimeData)
}
request.set(key: .runtimeData, value: runtimeData)

try await xpcSend(message: request)
} catch {
Expand Down
66 changes: 37 additions & 29 deletions Sources/Services/ContainerAPIService/Client/Utility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public struct Utility {
}
}

// TODO: refactor to remove kernel + initimage fetch from APIService
public static func containerConfigFromFlags(
id: String,
image: String,
Expand All @@ -77,9 +78,11 @@ public struct Utility {
registry: Flags.Registry,
imageFetch: Flags.ImageFetch,
containerSystemConfig: ContainerSystemConfig,
fetchKernel: Bool = true,
fetchInitImage: Bool = true,
progressUpdate: @escaping ProgressUpdateHandler,
log: Logger
) async throws -> (ContainerConfiguration, Kernel, String?) {
) async throws -> (ContainerConfiguration, Kernel?, String?) {
let requestedPlatform = try DefaultPlatform.resolveWithDefaults(
platform: management.platform,
os: management.os,
Expand Down Expand Up @@ -113,34 +116,39 @@ public struct Utility {
platform: requestedPlatform,
progressUpdate: ProgressTaskCoordinator.handler(for: unpackTask, from: progressUpdate))

await progressUpdate([
.setDescription("Fetching kernel"),
.setItemsName("binary"),
])

let kernel = try await self.getKernel(management: management)

// Pull and unpack the initial filesystem
await progressUpdate([
.setDescription("Fetching init image"),
.setItemsName("blobs"),
])
let fetchInitTask = await taskManager.startTask()
let initImageRef = management.initImage ?? containerSystemConfig.vminit.image
let initImage = try await ClientImage.fetch(
reference: initImageRef, platform: .current, scheme: scheme,
containerSystemConfig: containerSystemConfig,
progressUpdate: ProgressTaskCoordinator.handler(for: fetchInitTask, from: progressUpdate),
maxConcurrentDownloads: imageFetch.maxConcurrentDownloads)
var kernel: Kernel? = nil
if fetchKernel {
await progressUpdate([
.setDescription("Fetching kernel"),
.setItemsName("binary"),
])
kernel = try await self.getKernel(management: management)
}

await progressUpdate([
.setDescription("Unpacking init image"),
.setItemsName("entries"),
])
let unpackInitTask = await taskManager.startTask()
_ = try await initImage.getCreateSnapshot(
platform: .current,
progressUpdate: ProgressTaskCoordinator.handler(for: unpackInitTask, from: progressUpdate))
var initImageRef: String? = nil
if fetchInitImage {
await progressUpdate([
.setDescription("Fetching init image"),
.setItemsName("blobs"),
])
let fetchInitTask = await taskManager.startTask()
let initImageReference = management.initImage ?? containerSystemConfig.vminit.image
let initImage = try await ClientImage.fetch(
reference: initImageReference, platform: .current, scheme: scheme,
containerSystemConfig: containerSystemConfig,
progressUpdate: ProgressTaskCoordinator.handler(for: fetchInitTask, from: progressUpdate),
maxConcurrentDownloads: imageFetch.maxConcurrentDownloads)

await progressUpdate([
.setDescription("Unpacking init image"),
.setItemsName("entries"),
])
let unpackInitTask = await taskManager.startTask()
_ = try await initImage.getCreateSnapshot(
platform: .current,
progressUpdate: ProgressTaskCoordinator.handler(for: unpackInitTask, from: progressUpdate))
initImageRef = initImage.reference
}

await taskManager.finish()

Expand Down Expand Up @@ -264,7 +272,7 @@ public struct Utility {
config.runtimeHandler = runtime
}

return (config, kernel, management.initImage)
return (config, kernel, initImageRef)
}

static func getAttachmentConfigurations(
Expand Down
3 changes: 0 additions & 3 deletions Sources/Services/ContainerAPIService/Client/XPC+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,6 @@ public enum XPCKeys: String {
case systemPlatform
case kernelForce

/// Init image reference
case initImage

/// Volume
case volume
case volumes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,25 +180,21 @@ public struct ContainersHarness: Sendable {
message: "container configuration cannot be empty"
)
}
let kdata = message.dataNoCopy(key: .kernel)
guard let kdata else {
throw ContainerizationError(
.invalidArgument,
message: "kernel cannot be empty"
)
}
let odata = message.dataNoCopy(key: .containerOptions)
var options: ContainerCreateOptions = .default
if let odata {
options = try JSONDecoder().decode(ContainerCreateOptions.self, from: odata)
}
let config = try JSONDecoder().decode(ContainerConfiguration.self, from: data)
let kernel = try JSONDecoder().decode(Kernel.self, from: kdata)

let initImage = message.string(key: .initImage)
let runtimeData = message.dataNoCopy(key: .runtimeData)
guard let runtimeData = message.dataNoCopy(key: .runtimeData) else {
throw ContainerizationError(
.invalidArgument,
message: "runtime data cannot be empty"
)
}

try await service.create(configuration: config, kernel: kernel, options: options, initImage: initImage, runtimeData: runtimeData)
try await service.create(configuration: config, options: options, runtimeData: runtimeData)
return message.reply()
}

Expand Down
Loading