mirror of
https://github.com/apple/container.git
synced 2026-08-24 10:05:43 -05:00
Add variant Support (#1548)
- This change adds two types to support opaque runtime data passing through the APIServer: - RuntimeConfiguration `data` - RuntimeLinuxData type - LinuxRuntimeData defines runtime specific information. With this change, it will support `variant` only. The optional RuntimeConfiguration `data` field encodes runtime specific data to pass through the APIServer. It is decoded as needed by the runtime. - The idea is to eventually move all runtime specific data into the `data` field so that the APIServer is only aware of generic container information.
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "b606e718aa690122c644e34349c372f37b67ae1641c1f4e6216fad7aad555dc9",
|
||||
"originHash" : "22a1e1b6903f45f7d9b7dc7940fd328f427c4123c73d6985eab99ae6c21523c4",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
@@ -105,8 +105,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "6675bc0ff86e61436e615df6fc5174e043e57924",
|
||||
"version" : "1.4.1"
|
||||
"revision" : "03cc312c2c933ed87abace34044a5dff7a3117c1",
|
||||
"version" : "1.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+9
-1
@@ -108,6 +108,7 @@ let package = Package(
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
"ContainerRuntimeLinuxTypes",
|
||||
"ContainerVersion",
|
||||
"ContainerXPC",
|
||||
"TerminalProgress",
|
||||
@@ -199,6 +200,7 @@ let package = Package(
|
||||
dependencies: [
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
"ContainerResource",
|
||||
"ContainerRuntimeLinuxTypes",
|
||||
"ContainerSandboxServiceClient",
|
||||
]
|
||||
),
|
||||
@@ -333,6 +335,11 @@ let package = Package(
|
||||
],
|
||||
path: "Sources/Services/ContainerNetworkService/Client"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerRuntimeLinuxTypes",
|
||||
dependencies: [],
|
||||
path: "Sources/Plugins/RuntimeLinux/Types"
|
||||
),
|
||||
.executableTarget(
|
||||
name: "container-runtime-linux",
|
||||
dependencies: [
|
||||
@@ -342,13 +349,14 @@ let package = Package(
|
||||
"ContainerLog",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
"ContainerRuntimeLinuxTypes",
|
||||
"ContainerSandboxService",
|
||||
"ContainerSandboxServiceClient",
|
||||
"ContainerVersion",
|
||||
"ContainerXPC",
|
||||
],
|
||||
path: "Sources/Plugins/RuntimeLinux",
|
||||
exclude: ["config.toml"]
|
||||
exclude: ["config.toml", "Types"]
|
||||
),
|
||||
.target(
|
||||
name: "ContainerSandboxService",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Linux-specific runtime data passed through the opaque runtimeData field
|
||||
/// in RuntimeConfiguration. Encoded by the CLI, decoded by the Linux runtime.
|
||||
public struct LinuxRuntimeData: Codable, Sendable {
|
||||
public let variant: String?
|
||||
|
||||
public init(variant: String? = nil) {
|
||||
self.variant = variant
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@ public struct ContainerClient: Sendable {
|
||||
configuration: ContainerConfiguration,
|
||||
options: ContainerCreateOptions = .default,
|
||||
kernel: Kernel,
|
||||
initImage: String? = nil
|
||||
initImage: String? = nil,
|
||||
runtimeData: Data? = nil
|
||||
) async throws {
|
||||
do {
|
||||
let request = XPCMessage(route: .containerCreate)
|
||||
@@ -65,6 +66,10 @@ public struct ContainerClient: Sendable {
|
||||
request.set(key: .initImage, value: initImage)
|
||||
}
|
||||
|
||||
if let runtimeData {
|
||||
request.set(key: .runtimeData, value: runtimeData)
|
||||
}
|
||||
|
||||
try await xpcSend(message: request)
|
||||
} catch {
|
||||
throw ContainerizationError(
|
||||
|
||||
@@ -32,6 +32,8 @@ public enum XPCKeys: String {
|
||||
case containerConfig
|
||||
/// Container options key.
|
||||
case containerOptions
|
||||
/// Opaque runtime-specific data.
|
||||
case runtimeData
|
||||
/// Vsock port number key.
|
||||
case port
|
||||
/// Exit code for a process
|
||||
|
||||
@@ -196,8 +196,9 @@ public struct ContainersHarness: Sendable {
|
||||
let kernel = try JSONDecoder().decode(Kernel.self, from: kdata)
|
||||
|
||||
let initImage = message.string(key: .initImage)
|
||||
let runtimeData = message.dataNoCopy(key: .runtimeData)
|
||||
|
||||
try await service.create(configuration: config, kernel: kernel, options: options, initImage: initImage)
|
||||
try await service.create(configuration: config, kernel: kernel, options: options, initImage: initImage, runtimeData: runtimeData)
|
||||
return message.reply()
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public actor ContainersService {
|
||||
}
|
||||
|
||||
/// Create a new container from the provided id and configuration.
|
||||
public func create(configuration: ContainerConfiguration, kernel: Kernel, options: ContainerCreateOptions, initImage: String? = nil) async throws {
|
||||
public func create(configuration: ContainerConfiguration, kernel: Kernel, options: ContainerCreateOptions, initImage: String? = nil, runtimeData: Data? = nil) async throws {
|
||||
log.debug(
|
||||
"ContainersService: enter",
|
||||
metadata: [
|
||||
@@ -381,7 +381,8 @@ public actor ContainersService {
|
||||
kernel: kernel,
|
||||
containerConfiguration: configuration,
|
||||
containerRootFilesystem: imageFs,
|
||||
options: options
|
||||
options: options,
|
||||
runtimeData: runtimeData
|
||||
)
|
||||
|
||||
try runtimeConfig.writeRuntimeConfiguration()
|
||||
|
||||
@@ -28,6 +28,7 @@ public struct RuntimeConfiguration: Codable, Sendable {
|
||||
public let containerConfiguration: ContainerConfiguration?
|
||||
public let containerRootFilesystem: Filesystem?
|
||||
public let options: ContainerCreateOptions?
|
||||
public let runtimeData: Data?
|
||||
|
||||
public init(
|
||||
path: URL,
|
||||
@@ -35,7 +36,8 @@ public struct RuntimeConfiguration: Codable, Sendable {
|
||||
kernel: Kernel,
|
||||
containerConfiguration: ContainerConfiguration? = nil,
|
||||
containerRootFilesystem: Filesystem? = nil,
|
||||
options: ContainerCreateOptions? = nil
|
||||
options: ContainerCreateOptions? = nil,
|
||||
runtimeData: Data? = nil
|
||||
) {
|
||||
self.path = path
|
||||
self.initialFilesystem = initialFilesystem
|
||||
@@ -43,6 +45,7 @@ public struct RuntimeConfiguration: Codable, Sendable {
|
||||
self.containerConfiguration = containerConfiguration
|
||||
self.containerRootFilesystem = containerRootFilesystem
|
||||
self.options = options
|
||||
self.runtimeData = runtimeData
|
||||
}
|
||||
|
||||
public var runtimeConfigurationPath: URL {
|
||||
|
||||
@@ -139,6 +139,8 @@ 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()
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// import ContainerAPIService
|
||||
import ContainerResource
|
||||
import ContainerRuntimeLinuxTypes
|
||||
import ContainerSandboxServiceClient
|
||||
import Containerization
|
||||
// import ContainerizationOCI
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@@ -72,10 +71,6 @@ struct RuntimeConfigurationTests {
|
||||
|
||||
try runtimeConfig.writeRuntimeConfiguration()
|
||||
|
||||
defer {
|
||||
try? FileManager.default.removeItem(at: runtimeConfig.runtimeConfigurationPath)
|
||||
}
|
||||
|
||||
let readRuntimeConfig = try RuntimeConfiguration.readRuntimeConfiguration(from: bundlePath)
|
||||
|
||||
#expect(
|
||||
@@ -97,4 +92,44 @@ struct RuntimeConfigurationTests {
|
||||
readRuntimeConfig.options == nil,
|
||||
"Options should be nil")
|
||||
}
|
||||
|
||||
@Test
|
||||
func testRuntimeConfigurationWithVariant() throws {
|
||||
let tempDir = FileManager.default.temporaryDirectory
|
||||
let bundlePath = tempDir.appendingPathComponent("test-bundle-\(UUID())")
|
||||
|
||||
defer {
|
||||
try? FileManager.default.removeItem(at: bundlePath)
|
||||
}
|
||||
|
||||
let initFs = Filesystem.virtiofs(
|
||||
source: "/path/to/initfs",
|
||||
destination: "/",
|
||||
options: ["ro"]
|
||||
)
|
||||
|
||||
let kernel = Kernel(
|
||||
path: URL(fileURLWithPath: "/path/to/kernel"),
|
||||
platform: .linuxArm
|
||||
)
|
||||
|
||||
let linuxData = LinuxRuntimeData(variant: "test-variant")
|
||||
let encodedData = try JSONEncoder().encode(linuxData)
|
||||
|
||||
let runtimeConfig = RuntimeConfiguration(
|
||||
path: bundlePath,
|
||||
initialFilesystem: initFs,
|
||||
kernel: kernel,
|
||||
runtimeData: encodedData
|
||||
)
|
||||
|
||||
try runtimeConfig.writeRuntimeConfiguration()
|
||||
|
||||
let readRuntimeConfig = try RuntimeConfiguration.readRuntimeConfiguration(from: bundlePath)
|
||||
|
||||
#expect(readRuntimeConfig.runtimeData != nil, "runtimeData should be persisted")
|
||||
|
||||
let decodedData = try JSONDecoder().decode(LinuxRuntimeData.self, from: readRuntimeConfig.runtimeData!)
|
||||
#expect(decodedData.variant == "test-variant", "Variant should round-trip through RuntimeConfiguration")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user