mirror of
https://github.com/apple/container.git
synced 2026-08-24 10:05:43 -05:00
Makes bootstrap env parameter compatible with older clients. (#1434)
- Closes #1433. - Renames `env` parameter to `dynamicEnv` to differentiate these special-case environment variables from the standard container environment. - Use `[String: String]` instead of `[String: String]?`. - Default to `[:]` when an down-revision client calls bootstrap without supplying `dynamicEnv`.
This commit is contained in:
@@ -326,12 +326,12 @@ private func startBuildKit(
|
||||
)
|
||||
defer { try? io.close() }
|
||||
|
||||
var env: [String: String] = [:]
|
||||
var dynamicEnv: [String: String] = [:]
|
||||
if let sshAuthSock = ProcessInfo.processInfo.environment["SSH_AUTH_SOCK"] {
|
||||
env["SSH_AUTH_SOCK"] = sshAuthSock
|
||||
dynamicEnv["SSH_AUTH_SOCK"] = sshAuthSock
|
||||
}
|
||||
|
||||
let process = try await client.bootstrap(id: id, stdio: io.stdio, env: env)
|
||||
let process = try await client.bootstrap(id: id, stdio: io.stdio, dynamicEnv: dynamicEnv)
|
||||
try await process.start()
|
||||
await taskManager?.finish()
|
||||
try io.closeAfterStart()
|
||||
|
||||
@@ -123,12 +123,12 @@ extension Application {
|
||||
try? io.close()
|
||||
}
|
||||
|
||||
var env: [String: String] = [:]
|
||||
var dynamicEnv: [String: String] = [:]
|
||||
if let sshAuthSock = ProcessInfo.processInfo.environment["SSH_AUTH_SOCK"] {
|
||||
env["SSH_AUTH_SOCK"] = sshAuthSock
|
||||
dynamicEnv["SSH_AUTH_SOCK"] = sshAuthSock
|
||||
}
|
||||
|
||||
let process = try await client.bootstrap(id: id, stdio: io.stdio, env: env)
|
||||
let process = try await client.bootstrap(id: id, stdio: io.stdio, dynamicEnv: dynamicEnv)
|
||||
progress.finish()
|
||||
|
||||
if !self.managementFlags.cidfile.isEmpty {
|
||||
|
||||
@@ -92,7 +92,7 @@ extension Application {
|
||||
env["SSH_AUTH_SOCK"] = sshAuthSock
|
||||
}
|
||||
|
||||
let process = try await client.bootstrap(id: container.id, stdio: io.stdio, env: env)
|
||||
let process = try await client.bootstrap(id: container.id, stdio: io.stdio, dynamicEnv: env)
|
||||
progress.finish()
|
||||
|
||||
if detach {
|
||||
|
||||
@@ -113,7 +113,11 @@ public struct ContainerClient: Sendable {
|
||||
}
|
||||
|
||||
/// Bootstrap the container's init process.
|
||||
public func bootstrap(id: String, stdio: [FileHandle?], env: [String: String]? = nil) async throws -> ClientProcess {
|
||||
public func bootstrap(
|
||||
id: String,
|
||||
stdio: [FileHandle?],
|
||||
dynamicEnv: [String: String] = [:]
|
||||
) async throws -> ClientProcess {
|
||||
let request = XPCMessage(route: .containerBootstrap)
|
||||
|
||||
for (i, h) in stdio.enumerated() {
|
||||
@@ -133,8 +137,8 @@ public struct ContainerClient: Sendable {
|
||||
}
|
||||
|
||||
do {
|
||||
let env = try JSONEncoder().encode(env)
|
||||
request.set(key: .env, value: env)
|
||||
let dynamicEnv = try JSONEncoder().encode(dynamicEnv)
|
||||
request.set(key: .dynamicEnv, value: dynamicEnv)
|
||||
|
||||
request.set(key: .id, value: id)
|
||||
try await xpcClient.send(request)
|
||||
|
||||
@@ -56,8 +56,8 @@ public enum XPCKeys: String {
|
||||
case plugin
|
||||
/// Archive path to export rootfs
|
||||
case archive
|
||||
/// Environment variables passed from terminal
|
||||
case env
|
||||
/// Special-case environment variables recomputed on each container start
|
||||
case dynamicEnv
|
||||
|
||||
/// Health check request.
|
||||
case ping
|
||||
|
||||
@@ -56,15 +56,10 @@ public struct ContainersHarness: Sendable {
|
||||
}
|
||||
let stdio = message.stdio()
|
||||
|
||||
guard let data = message.dataNoCopy(key: .env) else {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
message: "env cannot be empty"
|
||||
)
|
||||
}
|
||||
let env = try JSONDecoder().decode([String: String]?.self, from: data)
|
||||
let data = message.dataNoCopy(key: .dynamicEnv)
|
||||
let env = try data.map { try JSONDecoder().decode([String: String].self, from: $0) } ?? [:]
|
||||
|
||||
try await service.bootstrap(id: id, stdio: stdio, env: env)
|
||||
try await service.bootstrap(id: id, stdio: stdio, dynamicEnv: env)
|
||||
return message.reply()
|
||||
}
|
||||
|
||||
|
||||
@@ -398,13 +398,13 @@ public actor ContainersService {
|
||||
}
|
||||
|
||||
/// Bootstrap the init process of the container.
|
||||
public func bootstrap(id: String, stdio: [FileHandle?], env: [String: String]?) async throws {
|
||||
public func bootstrap(id: String, stdio: [FileHandle?], dynamicEnv: [String: String]) async throws {
|
||||
log.debug(
|
||||
"ContainersService: enter",
|
||||
metadata: [
|
||||
"func": "\(#function)",
|
||||
"id": "\(id)",
|
||||
"env": "\(env ?? [:])",
|
||||
"env": "\(dynamicEnv)",
|
||||
]
|
||||
)
|
||||
defer {
|
||||
@@ -474,7 +474,7 @@ public actor ContainersService {
|
||||
id: id,
|
||||
runtime: runtime
|
||||
)
|
||||
try await sandboxClient.bootstrap(stdio: stdio, allocatedAttachments: allocatedAttachments, env: env)
|
||||
try await sandboxClient.bootstrap(stdio: stdio, allocatedAttachments: allocatedAttachments, dynamicEnv: dynamicEnv)
|
||||
|
||||
try await self.exitMonitor.registerProcess(
|
||||
id: id,
|
||||
|
||||
@@ -77,7 +77,11 @@ public struct SandboxClient: Sendable {
|
||||
|
||||
// Runtime Methods
|
||||
extension SandboxClient {
|
||||
public func bootstrap(stdio: [FileHandle?], allocatedAttachments: [AllocatedAttachment], env: [String: String]? = nil) async throws {
|
||||
public func bootstrap(
|
||||
stdio: [FileHandle?],
|
||||
allocatedAttachments: [AllocatedAttachment],
|
||||
dynamicEnv: [String: String] = [:]
|
||||
) async throws {
|
||||
let request = XPCMessage(route: SandboxRoutes.bootstrap.rawValue)
|
||||
|
||||
for (i, h) in stdio.enumerated() {
|
||||
@@ -97,8 +101,8 @@ extension SandboxClient {
|
||||
}
|
||||
|
||||
do {
|
||||
let env = try JSONEncoder().encode(env)
|
||||
request.set(key: SandboxKeys.env.rawValue, value: env)
|
||||
let dynamicEnv = try JSONEncoder().encode(dynamicEnv)
|
||||
request.set(key: SandboxKeys.dynamicEnv.rawValue, value: dynamicEnv)
|
||||
|
||||
try request.setAllocatedAttachments(allocatedAttachments)
|
||||
try await self.client.send(request)
|
||||
|
||||
@@ -43,8 +43,8 @@ public enum SandboxKeys: String {
|
||||
/// Container statistics
|
||||
case statistics
|
||||
|
||||
/// Environment variables passed from terminal.
|
||||
case env
|
||||
/// Special-case environment variables recomputed on each container start
|
||||
case dynamicEnv
|
||||
|
||||
/// Network resource keys.
|
||||
case allocatedAttachments
|
||||
|
||||
@@ -76,12 +76,16 @@ public actor SandboxService {
|
||||
}
|
||||
}
|
||||
|
||||
private static func sshAuthSocketHostUrl(config: ContainerConfiguration, hostEnv: [String: String]?, log: Logger? = nil) -> URL? {
|
||||
private static func sshAuthSocketHostUrl(
|
||||
config: ContainerConfiguration,
|
||||
dynamicEnv: [String: String] = [:],
|
||||
log: Logger? = nil
|
||||
) -> URL? {
|
||||
guard config.ssh else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let sshSocket = hostEnv?[Self.sshAuthSocketEnvVar] else {
|
||||
guard let sshSocket = dynamicEnv[Self.sshAuthSocketEnvVar] else {
|
||||
log?.warning("ssh forwarding requested but no \(Self.sshAuthSocketEnvVar) found")
|
||||
return nil
|
||||
}
|
||||
@@ -147,7 +151,7 @@ public actor SandboxService {
|
||||
)
|
||||
}
|
||||
|
||||
let env = try message.env()
|
||||
let dynamicEnv = try message.dynamicEnv()
|
||||
|
||||
let bundle = ContainerResource.Bundle(path: self.root)
|
||||
try bundle.createLogFile()
|
||||
@@ -224,7 +228,7 @@ public actor SandboxService {
|
||||
let id = config.id
|
||||
let rootfs = try bundle.containerRootfs.asMount
|
||||
let container = try LinuxContainer(id, rootfs: rootfs, vmm: vmm, logger: self.log) { czConfig in
|
||||
try Self.configureContainer(czConfig: &czConfig, config: config, hostEnv: env, log: self.log)
|
||||
try Self.configureContainer(czConfig: &czConfig, config: config, dynamicEnv: dynamicEnv, log: self.log)
|
||||
czConfig.interfaces = interfaces
|
||||
czConfig.process.stdout = stdout
|
||||
czConfig.process.stderr = stderr
|
||||
@@ -846,7 +850,7 @@ public actor SandboxService {
|
||||
private static func configureContainer(
|
||||
czConfig: inout LinuxContainer.Configuration,
|
||||
config: ContainerConfiguration,
|
||||
hostEnv: [String: String]?,
|
||||
dynamicEnv: [String: String] = [:],
|
||||
log: Logger? = nil,
|
||||
) throws {
|
||||
czConfig.cpus = config.resources.cpus
|
||||
@@ -880,7 +884,7 @@ public actor SandboxService {
|
||||
czConfig.sockets.append(socketConfig)
|
||||
}
|
||||
|
||||
if let socketUrl = Self.sshAuthSocketHostUrl(config: config, hostEnv: hostEnv, log: log) {
|
||||
if let socketUrl = Self.sshAuthSocketHostUrl(config: config, dynamicEnv: dynamicEnv, log: log) {
|
||||
let socketPath = socketUrl.path(percentEncoded: false)
|
||||
let attrs = try? FileManager.default.attributesOfItem(atPath: socketPath)
|
||||
let permissions = (attrs?[.posixPermissions] as? NSNumber)
|
||||
@@ -1165,11 +1169,10 @@ extension XPCMessage {
|
||||
return try JSONDecoder().decode(ProcessConfiguration.self, from: data)
|
||||
}
|
||||
|
||||
fileprivate func env() throws -> [String: String]? {
|
||||
guard let data = self.dataNoCopy(key: SandboxKeys.env.rawValue) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "empty env")
|
||||
}
|
||||
return try JSONDecoder().decode([String: String]?.self, from: data)
|
||||
fileprivate func dynamicEnv() throws -> [String: String] {
|
||||
let data = self.dataNoCopy(key: SandboxKeys.dynamicEnv.rawValue)
|
||||
let dynamicEnv = try data.map { try JSONDecoder().decode([String: String].self, from: $0) } ?? [:]
|
||||
return dynamicEnv
|
||||
}
|
||||
|
||||
fileprivate func getAllocatedAttachments() throws -> [AllocatedAttachment] {
|
||||
|
||||
Reference in New Issue
Block a user