Skip to content
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 13 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import PackageDescription
let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0"
let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified"
let builderShimVersion = "0.12.0"
let scVersion = "0.32.2"
let scVersion = "0.33.2"

let package = Package(
name: "container",
Expand Down Expand Up @@ -341,7 +341,9 @@ let package = Package(
),
.target(
name: "ContainerRuntimeLinuxClient",
dependencies: [],
dependencies: [
.product(name: "ContainerizationOCI", package: "containerization"),
],
path: "Sources/Services/RuntimeLinux/Client"
),
.executableTarget(
Expand Down Expand Up @@ -520,9 +522,17 @@ let package = Package(
.target(
name: "ContainerVersion",
dependencies: [
"CVersion"
.product(name: "SystemPackage", package: "swift-system"),
"CVersion",
],
),
.testTarget(
name: "ContainerVersionTests",
dependencies: [
.product(name: "SystemPackage", package: "swift-system"),
"ContainerVersion",
]
),
.target(
name: "CVersion",
dependencies: [],
Expand Down
50 changes: 28 additions & 22 deletions Sources/APIServer/APIServer+Start.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ extension APIServer {
@Flag(name: .long, help: "Enable debug logging")
var debug = false

var appRoot = ApplicationRoot.url
var appRoot = ApplicationRoot.path

var installRoot = InstallRoot.url
var installRoot = InstallRoot.path

var logRoot = LogRoot.path

func run() async throws {
let containerSystemConfig: ContainerSystemConfig = try await ConfigurationLoader.load()
let commandName = APIServer._commandName
let logPath = logRoot.map { $0.appending("\(commandName).log") }
let logPath = logRoot.map { $0.appending(FilePath.Component("\(commandName).log") ?? "unknown") }
let log = ServiceLogger.bootstrap(category: "APIServer", debug: debug, logPath: logPath)
log.info("starting helper", metadata: ["name": "\(commandName)"])
defer {
Expand Down Expand Up @@ -179,22 +179,24 @@ extension APIServer {
log.info(
"initializing plugin loader",
metadata: [
"installRoot": "\(installRoot.path(percentEncoded: false))"
"installRoot": "\(installRoot.string)"
])

let pluginsURL = PluginLoader.userPluginsDir(installRoot: installRoot)
// TODO: Remove when we convert PluginLoader to FilePath
let installRootURL = URL(fileURLWithPath: installRoot.string)
let pluginsURL = PluginLoader.userPluginsDir(installRoot: installRootURL)
log.info("detecting user plugins directory", metadata: ["path": "\(pluginsURL.path(percentEncoded: false))"])
var directoryExists: ObjCBool = false
_ = FileManager.default.fileExists(atPath: pluginsURL.path, isDirectory: &directoryExists)
let userPluginsURL = directoryExists.boolValue ? pluginsURL : nil

// plugins built into the application installed as a Unix-like application
let installRootPluginsURL =
let installRootPluginsPath =
installRoot
.appendingPathComponent("libexec")
.appendingPathComponent("container")
.appendingPathComponent("plugins")
.standardized
.appending(FilePath.Component("libexec"))
.appending(FilePath.Component("container"))
.appending(FilePath.Component("plugins"))
let installRootPluginsURL = URL(fileURLWithPath: installRootPluginsPath.string)

let pluginDirectories = [
userPluginsURL,
Expand All @@ -210,9 +212,10 @@ extension APIServer {
log.info("discovered plugin directory", metadata: ["path": "\(pluginDirectory.path(percentEncoded: false))"])
}

let appRootURL = URL(fileURLWithPath: appRoot.string)
return try PluginLoader(
appRoot: appRoot,
installRoot: installRoot,
appRoot: appRootURL,
installRoot: installRootURL,
logRoot: logRoot,
pluginDirectories: pluginDirectories,
pluginFactories: pluginFactories,
Expand Down Expand Up @@ -245,9 +248,12 @@ extension APIServer {
private func initializeHealthCheckService(log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) {
log.info("initializing health check service")

// TODO: Remove when we convert HealthCheckHarness to FilePath
let installRootURL = URL(fileURLWithPath: installRoot.string)
let appRootURL = URL(fileURLWithPath: appRoot.string)
let svc = HealthCheckHarness(
appRoot: appRoot,
installRoot: installRoot,
appRoot: appRootURL,
installRoot: installRootURL,
logRoot: logRoot,
log: log
)
Expand All @@ -257,7 +263,9 @@ extension APIServer {
private func initializeKernelService(log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
log.info("initializing kernel service")

let svc = try KernelService(log: log, appRoot: appRoot)
// TODO: Remove when we convert KernelService to FilePath
let appRootURL = URL(fileURLWithPath: appRoot.string)
let svc = try KernelService(log: log, appRoot: appRootURL)
let harness = KernelHarness(service: svc, log: log)
routes[XPCRoute.installKernel] = XPCServer.route(harness.install)
routes[XPCRoute.getDefaultKernel] = XPCServer.route(harness.getDefaultKernel)
Expand All @@ -271,8 +279,10 @@ extension APIServer {
) throws -> ContainersService {
log.info("initializing containers service")

// TODO: Remove when we convert ContainersService to FilePath
let appRootURL = URL(fileURLWithPath: appRoot.string)
let service = try ContainersService(
appRoot: appRoot,
appRoot: appRootURL,
pluginLoader: pluginLoader,
containerSystemConfig: containerSystemConfig,
log: log,
Expand Down Expand Up @@ -310,9 +320,7 @@ extension APIServer {
) async throws -> NetworksService {
log.info("initializing networks service")

// TODO: This goes away when we convert our roots to FilePath
let appPath = FilePath(appRoot.absolutePath())
let resourceRoot = appPath.appending("networks")
let resourceRoot = appRoot.appending(FilePath.Component("networks"))
let service = try await NetworksService(
pluginLoader: pluginLoader,
resourceRoot: resourceRoot,
Expand Down Expand Up @@ -355,9 +363,7 @@ extension APIServer {
) throws -> VolumesService {
log.info("initializing volume service")

// TODO: This goes away when we convert our roots to FilePath
let appPath = FilePath(appRoot.absolutePath())
let resourceRoot = appPath.appending("volumes")
let resourceRoot = appRoot.appending(FilePath.Component("volumes"))
let service = try VolumesService(resourceRoot: resourceRoot, containersService: containersService, log: log)
let harness = VolumesHarness(service: service, log: log)

Expand Down
40 changes: 28 additions & 12 deletions Sources/ContainerCommands/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
import ArgumentParser
import ContainerAPIClient
import ContainerLog
import ContainerPersistence
import ContainerPlugin
import ContainerVersion
import ContainerizationError
import ContainerizationOS
import Foundation
import Logging
import SystemPackage
import TerminalProgress

// This logger is only used until `asyncCommand.run()`.
Expand Down Expand Up @@ -136,11 +138,12 @@ public struct Application: AsyncLoggableCommand {
}

public static func createPluginLoader() async throws -> PluginLoader {
let installRoot = CommandLine.executablePathUrl
.deletingLastPathComponent()
.appendingPathComponent("..")
.standardized
let pluginsURL = PluginLoader.userPluginsDir(installRoot: installRoot)
let installRootPath = CommandLine.executablePath
.removingLastComponent()
.removingLastComponent()
// TODO: Remove when we convert PluginLoader to FilePath.
let installRootURL = URL(fileURLWithPath: installRootPath.string)
let pluginsURL = PluginLoader.userPluginsDir(installRoot: installRootURL)
var directoryExists: ObjCBool = false
_ = FileManager.default.fileExists(atPath: pluginsURL.path, isDirectory: &directoryExists)
let userPluginsURL = directoryExists.boolValue ? pluginsURL : nil
Expand All @@ -149,13 +152,12 @@ public struct Application: AsyncLoggableCommand {
let appBundlePluginsURL = Bundle.main.resourceURL?.appending(path: "plugins")

// plugins built into the application installed as a Unix-like application
let installRootPluginsURL =
installRoot
.appendingPathComponent("libexec")
.appendingPathComponent("container")
.appendingPathComponent("plugins")
.standardized

let installRootPluginsPath =
installRootPath
.appending(FilePath.Component("libexec"))
.appending(FilePath.Component("container"))
.appending(FilePath.Component("plugins"))
let installRootPluginsURL = URL(fileURLWithPath: installRootPluginsPath.string)
let pluginDirectories = [
userPluginsURL,
appBundlePluginsURL,
Expand All @@ -180,6 +182,20 @@ public struct Application: AsyncLoggableCommand {
)
}

/// Load the system configuration using `appRoot` / `installRoot` reported by the
/// daemon. `container system start` MUST have previously been run to start the daemon.
public static func loadContainerSystemConfig() async throws -> ContainerSystemConfig {
let health = try await ClientHealthCheck.ping(timeout: .seconds(10))
let appRoot = FilePath(health.appRoot.path(percentEncoded: false))
let installRoot = FilePath(health.installRoot.path(percentEncoded: false))
return try await ConfigurationLoader.load(
configurationFiles: [
ConfigurationLoader.configurationFile(in: appRoot, of: .appRoot),
ConfigurationLoader.configurationFile(in: installRoot, of: .installRoot),
]
)
}

public func validate() throws {
// Not really a "validation", but a cheat to run this before
// any of the commands do their business.
Expand Down
2 changes: 1 addition & 1 deletion Sources/ContainerCommands/BuildCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ extension Application {
var pull: Bool = false

public func run() async throws {
let containerSystemConfig: ContainerSystemConfig = try await ConfigurationLoader.load()
let containerSystemConfig: ContainerSystemConfig = try await Application.loadContainerSystemConfig()
do {
let timeout: Duration = .seconds(300)
let progressConfig = try ProgressConfig(
Expand Down
2 changes: 1 addition & 1 deletion Sources/ContainerCommands/Builder/BuilderStart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ extension Application {
public init() {}

public func run() async throws {
let containerSystemConfig: ContainerSystemConfig = try await ConfigurationLoader.load()
let containerSystemConfig: ContainerSystemConfig = try await Application.loadContainerSystemConfig()
let progressConfig = try ProgressConfig(
showTasks: true,
showItems: true,
Expand Down
13 changes: 11 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 @@ -56,7 +57,7 @@ extension Application {
var arguments: [String] = []

public func run() async throws {
let containerSystemConfig: ContainerSystemConfig = try await ConfigurationLoader.load()
let containerSystemConfig: ContainerSystemConfig = try await Application.loadContainerSystemConfig()
let progressConfig = try ProgressConfig(
showTasks: true,
showItems: true,
Expand Down Expand Up @@ -88,7 +89,15 @@ extension Application {

let options = ContainerCreateOptions(autoRemove: managementFlags.remove)
let client = ContainerClient()
try await client.create(configuration: ck.0, options: options, kernel: ck.1, initImage: ck.2)
let blockIO = try Parser.blockIO(specs: managementFlags.blkio)
let runtimeData: Data? = try blockIO.map { try JSONEncoder().encode(LinuxRuntimeData(blockIO: $0)) }
try await client.create(
configuration: ck.0,
options: options,
kernel: ck.1,
initImage: ck.2,
runtimeData: runtimeData
)

if !self.managementFlags.cidfile.isEmpty {
let path = self.managementFlags.cidfile
Expand Down
8 changes: 6 additions & 2 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 @@ -63,7 +64,7 @@ extension Application {
var arguments: [String] = []

public func run() async throws {
let containerSystemConfig: ContainerSystemConfig = try await ConfigurationLoader.load()
let containerSystemConfig: ContainerSystemConfig = try await Application.loadContainerSystemConfig()
var exitCode: Int32 = 127
let id = Utility.createContainerID(name: self.managementFlags.name)

Expand Down Expand Up @@ -109,11 +110,14 @@ extension Application {
progress.set(description: "Starting container")

let options = ContainerCreateOptions(autoRemove: managementFlags.remove)
let blockIO = try Parser.blockIO(specs: managementFlags.blkio)
let runtimeData: Data? = try blockIO.map { try JSONEncoder().encode(LinuxRuntimeData(blockIO: $0)) }
try await client.create(
configuration: ck.0,
options: options,
kernel: ck.1,
initImage: ck.2
initImage: ck.2,
runtimeData: runtimeData
)

let detach = self.managementFlags.detach
Expand Down
23 changes: 13 additions & 10 deletions Sources/ContainerCommands/DefaultCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ContainerAPIClient
import ContainerPlugin
import Darwin
import Foundation
import SystemPackage

struct DefaultCommand: AsyncLoggableCommand {
public static let configuration = CommandConfiguration(
Expand Down Expand Up @@ -48,17 +49,19 @@ struct DefaultCommand: AsyncLoggableCommand {
}

// Compute canonical plugin directories to show in helpful errors (avoid hard-coded paths)
let installRoot = CommandLine.executablePathUrl
.deletingLastPathComponent()
.appendingPathComponent("..")
.standardized
let userPluginsURL = PluginLoader.userPluginsDir(installRoot: installRoot)
let installRootPluginsURL =
let installRoot = CommandLine.executablePath
.removingLastComponent()
.removingLastComponent()

// TODO: Remove when we convert PluginLoader to FilePath
let installRootURL = URL(fileURLWithPath: installRoot.string)
let userPluginsURL = PluginLoader.userPluginsDir(installRoot: installRootURL)
let installRootPluginsPath =
installRoot
.appendingPathComponent("libexec")
.appendingPathComponent("container")
.appendingPathComponent("plugins")
.standardized
.appending(FilePath.Component("libexec"))
.appending(FilePath.Component("container"))
.appending(FilePath.Component("plugins"))
let installRootPluginsURL = URL(fileURLWithPath: installRootPluginsPath.string)
let hintPaths = [userPluginsURL, installRootPluginsURL]
.map { $0.appendingPathComponent(command).path(percentEncoded: false) }
.joined(separator: "\n - ")
Expand Down
Loading