mirror of
https://github.com/apple/container.git
synced 2026-08-24 10:05:43 -05:00
Reorganize runtime targets for scalable plugin support (#1577)
## Type of Change - [x] Bug fix - [ ] New feature - [ ] Breaking change - [ ] Documentation update ## Motivation and Context Closes #1569 Separates generic runtime infrastructure from Linux-specific implementation into distinct SPM targets: - ContainerRuntimeClient — generic runtime plugin client API - ContainerRuntimeLinuxClient — Linux-specific types - ContainerRuntimeLinuxServer — Linux runtime service + interface strategy implementations Renames types to reflect the runtime/sandbox distinction: - SandboxClient → RuntimeClient - SandboxService → RuntimeService - SandboxRoutes → RuntimeRoutes - SandboxKeys → RuntimeKeys Breaking for external consumers: - ContainerSandboxService removed, replaced by ContainerRuntimeClient + ContainerRuntimeLinuxServer - Public types renamed ## Testing - [x] Tested locally - [ ] Added/updated tests - [ ] Added/updated docs
This commit is contained in:
@@ -5,8 +5,9 @@ builder:
|
||||
documentation_targets:
|
||||
- ContainerAPIService
|
||||
- ContainerAPIClient
|
||||
- ContainerSandboxService
|
||||
- ContainerSandboxServiceClient
|
||||
- ContainerRuntimeClient
|
||||
- ContainerRuntimeLinuxClient
|
||||
- ContainerRuntimeLinuxServer
|
||||
- ContainerNetworkService
|
||||
- ContainerNetworkServiceClient
|
||||
- ContainerImagesService
|
||||
|
||||
+21
-16
@@ -35,11 +35,13 @@ let package = Package(
|
||||
.library(name: "ContainerAPIClient", targets: ["ContainerAPIClient"]),
|
||||
.library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]),
|
||||
.library(name: "ContainerNetworkService", targets: ["ContainerNetworkService", "ContainerNetworkServiceClient"]),
|
||||
.library(name: "ContainerSandboxService", targets: ["ContainerSandboxService", "ContainerSandboxServiceClient"]),
|
||||
.library(name: "ContainerResource", targets: ["ContainerResource"]),
|
||||
.library(name: "ContainerLog", targets: ["ContainerLog"]),
|
||||
.library(name: "ContainerPersistence", targets: ["ContainerPersistence"]),
|
||||
.library(name: "ContainerPlugin", targets: ["ContainerPlugin"]),
|
||||
.library(name: "ContainerRuntimeClient", targets: ["ContainerRuntimeClient"]),
|
||||
.library(name: "ContainerRuntimeLinuxClient", targets: ["ContainerRuntimeLinuxClient"]),
|
||||
.library(name: "ContainerRuntimeLinuxServer", targets: ["ContainerRuntimeLinuxServer"]),
|
||||
.library(name: "ContainerVersion", targets: ["ContainerVersion"]),
|
||||
.library(name: "ContainerXPC", targets: ["ContainerXPC"]),
|
||||
.library(name: "ContainerOS", targets: ["ContainerOS"]),
|
||||
@@ -108,7 +110,8 @@ let package = Package(
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
"ContainerRuntimeLinuxTypes",
|
||||
"ContainerRuntimeClient",
|
||||
"ContainerRuntimeLinuxClient",
|
||||
"ContainerVersion",
|
||||
"ContainerXPC",
|
||||
"TerminalProgress",
|
||||
@@ -188,7 +191,7 @@ let package = Package(
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
"ContainerSandboxServiceClient",
|
||||
"ContainerRuntimeClient",
|
||||
"ContainerVersion",
|
||||
"ContainerXPC",
|
||||
"TerminalProgress",
|
||||
@@ -200,8 +203,8 @@ let package = Package(
|
||||
dependencies: [
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
"ContainerResource",
|
||||
"ContainerRuntimeLinuxTypes",
|
||||
"ContainerSandboxServiceClient",
|
||||
"ContainerRuntimeLinuxClient",
|
||||
"ContainerRuntimeClient",
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -336,9 +339,9 @@ let package = Package(
|
||||
path: "Sources/Services/ContainerNetworkService/Client"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerRuntimeLinuxTypes",
|
||||
name: "ContainerRuntimeLinuxClient",
|
||||
dependencies: [],
|
||||
path: "Sources/Plugins/RuntimeLinux/Types"
|
||||
path: "Sources/Services/RuntimeLinux/Client"
|
||||
),
|
||||
.executableTarget(
|
||||
name: "container-runtime-linux",
|
||||
@@ -349,41 +352,43 @@ let package = Package(
|
||||
"ContainerLog",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
"ContainerRuntimeLinuxTypes",
|
||||
"ContainerSandboxService",
|
||||
"ContainerSandboxServiceClient",
|
||||
"ContainerRuntimeClient",
|
||||
"ContainerRuntimeLinuxClient",
|
||||
"ContainerRuntimeLinuxServer",
|
||||
"ContainerVersion",
|
||||
"ContainerXPC",
|
||||
],
|
||||
path: "Sources/Plugins/RuntimeLinux",
|
||||
exclude: ["config.toml", "Types"]
|
||||
exclude: ["config.toml"]
|
||||
),
|
||||
.target(
|
||||
name: "ContainerSandboxService",
|
||||
name: "ContainerRuntimeLinuxServer",
|
||||
dependencies: [
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
.product(name: "ContainerizationOS", package: "containerization"),
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
"ContainerAPIClient",
|
||||
"ContainerNetworkServiceClient",
|
||||
"ContainerOS",
|
||||
"ContainerPersistence",
|
||||
"ContainerResource",
|
||||
"ContainerSandboxServiceClient",
|
||||
"ContainerRuntimeClient",
|
||||
"ContainerRuntimeLinuxClient",
|
||||
"ContainerXPC",
|
||||
"SocketForwarder",
|
||||
],
|
||||
path: "Sources/Services/ContainerSandboxService/Server"
|
||||
path: "Sources/Services/RuntimeLinux/Server"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerSandboxServiceClient",
|
||||
name: "ContainerRuntimeClient",
|
||||
dependencies: [
|
||||
"ContainerAPIClient",
|
||||
"ContainerResource",
|
||||
"ContainerXPC",
|
||||
],
|
||||
path: "Sources/Services/ContainerSandboxService/Client"
|
||||
path: "Sources/Services/Runtime/RuntimeClient"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerResource",
|
||||
|
||||
@@ -18,8 +18,8 @@ import ArgumentParser
|
||||
import ContainerLog
|
||||
import ContainerPlugin
|
||||
import ContainerResource
|
||||
import ContainerSandboxService
|
||||
import ContainerSandboxServiceClient
|
||||
import ContainerRuntimeClient
|
||||
import ContainerRuntimeLinuxServer
|
||||
import ContainerXPC
|
||||
import Foundation
|
||||
import Logging
|
||||
@@ -74,7 +74,7 @@ extension RuntimeLinuxHelper {
|
||||
log.info("configuring XPC server")
|
||||
nonisolated(unsafe) let anonymousConnection = xpc_connection_create(nil, nil)
|
||||
|
||||
let server = SandboxService(
|
||||
let server = RuntimeService(
|
||||
root: .init(fileURLWithPath: root),
|
||||
interfaceStrategies: interfaceStrategies,
|
||||
eventLoopGroup: eventLoopGroup,
|
||||
@@ -85,7 +85,7 @@ extension RuntimeLinuxHelper {
|
||||
let endpointServer = XPCServer(
|
||||
identifier: machServiceLabel,
|
||||
routes: [
|
||||
SandboxRoutes.createEndpoint.rawValue: XPCServer.route(server.createEndpoint)
|
||||
RuntimeRoutes.createEndpoint.rawValue: XPCServer.route(server.createEndpoint)
|
||||
],
|
||||
log: log
|
||||
)
|
||||
@@ -93,19 +93,19 @@ extension RuntimeLinuxHelper {
|
||||
let mainServer = XPCServer(
|
||||
connection: anonymousConnection,
|
||||
routes: [
|
||||
SandboxRoutes.bootstrap.rawValue: XPCServer.route(server.bootstrap),
|
||||
SandboxRoutes.createProcess.rawValue: XPCServer.route(server.createProcess),
|
||||
SandboxRoutes.state.rawValue: XPCServer.route(server.state),
|
||||
SandboxRoutes.stop.rawValue: XPCServer.route(server.stop),
|
||||
SandboxRoutes.kill.rawValue: XPCServer.route(server.kill),
|
||||
SandboxRoutes.resize.rawValue: XPCServer.route(server.resize),
|
||||
SandboxRoutes.wait.rawValue: XPCServer.route(server.wait),
|
||||
SandboxRoutes.start.rawValue: XPCServer.route(server.startProcess),
|
||||
SandboxRoutes.dial.rawValue: XPCServer.route(server.dial),
|
||||
SandboxRoutes.shutdown.rawValue: XPCServer.route(server.shutdown),
|
||||
SandboxRoutes.statistics.rawValue: XPCServer.route(server.statistics),
|
||||
SandboxRoutes.copyIn.rawValue: XPCServer.route(server.copyIn),
|
||||
SandboxRoutes.copyOut.rawValue: XPCServer.route(server.copyOut),
|
||||
RuntimeRoutes.bootstrap.rawValue: XPCServer.route(server.bootstrap),
|
||||
RuntimeRoutes.createProcess.rawValue: XPCServer.route(server.createProcess),
|
||||
RuntimeRoutes.state.rawValue: XPCServer.route(server.state),
|
||||
RuntimeRoutes.stop.rawValue: XPCServer.route(server.stop),
|
||||
RuntimeRoutes.kill.rawValue: XPCServer.route(server.kill),
|
||||
RuntimeRoutes.resize.rawValue: XPCServer.route(server.resize),
|
||||
RuntimeRoutes.wait.rawValue: XPCServer.route(server.wait),
|
||||
RuntimeRoutes.start.rawValue: XPCServer.route(server.startProcess),
|
||||
RuntimeRoutes.dial.rawValue: XPCServer.route(server.dial),
|
||||
RuntimeRoutes.shutdown.rawValue: XPCServer.route(server.shutdown),
|
||||
RuntimeRoutes.statistics.rawValue: XPCServer.route(server.statistics),
|
||||
RuntimeRoutes.copyIn.rawValue: XPCServer.route(server.copyIn),
|
||||
RuntimeRoutes.copyOut.rawValue: XPCServer.route(server.copyOut),
|
||||
],
|
||||
log: log
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ import ContainerAPIClient
|
||||
import ContainerPersistence
|
||||
import ContainerPlugin
|
||||
import ContainerResource
|
||||
import ContainerSandboxServiceClient
|
||||
import ContainerRuntimeClient
|
||||
import ContainerXPC
|
||||
import Containerization
|
||||
import ContainerizationEXT4
|
||||
@@ -34,11 +34,11 @@ import SystemPackage
|
||||
public actor ContainersService {
|
||||
struct ContainerState {
|
||||
var snapshot: ContainerSnapshot
|
||||
var client: SandboxClient? = nil
|
||||
var client: RuntimeClient? = nil
|
||||
|
||||
func getClient() throws -> SandboxClient {
|
||||
func getClient() throws -> RuntimeClient {
|
||||
guard let client else {
|
||||
var message = "no sandbox client exists"
|
||||
var message = "no runtime client exists"
|
||||
if snapshot.status == .stopped {
|
||||
message += ": container is stopped"
|
||||
}
|
||||
@@ -335,7 +335,7 @@ public actor ContainersService {
|
||||
// to boot. We can go lower, but this is a somewhat safe threshold. Containerization
|
||||
// also gives a little bit extra than the user asked for to account for guest agent overhead.
|
||||
//
|
||||
// NOTE: We could potentially leave this validation to the sandbox service(s), as
|
||||
// NOTE: We could potentially leave this validation to the runtime service(s), as
|
||||
// it's possible there could be an implementation that can get away with a lower
|
||||
// amount and be perfectly safe.
|
||||
let minimumMemory: UInt64 = 200.mib()
|
||||
@@ -451,18 +451,18 @@ public actor ContainersService {
|
||||
)
|
||||
|
||||
let runtime = state.snapshot.configuration.runtimeHandler
|
||||
let sandboxClient = try await SandboxClient.create(
|
||||
let runtimeClient = try await RuntimeClient.create(
|
||||
id: id,
|
||||
runtime: runtime
|
||||
)
|
||||
try await sandboxClient.bootstrap(stdio: stdio, networkBootstrapInfos: networkBootstrapInfos, dynamicEnv: dynamicEnv)
|
||||
try await runtimeClient.bootstrap(stdio: stdio, networkBootstrapInfos: networkBootstrapInfos, dynamicEnv: dynamicEnv)
|
||||
|
||||
try await self.exitMonitor.registerProcess(
|
||||
id: id,
|
||||
onExit: self.handleContainerExit
|
||||
)
|
||||
|
||||
state.client = sandboxClient
|
||||
state.client = runtimeClient
|
||||
await self.setContainerState(id, state, context: context)
|
||||
} catch {
|
||||
let label = Self.fullLaunchdServiceLabel(
|
||||
@@ -629,7 +629,7 @@ public actor ContainersService {
|
||||
let state = try self._getContainerState(id: id)
|
||||
|
||||
// Stop should be idempotent.
|
||||
let client: SandboxClient
|
||||
let client: RuntimeClient
|
||||
do {
|
||||
client = try state.getClient()
|
||||
} catch {
|
||||
@@ -941,8 +941,8 @@ public actor ContainersService {
|
||||
|
||||
await self.exitMonitor.stopTracking(id: id)
|
||||
|
||||
// Shutdown and deregister the sandbox service
|
||||
self.log.info("shutting down sandbox service", metadata: ["id": "\(id)"])
|
||||
// Shutdown and deregister the runtime service
|
||||
self.log.info("shutting down runtime service", metadata: ["id": "\(id)"])
|
||||
|
||||
let path = self.containerRoot.appendingPathComponent(id)
|
||||
let bundle = ContainerResource.Bundle(path: path)
|
||||
@@ -952,7 +952,7 @@ public actor ContainersService {
|
||||
instanceId: id
|
||||
)
|
||||
|
||||
// Try to shutdown the client gracefully, but if the sandbox service
|
||||
// Try to shutdown the client gracefully, but if the runtime service
|
||||
// is already dead (e.g., killed externally), we should still continue
|
||||
// with state cleanup.
|
||||
if let client = state.client {
|
||||
@@ -960,7 +960,7 @@ public actor ContainersService {
|
||||
try await client.shutdown()
|
||||
} catch {
|
||||
self.log.error(
|
||||
"failed to shutdown sandbox service",
|
||||
"failed to shutdown runtime service",
|
||||
metadata: [
|
||||
"id": "\(id)",
|
||||
"error": "\(error)",
|
||||
@@ -973,10 +973,10 @@ public actor ContainersService {
|
||||
// the process was killed externally.
|
||||
do {
|
||||
try ServiceManager.deregister(fullServiceLabel: label)
|
||||
self.log.info("deregistered sandbox service", metadata: ["id": "\(id)"])
|
||||
self.log.info("deregistered runtime service", metadata: ["id": "\(id)"])
|
||||
} catch {
|
||||
self.log.error(
|
||||
"failed to deregister sandbox service",
|
||||
"failed to deregister runtime service",
|
||||
metadata: [
|
||||
"id": "\(id)",
|
||||
"error": "\(error)",
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
public enum SandboxRoutes: String {
|
||||
/// Create an xpc endpoint to the sandbox instance.
|
||||
case createEndpoint = "com.apple.container.sandbox/createEndpoint"
|
||||
/// Bootstrap the sandbox instance and create the init process.
|
||||
case bootstrap = "com.apple.container.sandbox/bootstrap"
|
||||
/// Create a process in the sandbox.
|
||||
case createProcess = "com.apple.container.sandbox/createProcess"
|
||||
/// Start a process in the sandbox.
|
||||
case start = "com.apple.container.sandbox/start"
|
||||
/// Stop the sandbox.
|
||||
case stop = "com.apple.container.sandbox/stop"
|
||||
/// Return the current state of the sandbox.
|
||||
case state = "com.apple.container.sandbox/state"
|
||||
/// Kill a process in the sandbox.
|
||||
case kill = "com.apple.container.sandbox/kill"
|
||||
/// Resize the pty of a process in the sandbox.
|
||||
case resize = "com.apple.container.sandbox/resize"
|
||||
/// Wait on a process in the sandbox.
|
||||
case wait = "com.apple.container.sandbox/wait"
|
||||
/// Execute a new process in the sandbox.
|
||||
case exec = "com.apple.container.sandbox/exec"
|
||||
/// Dial a vsock port in the sandbox.
|
||||
case dial = "com.apple.container.sandbox/dial"
|
||||
/// Shutdown the sandbox service process.
|
||||
case shutdown = "com.apple.container.sandbox/shutdown"
|
||||
/// Get statistics for the sandbox.
|
||||
case statistics = "com.apple.container.sandbox/statistics"
|
||||
/// Copy a file or directory into the container.
|
||||
case copyIn = "com.apple.container.sandbox/copyIn"
|
||||
/// Copy a file or directory out of the container.
|
||||
case copyOut = "com.apple.container.sandbox/copyOut"
|
||||
}
|
||||
+51
-51
@@ -23,8 +23,8 @@ import ContainerizationOS
|
||||
import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
/// A client for interacting with a single sandbox.
|
||||
public struct SandboxClient: Sendable {
|
||||
/// A client for interacting with a container runtime service instance.
|
||||
public struct RuntimeClient: Sendable {
|
||||
static let label = "com.apple.container.runtime"
|
||||
|
||||
public static func machServiceLabel(runtime: String, id: String) -> String {
|
||||
@@ -45,12 +45,12 @@ public struct SandboxClient: Sendable {
|
||||
self.client = client
|
||||
}
|
||||
|
||||
/// Create a SandboxClient by ID and runtime string. The returned client is ready to be used
|
||||
/// Create a RuntimeClient by ID and runtime string. The returned client is ready to be used
|
||||
/// without additional steps.
|
||||
public static func create(id: String, runtime: String, timeout: Duration = XPCClient.xpcRegistrationTimeout) async throws -> SandboxClient {
|
||||
public static func create(id: String, runtime: String, timeout: Duration = XPCClient.xpcRegistrationTimeout) async throws -> RuntimeClient {
|
||||
let label = Self.machServiceLabel(runtime: runtime, id: id)
|
||||
let client = XPCClient(service: label)
|
||||
let request = XPCMessage(route: SandboxRoutes.createEndpoint.rawValue)
|
||||
let request = XPCMessage(route: RuntimeRoutes.createEndpoint.rawValue)
|
||||
|
||||
let response: XPCMessage
|
||||
do {
|
||||
@@ -62,30 +62,30 @@ public struct SandboxClient: Sendable {
|
||||
cause: error
|
||||
)
|
||||
}
|
||||
guard let endpoint = response.endpoint(key: SandboxKeys.sandboxServiceEndpoint.rawValue) else {
|
||||
guard let endpoint = response.endpoint(key: RuntimeKeys.runtimeServiceEndpoint.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.internalError,
|
||||
message: "failed to get endpoint for sandbox service"
|
||||
message: "failed to get endpoint for runtime service"
|
||||
)
|
||||
}
|
||||
|
||||
let endpointConnection = xpc_connection_create_from_endpoint(endpoint)
|
||||
let xpcClient = XPCClient(connection: endpointConnection, label: label)
|
||||
return SandboxClient(id: id, runtime: runtime, client: xpcClient)
|
||||
return RuntimeClient(id: id, runtime: runtime, client: xpcClient)
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime Methods
|
||||
extension SandboxClient {
|
||||
extension RuntimeClient {
|
||||
public func bootstrap(
|
||||
stdio: [FileHandle?],
|
||||
networkBootstrapInfos: [NetworkBootstrapInfo],
|
||||
dynamicEnv: [String: String] = [:]
|
||||
) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.bootstrap.rawValue)
|
||||
let request = XPCMessage(route: RuntimeRoutes.bootstrap.rawValue)
|
||||
|
||||
for (i, h) in stdio.enumerated() {
|
||||
let key: SandboxKeys = try {
|
||||
let key: RuntimeKeys = try {
|
||||
switch i {
|
||||
case 0: .stdin
|
||||
case 1: .stdout
|
||||
@@ -102,10 +102,10 @@ extension SandboxClient {
|
||||
|
||||
do {
|
||||
let dynamicEnv = try JSONEncoder().encode(dynamicEnv)
|
||||
request.set(key: SandboxKeys.dynamicEnv.rawValue, value: dynamicEnv)
|
||||
request.set(key: RuntimeKeys.dynamicEnv.rawValue, value: dynamicEnv)
|
||||
|
||||
let infosData = try JSONEncoder().encode(networkBootstrapInfos)
|
||||
request.set(key: SandboxKeys.networkBootstrapInfos.rawValue, value: infosData)
|
||||
request.set(key: RuntimeKeys.networkBootstrapInfos.rawValue, value: infosData)
|
||||
try await self.client.send(request)
|
||||
} catch {
|
||||
throw ContainerizationError(
|
||||
@@ -117,7 +117,7 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func state() async throws -> SandboxSnapshot {
|
||||
let request = XPCMessage(route: SandboxRoutes.state.rawValue)
|
||||
let request = XPCMessage(route: RuntimeRoutes.state.rawValue)
|
||||
let response: XPCMessage
|
||||
do {
|
||||
response = try await self.client.send(request)
|
||||
@@ -132,13 +132,13 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func createProcess(_ id: String, config: ProcessConfiguration, stdio: [FileHandle?]) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.createProcess.rawValue)
|
||||
request.set(key: SandboxKeys.id.rawValue, value: id)
|
||||
let request = XPCMessage(route: RuntimeRoutes.createProcess.rawValue)
|
||||
request.set(key: RuntimeKeys.id.rawValue, value: id)
|
||||
let data = try JSONEncoder().encode(config)
|
||||
request.set(key: SandboxKeys.processConfig.rawValue, value: data)
|
||||
request.set(key: RuntimeKeys.processConfig.rawValue, value: data)
|
||||
|
||||
for (i, h) in stdio.enumerated() {
|
||||
let key: SandboxKeys = try {
|
||||
let key: RuntimeKeys = try {
|
||||
switch i {
|
||||
case 0: .stdin
|
||||
case 1: .stdout
|
||||
@@ -165,8 +165,8 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func startProcess(_ id: String) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.start.rawValue)
|
||||
request.set(key: SandboxKeys.id.rawValue, value: id)
|
||||
let request = XPCMessage(route: RuntimeRoutes.start.rawValue)
|
||||
request.set(key: RuntimeKeys.id.rawValue, value: id)
|
||||
do {
|
||||
try await self.client.send(request)
|
||||
} catch {
|
||||
@@ -179,10 +179,10 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func stop(options: ContainerStopOptions) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.stop.rawValue)
|
||||
let request = XPCMessage(route: RuntimeRoutes.stop.rawValue)
|
||||
|
||||
let data = try JSONEncoder().encode(options)
|
||||
request.set(key: SandboxKeys.stopOptions.rawValue, value: data)
|
||||
request.set(key: RuntimeKeys.stopOptions.rawValue, value: data)
|
||||
|
||||
do {
|
||||
try await self.client.send(request)
|
||||
@@ -196,9 +196,9 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func kill(_ id: String, signal: Int64) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.kill.rawValue)
|
||||
request.set(key: SandboxKeys.id.rawValue, value: id)
|
||||
request.set(key: SandboxKeys.signal.rawValue, value: signal)
|
||||
let request = XPCMessage(route: RuntimeRoutes.kill.rawValue)
|
||||
request.set(key: RuntimeKeys.id.rawValue, value: id)
|
||||
request.set(key: RuntimeKeys.signal.rawValue, value: signal)
|
||||
|
||||
do {
|
||||
try await self.client.send(request)
|
||||
@@ -212,10 +212,10 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func resize(_ id: String, size: Terminal.Size) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.resize.rawValue)
|
||||
request.set(key: SandboxKeys.id.rawValue, value: id)
|
||||
request.set(key: SandboxKeys.width.rawValue, value: UInt64(size.width))
|
||||
request.set(key: SandboxKeys.height.rawValue, value: UInt64(size.height))
|
||||
let request = XPCMessage(route: RuntimeRoutes.resize.rawValue)
|
||||
request.set(key: RuntimeKeys.id.rawValue, value: id)
|
||||
request.set(key: RuntimeKeys.width.rawValue, value: UInt64(size.width))
|
||||
request.set(key: RuntimeKeys.height.rawValue, value: UInt64(size.height))
|
||||
|
||||
do {
|
||||
try await self.client.send(request)
|
||||
@@ -229,8 +229,8 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func wait(_ id: String) async throws -> ExitStatus {
|
||||
let request = XPCMessage(route: SandboxRoutes.wait.rawValue)
|
||||
request.set(key: SandboxKeys.id.rawValue, value: id)
|
||||
let request = XPCMessage(route: RuntimeRoutes.wait.rawValue)
|
||||
request.set(key: RuntimeKeys.id.rawValue, value: id)
|
||||
|
||||
let response: XPCMessage
|
||||
do {
|
||||
@@ -242,14 +242,14 @@ extension SandboxClient {
|
||||
cause: error
|
||||
)
|
||||
}
|
||||
let code = response.int64(key: SandboxKeys.exitCode.rawValue)
|
||||
let date = response.date(key: SandboxKeys.exitedAt.rawValue)
|
||||
let code = response.int64(key: RuntimeKeys.exitCode.rawValue)
|
||||
let date = response.date(key: RuntimeKeys.exitedAt.rawValue)
|
||||
return ExitStatus(exitCode: Int32(code), exitedAt: date)
|
||||
}
|
||||
|
||||
public func dial(_ port: UInt32) async throws -> FileHandle {
|
||||
let request = XPCMessage(route: SandboxRoutes.dial.rawValue)
|
||||
request.set(key: SandboxKeys.port.rawValue, value: UInt64(port))
|
||||
let request = XPCMessage(route: RuntimeRoutes.dial.rawValue)
|
||||
request.set(key: RuntimeKeys.port.rawValue, value: UInt64(port))
|
||||
|
||||
let response: XPCMessage
|
||||
do {
|
||||
@@ -261,7 +261,7 @@ extension SandboxClient {
|
||||
cause: error
|
||||
)
|
||||
}
|
||||
guard let fh = response.fileHandle(key: SandboxKeys.fd.rawValue) else {
|
||||
guard let fh = response.fileHandle(key: RuntimeKeys.fd.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.internalError,
|
||||
message: "failed to get fd for vsock port \(port)"
|
||||
@@ -271,7 +271,7 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func shutdown() async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.shutdown.rawValue)
|
||||
let request = XPCMessage(route: RuntimeRoutes.shutdown.rawValue)
|
||||
|
||||
do {
|
||||
_ = try await self.client.send(request)
|
||||
@@ -285,11 +285,11 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func copyIn(source: String, destination: String, mode: UInt32, createParents: Bool = true) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.copyIn.rawValue)
|
||||
request.set(key: SandboxKeys.sourcePath.rawValue, value: source)
|
||||
request.set(key: SandboxKeys.destinationPath.rawValue, value: destination)
|
||||
request.set(key: SandboxKeys.fileMode.rawValue, value: UInt64(mode))
|
||||
request.set(key: SandboxKeys.createParents.rawValue, value: createParents)
|
||||
let request = XPCMessage(route: RuntimeRoutes.copyIn.rawValue)
|
||||
request.set(key: RuntimeKeys.sourcePath.rawValue, value: source)
|
||||
request.set(key: RuntimeKeys.destinationPath.rawValue, value: destination)
|
||||
request.set(key: RuntimeKeys.fileMode.rawValue, value: UInt64(mode))
|
||||
request.set(key: RuntimeKeys.createParents.rawValue, value: createParents)
|
||||
|
||||
do {
|
||||
try await self.client.send(request, responseTimeout: .seconds(300))
|
||||
@@ -303,10 +303,10 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func copyOut(source: String, destination: String, createParents: Bool = true) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.copyOut.rawValue)
|
||||
request.set(key: SandboxKeys.sourcePath.rawValue, value: source)
|
||||
request.set(key: SandboxKeys.destinationPath.rawValue, value: destination)
|
||||
request.set(key: SandboxKeys.createParents.rawValue, value: createParents)
|
||||
let request = XPCMessage(route: RuntimeRoutes.copyOut.rawValue)
|
||||
request.set(key: RuntimeKeys.sourcePath.rawValue, value: source)
|
||||
request.set(key: RuntimeKeys.destinationPath.rawValue, value: destination)
|
||||
request.set(key: RuntimeKeys.createParents.rawValue, value: createParents)
|
||||
|
||||
do {
|
||||
try await self.client.send(request, responseTimeout: .seconds(300))
|
||||
@@ -320,7 +320,7 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
public func statistics() async throws -> ContainerStats {
|
||||
let request = XPCMessage(route: SandboxRoutes.statistics.rawValue)
|
||||
let request = XPCMessage(route: RuntimeRoutes.statistics.rawValue)
|
||||
|
||||
let response: XPCMessage
|
||||
do {
|
||||
@@ -333,7 +333,7 @@ extension SandboxClient {
|
||||
)
|
||||
}
|
||||
|
||||
guard let data = response.dataNoCopy(key: SandboxKeys.statistics.rawValue) else {
|
||||
guard let data = response.dataNoCopy(key: RuntimeKeys.statistics.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.internalError,
|
||||
message: "no statistics data returned"
|
||||
@@ -346,7 +346,7 @@ extension SandboxClient {
|
||||
|
||||
extension XPCMessage {
|
||||
public func id() throws -> String {
|
||||
let id = self.string(key: SandboxKeys.id.rawValue)
|
||||
let id = self.string(key: RuntimeKeys.id.rawValue)
|
||||
guard let id else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
@@ -357,7 +357,7 @@ extension XPCMessage {
|
||||
}
|
||||
|
||||
func sandboxSnapshot() throws -> SandboxSnapshot {
|
||||
let data = self.dataNoCopy(key: SandboxKeys.snapshot.rawValue)
|
||||
let data = self.dataNoCopy(key: RuntimeKeys.snapshot.rawValue)
|
||||
guard let data else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
@@ -368,7 +368,7 @@ extension XPCMessage {
|
||||
}
|
||||
|
||||
public func networkBootstrapInfos() throws -> [NetworkBootstrapInfo] {
|
||||
guard let data = self.dataNoCopy(key: SandboxKeys.networkBootstrapInfos.rawValue) else {
|
||||
guard let data = self.dataNoCopy(key: RuntimeKeys.networkBootstrapInfos.rawValue) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "missing networkBootstrapInfos in bootstrap message")
|
||||
}
|
||||
return try JSONDecoder().decode([NetworkBootstrapInfo].self, from: data)
|
||||
+6
-3
@@ -23,6 +23,8 @@ public struct RuntimeConfiguration: Codable, Sendable {
|
||||
static let runtimeConfigurationFilename = "runtime-configuration.json"
|
||||
|
||||
public let path: URL
|
||||
// TODO: Remove runtime-specific fields (initialFilesystem, kernel, containerRootFilesystem).
|
||||
// These should be encoded into the opaque `runtimeData` field by the CLI.
|
||||
public let initialFilesystem: Filesystem
|
||||
public let kernel: Kernel
|
||||
public let containerConfiguration: ContainerConfiguration?
|
||||
@@ -63,14 +65,15 @@ public struct RuntimeConfiguration: Codable, Sendable {
|
||||
|
||||
public static func readRuntimeConfiguration(from runtimeConfigurationPath: URL) throws -> RuntimeConfiguration {
|
||||
let configurationPath = runtimeConfigurationPath.appendingPathComponent(RuntimeConfiguration.runtimeConfigurationFilename)
|
||||
guard FileManager.default.fileExists(atPath: configurationPath.path) else {
|
||||
let data: Data
|
||||
do {
|
||||
data = try Data(contentsOf: configurationPath)
|
||||
} catch {
|
||||
throw ContainerizationError(
|
||||
.notFound,
|
||||
message: "runtime configuration file not found at path: \(configurationPath.path)"
|
||||
)
|
||||
}
|
||||
|
||||
let data = try Data(contentsOf: configurationPath)
|
||||
return try JSONDecoder().decode(RuntimeConfiguration.self, from: data)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
public enum SandboxKeys: String {
|
||||
public enum RuntimeKeys: String {
|
||||
/// ID key.
|
||||
case id
|
||||
/// Vsock port number key.
|
||||
@@ -27,8 +27,8 @@ public enum SandboxKeys: String {
|
||||
case fd
|
||||
/// Options for stopping a container key.
|
||||
case stopOptions
|
||||
/// An endpoint to talk to a sandbox service.
|
||||
case sandboxServiceEndpoint
|
||||
/// An endpoint to talk to the runtime service.
|
||||
case runtimeServiceEndpoint
|
||||
|
||||
/// Process request keys.
|
||||
case signal
|
||||
@@ -0,0 +1,59 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// XPC routes exposed by the runtime service.
|
||||
public enum RuntimeRoutes: String {
|
||||
// MARK: - Service lifecycle
|
||||
|
||||
/// Create an XPC endpoint for communicating with the runtime service.
|
||||
case createEndpoint = "com.apple.container.runtime/createEndpoint"
|
||||
/// Shut down the runtime service process. Requires the sandbox to be stopped first.
|
||||
case shutdown = "com.apple.container.runtime/shutdown"
|
||||
|
||||
// MARK: - Sandbox lifecycle
|
||||
|
||||
/// Bootstrap the sandbox: create the VM, configure networks, and boot the guest.
|
||||
case bootstrap = "com.apple.container.runtime/bootstrap"
|
||||
/// Stop the sandbox and all processes running inside it.
|
||||
case stop = "com.apple.container.runtime/stop"
|
||||
/// Return the current state of the sandbox.
|
||||
case state = "com.apple.container.runtime/state"
|
||||
/// Get resource usage statistics for the sandbox.
|
||||
case statistics = "com.apple.container.runtime/statistics"
|
||||
/// Open a vsock connection to a port inside the sandbox.
|
||||
case dial = "com.apple.container.runtime/dial"
|
||||
|
||||
// MARK: - Process management
|
||||
|
||||
/// Register a new process inside the sandbox (used by exec).
|
||||
case createProcess = "com.apple.container.runtime/createProcess"
|
||||
/// Start a registered process inside the sandbox.
|
||||
case start = "com.apple.container.runtime/start"
|
||||
/// Send a signal to a process inside the sandbox.
|
||||
case kill = "com.apple.container.runtime/kill"
|
||||
/// Resize the PTY of a process inside the sandbox.
|
||||
case resize = "com.apple.container.runtime/resize"
|
||||
/// Wait for a process inside the sandbox to exit.
|
||||
case wait = "com.apple.container.runtime/wait"
|
||||
/// Execute a new process in the sandbox.
|
||||
case exec = "com.apple.container.runtime/exec"
|
||||
|
||||
// MARK: - File Management
|
||||
/// Copy a file or directory into the container.
|
||||
case copyIn = "com.apple.container.runtime/copyIn"
|
||||
/// Copy a file or directory out of the container.
|
||||
case copyOut = "com.apple.container.runtime/copyOut"
|
||||
}
|
||||
+4
-2
@@ -15,14 +15,16 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerResource
|
||||
import ContainerSandboxService
|
||||
import ContainerRuntimeClient
|
||||
import ContainerXPC
|
||||
import Containerization
|
||||
|
||||
/// Isolated container network interface strategy. This strategy prohibits
|
||||
/// container to container networking, but it is the only approach that
|
||||
/// works for macOS Sequoia.
|
||||
struct IsolatedInterfaceStrategy: InterfaceStrategy {
|
||||
public struct IsolatedInterfaceStrategy: InterfaceStrategy {
|
||||
public init() {}
|
||||
|
||||
public func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) -> Interface {
|
||||
let ipv4Gateway = interfaceIndex == 0 ? attachment.ipv4Gateway : nil
|
||||
return NATInterface(
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerResource
|
||||
import ContainerSandboxService
|
||||
import ContainerRuntimeClient
|
||||
import ContainerXPC
|
||||
import Containerization
|
||||
import ContainerizationError
|
||||
@@ -25,7 +25,7 @@ import vmnet
|
||||
|
||||
/// Interface strategy for containers that use macOS's custom network feature.
|
||||
@available(macOS 26, *)
|
||||
struct NonisolatedInterfaceStrategy: InterfaceStrategy {
|
||||
public struct NonisolatedInterfaceStrategy: InterfaceStrategy {
|
||||
private let log: Logger
|
||||
|
||||
public init(log: Logger) {
|
||||
+31
-33
@@ -18,7 +18,7 @@ import ContainerNetworkServiceClient
|
||||
import ContainerOS
|
||||
import ContainerPersistence
|
||||
import ContainerResource
|
||||
import ContainerSandboxServiceClient
|
||||
import ContainerRuntimeClient
|
||||
import ContainerXPC
|
||||
import Containerization
|
||||
import ContainerizationError
|
||||
@@ -37,7 +37,7 @@ import struct ContainerizationOCI.Mount
|
||||
import struct ContainerizationOCI.Process
|
||||
|
||||
/// An XPC service that manages the lifecycle of a single VM-backed container.
|
||||
public actor SandboxService {
|
||||
public actor RuntimeService {
|
||||
private let connection: xpc_connection_t
|
||||
private let root: URL
|
||||
private let interfaceStrategies: [NetworkPluginInfo: InterfaceStrategy]
|
||||
@@ -116,7 +116,7 @@ public actor SandboxService {
|
||||
///
|
||||
/// - Returns: An XPC message with the following parameters:
|
||||
/// - endpoint: An XPC endpoint that can be used to communicate
|
||||
/// with the sandbox service.
|
||||
/// with the runtime service.
|
||||
@Sendable
|
||||
public func createEndpoint(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
self.log.debug("enter", metadata: ["func": "\(#function)"])
|
||||
@@ -124,7 +124,7 @@ public actor SandboxService {
|
||||
|
||||
let endpoint = xpc_endpoint_create(self.connection)
|
||||
let reply = message.reply()
|
||||
reply.set(key: SandboxKeys.sandboxServiceEndpoint.rawValue, value: endpoint)
|
||||
reply.set(key: RuntimeKeys.runtimeServiceEndpoint.rawValue, value: endpoint)
|
||||
return reply
|
||||
}
|
||||
|
||||
@@ -139,8 +139,6 @@ public actor SandboxService {
|
||||
self.log.debug("enter", metadata: ["func": "\(#function)"])
|
||||
defer { self.log.debug("exit", metadata: ["func": "\(#function)"]) }
|
||||
|
||||
// Create the bundle if it doesn't exist yet
|
||||
|
||||
// Create the bundle if it doesn't exist yet
|
||||
if !self.bundleExists(at: self.root) {
|
||||
try self.createBundle()
|
||||
@@ -365,12 +363,12 @@ public actor SandboxService {
|
||||
|
||||
let reply = message.reply()
|
||||
let data = try JSONEncoder().encode(containerStats)
|
||||
reply.set(key: SandboxKeys.statistics.rawValue, value: data)
|
||||
reply.set(key: RuntimeKeys.statistics.rawValue, value: data)
|
||||
return reply
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown the SandboxService.
|
||||
/// Shutdown the RuntimeService.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: An XPC message with no parameters.
|
||||
@@ -602,8 +600,8 @@ public actor SandboxService {
|
||||
case .running:
|
||||
let id = try message.id()
|
||||
let ctr = try getContainer()
|
||||
let width = message.uint64(key: SandboxKeys.width.rawValue)
|
||||
let height = message.uint64(key: SandboxKeys.height.rawValue)
|
||||
let width = message.uint64(key: RuntimeKeys.width.rawValue)
|
||||
let height = message.uint64(key: RuntimeKeys.height.rawValue)
|
||||
|
||||
if id != ctr.container.id {
|
||||
guard let processInfo = self.processes[id] else {
|
||||
@@ -655,7 +653,7 @@ public actor SandboxService {
|
||||
self.log.debug("enter", metadata: ["func": "\(#function)"])
|
||||
defer { self.log.debug("exit", metadata: ["func": "\(#function)"]) }
|
||||
|
||||
guard let id = message.string(key: SandboxKeys.id.rawValue) else {
|
||||
guard let id = message.string(key: RuntimeKeys.id.rawValue) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "missing id in wait xpc message")
|
||||
}
|
||||
|
||||
@@ -663,8 +661,8 @@ public actor SandboxService {
|
||||
self.waitForExit(id: id, cont: cc)
|
||||
}
|
||||
let reply = message.reply()
|
||||
reply.set(key: SandboxKeys.exitCode.rawValue, value: Int64(exitStatus.exitCode))
|
||||
reply.set(key: SandboxKeys.exitedAt.rawValue, value: exitStatus.exitedAt)
|
||||
reply.set(key: RuntimeKeys.exitCode.rawValue, value: Int64(exitStatus.exitCode))
|
||||
reply.set(key: RuntimeKeys.exitedAt.rawValue, value: exitStatus.exitedAt)
|
||||
return reply
|
||||
}
|
||||
|
||||
@@ -682,20 +680,20 @@ public actor SandboxService {
|
||||
self.log.info("`copyIn` xpc handler")
|
||||
switch self.state {
|
||||
case .running, .booted:
|
||||
guard let source = message.string(key: SandboxKeys.sourcePath.rawValue) else {
|
||||
guard let source = message.string(key: RuntimeKeys.sourcePath.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
message: "no source path supplied for copyIn"
|
||||
)
|
||||
}
|
||||
guard let destination = message.string(key: SandboxKeys.destinationPath.rawValue) else {
|
||||
guard let destination = message.string(key: RuntimeKeys.destinationPath.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
message: "no destination path supplied for copyIn"
|
||||
)
|
||||
}
|
||||
let mode = UInt32(message.uint64(key: SandboxKeys.fileMode.rawValue))
|
||||
let createParents = message.bool(key: SandboxKeys.createParents.rawValue)
|
||||
let mode = UInt32(message.uint64(key: RuntimeKeys.fileMode.rawValue))
|
||||
let createParents = message.bool(key: RuntimeKeys.createParents.rawValue)
|
||||
|
||||
let ctr = try getContainer()
|
||||
try await ctr.container.copyIn(
|
||||
@@ -727,20 +725,20 @@ public actor SandboxService {
|
||||
self.log.info("`copyOut` xpc handler")
|
||||
switch self.state {
|
||||
case .running, .booted:
|
||||
guard let source = message.string(key: SandboxKeys.sourcePath.rawValue) else {
|
||||
guard let source = message.string(key: RuntimeKeys.sourcePath.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
message: "no source path supplied for copyOut"
|
||||
)
|
||||
}
|
||||
guard let destination = message.string(key: SandboxKeys.destinationPath.rawValue) else {
|
||||
guard let destination = message.string(key: RuntimeKeys.destinationPath.rawValue) else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
message: "no destination path supplied for copyOut"
|
||||
)
|
||||
}
|
||||
|
||||
let createParents = message.bool(key: SandboxKeys.createParents.rawValue)
|
||||
let createParents = message.bool(key: RuntimeKeys.createParents.rawValue)
|
||||
|
||||
let ctr = try getContainer()
|
||||
try await ctr.container.copyOut(
|
||||
@@ -773,7 +771,7 @@ public actor SandboxService {
|
||||
|
||||
switch self.state {
|
||||
case .running, .booted:
|
||||
let port = message.uint64(key: SandboxKeys.port.rawValue)
|
||||
let port = message.uint64(key: RuntimeKeys.port.rawValue)
|
||||
guard port > 0 else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
@@ -785,7 +783,7 @@ public actor SandboxService {
|
||||
let fh = try await ctr.container.dialVsock(port: UInt32(port))
|
||||
|
||||
let reply = message.reply()
|
||||
reply.set(key: SandboxKeys.fd.rawValue, value: fh)
|
||||
reply.set(key: RuntimeKeys.fd.rawValue, value: fh)
|
||||
return reply
|
||||
default:
|
||||
throw ContainerizationError(
|
||||
@@ -1252,11 +1250,11 @@ public actor SandboxService {
|
||||
|
||||
extension XPCMessage {
|
||||
fileprivate func signal() throws -> Int64 {
|
||||
self.int64(key: SandboxKeys.signal.rawValue)
|
||||
self.int64(key: RuntimeKeys.signal.rawValue)
|
||||
}
|
||||
|
||||
fileprivate func stopOptions() throws -> ContainerStopOptions {
|
||||
guard let data = self.dataNoCopy(key: SandboxKeys.stopOptions.rawValue) else {
|
||||
guard let data = self.dataNoCopy(key: RuntimeKeys.stopOptions.rawValue) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "empty StopOptions")
|
||||
}
|
||||
return try JSONDecoder().decode(ContainerStopOptions.self, from: data)
|
||||
@@ -1264,36 +1262,36 @@ extension XPCMessage {
|
||||
|
||||
fileprivate func setState(_ state: SandboxSnapshot) throws {
|
||||
let data = try JSONEncoder().encode(state)
|
||||
self.set(key: SandboxKeys.snapshot.rawValue, value: data)
|
||||
self.set(key: RuntimeKeys.snapshot.rawValue, value: data)
|
||||
}
|
||||
|
||||
fileprivate func stdio() -> [FileHandle?] {
|
||||
var handles = [FileHandle?](repeating: nil, count: 3)
|
||||
if let stdin = self.fileHandle(key: SandboxKeys.stdin.rawValue) {
|
||||
if let stdin = self.fileHandle(key: RuntimeKeys.stdin.rawValue) {
|
||||
handles[0] = stdin
|
||||
}
|
||||
if let stdout = self.fileHandle(key: SandboxKeys.stdout.rawValue) {
|
||||
if let stdout = self.fileHandle(key: RuntimeKeys.stdout.rawValue) {
|
||||
handles[1] = stdout
|
||||
}
|
||||
if let stderr = self.fileHandle(key: SandboxKeys.stderr.rawValue) {
|
||||
if let stderr = self.fileHandle(key: RuntimeKeys.stderr.rawValue) {
|
||||
handles[2] = stderr
|
||||
}
|
||||
return handles
|
||||
}
|
||||
|
||||
fileprivate func setFileHandle(_ handle: FileHandle) {
|
||||
self.set(key: SandboxKeys.fd.rawValue, value: handle)
|
||||
self.set(key: RuntimeKeys.fd.rawValue, value: handle)
|
||||
}
|
||||
|
||||
fileprivate func processConfig() throws -> ProcessConfiguration {
|
||||
guard let data = self.dataNoCopy(key: SandboxKeys.processConfig.rawValue) else {
|
||||
guard let data = self.dataNoCopy(key: RuntimeKeys.processConfig.rawValue) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "empty process configuration")
|
||||
}
|
||||
return try JSONDecoder().decode(ProcessConfiguration.self, from: data)
|
||||
}
|
||||
|
||||
fileprivate func dynamicEnv() throws -> [String: String] {
|
||||
let data = self.dataNoCopy(key: SandboxKeys.dynamicEnv.rawValue)
|
||||
let data = self.dataNoCopy(key: RuntimeKeys.dynamicEnv.rawValue)
|
||||
let dynamicEnv = try data.map { try JSONDecoder().decode([String: String].self, from: $0) } ?? [:]
|
||||
return dynamicEnv
|
||||
}
|
||||
@@ -1428,7 +1426,7 @@ extension FileHandle: @retroactive ReaderStream, @retroactive Writer {
|
||||
|
||||
// MARK: State handler and bundle creation helpers
|
||||
|
||||
extension SandboxService {
|
||||
extension RuntimeService {
|
||||
private func initializeWaiters(for id: String) throws {
|
||||
guard waiters[id] == nil else {
|
||||
throw ContainerizationError(.invalidState, message: "waiter for \(id) already initialized")
|
||||
@@ -1508,7 +1506,7 @@ extension SandboxService {
|
||||
case stopping
|
||||
/// Once a stop is successful, .stopping will transition to .stopped.
|
||||
case stopped
|
||||
/// .shuttingDown will be the last state the sandbox service will ever be in. Shortly
|
||||
/// .shuttingDown will be the last state the runtime service will ever be in. Shortly
|
||||
/// afterwards the process will exit.
|
||||
case shuttingDown
|
||||
}
|
||||
@@ -15,8 +15,8 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerResource
|
||||
import ContainerRuntimeLinuxTypes
|
||||
import ContainerSandboxServiceClient
|
||||
import ContainerRuntimeClient
|
||||
import ContainerRuntimeLinuxClient
|
||||
import Containerization
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@@ -18,8 +18,9 @@ opts+=("--allow-writing-to-directory" "$1")
|
||||
opts+=("generate-documentation")
|
||||
opts+=("--target" "ContainerAPIService")
|
||||
opts+=("--target" "ContainerAPIClient")
|
||||
opts+=("--target" "ContainerSandboxService")
|
||||
opts+=("--target" "ContainerSandboxServiceClient")
|
||||
opts+=("--target" "ContainerRuntimeClient")
|
||||
opts+=("--target" "ContainerRuntimeLinuxClient")
|
||||
opts+=("--target" "ContainerRuntimeLinuxServer")
|
||||
opts+=("--target" "ContainerNetworkService")
|
||||
opts+=("--target" "ContainerNetworkServiceClient")
|
||||
opts+=("--target" "ContainerImagesService")
|
||||
|
||||
Reference in New Issue
Block a user