diff --git a/Sources/Containerization/LinuxContainer.swift b/Sources/Containerization/LinuxContainer.swift index 88f51f59..ecc287aa 100644 --- a/Sources/Containerization/LinuxContainer.swift +++ b/Sources/Containerization/LinuxContainer.swift @@ -64,6 +64,34 @@ public final class LinuxContainer: Container, Sendable { public var bootlog: URL? public init() {} + + public init( + process: LinuxProcessConfiguration, + cpus: Int = 4, + memoryInBytes: UInt64 = 1024.mib(), + hostname: String = "", + sysctl: [String: String] = [:], + interfaces: [any Interface] = [], + sockets: [UnixSocketConfiguration] = [], + mounts: [Mount] = LinuxContainer.defaultMounts(), + dns: DNS? = nil, + hosts: Hosts? = nil, + virtualization: Bool = false, + bootlog: URL? = nil + ) { + self.process = process + self.cpus = cpus + self.memoryInBytes = memoryInBytes + self.hostname = hostname + self.sysctl = sysctl + self.interfaces = interfaces + self.sockets = sockets + self.mounts = mounts + self.dns = dns + self.hosts = hosts + self.virtualization = virtualization + self.bootlog = bootlog + } } private let state: AsyncMutex @@ -189,10 +217,14 @@ public final class LinuxContainer: Container, Sendable { private let vmm: VirtualMachineManager private let logger: Logger? - /// Create a new `LinuxContainer`. A `Mount` that contains the contents - /// of the container image must be provided, as well as a `VirtualMachineManager` - /// instance that will handle launching the virtual machine the container will - /// execute inside of. + /// Create a new `LinuxContainer`. + /// + /// - Parameters: + /// - id: The identifier for the container. + /// - rootfs: The root filesystem mount containing the container image contents. + /// - vmm: The virtual machine manager that will handle launching the VM for the container. + /// - logger: Optional logger for container operations. + /// - configuration: A closure that configures the container by modifying the Configuration instance. public init( _ id: String, rootfs: Mount, @@ -214,6 +246,32 @@ public final class LinuxContainer: Container, Sendable { self.state = AsyncMutex(.initialized) } + /// Create a new `LinuxContainer`. + /// + /// - Parameters: + /// - id: The identifier for the container. + /// - rootfs: The root filesystem mount containing the container image contents. + /// - vmm: The virtual machine manager that will handle launching the VM for the container. + /// - configuration: The container configuration specifying process, resources, networking, and other settings. + /// - logger: Optional logger for container operations. + public init( + _ id: String, + rootfs: Mount, + vmm: VirtualMachineManager, + configuration: LinuxContainer.Configuration, + logger: Logger? = nil + ) { + self.id = id + self.vmm = vmm + self.hostVsockPorts = Atomic(0x1000_0000) + self.guestVsockPorts = Atomic(0x1000_0000) + self.rootfs = rootfs + self.logger = logger + + self.config = configuration + self.state = AsyncMutex(.initialized) + } + private static func createDefaultRuntimeSpec(_ id: String) -> Spec { .init( process: .init(), diff --git a/Sources/Integration/ContainerTests.swift b/Sources/Integration/ContainerTests.swift index 5cc303eb..36cd447b 100644 --- a/Sources/Integration/ContainerTests.swift +++ b/Sources/Integration/ContainerTests.swift @@ -987,6 +987,31 @@ extension IntegrationSuite { } } + func testNonClosureConstructor() async throws { + let id = "test-container-non-closure-constructor" + + let bs = try await bootstrap(id) + let config = LinuxContainer.Configuration( + process: LinuxProcessConfiguration(arguments: ["/bin/true"]) + ) + let container = LinuxContainer( + id, + rootfs: bs.rootfs, + vmm: bs.vmm, + configuration: config + ) + + try await container.create() + try await container.start() + + let status = try await container.wait() + try await container.stop() + + guard status.exitCode == 0 else { + throw IntegrationError.assert(msg: "process status \(status) != 0") + } + } + private func createHostUnixSocket() throws -> String { let dir = FileManager.default.uniqueTemporaryDirectory(create: true) let socketPath = dir.appendingPathComponent("test.sock").path diff --git a/Sources/Integration/Suite.swift b/Sources/Integration/Suite.swift index 32b8ef24..23d22a8d 100644 --- a/Sources/Integration/Suite.swift +++ b/Sources/Integration/Suite.swift @@ -294,6 +294,7 @@ struct IntegrationSuite: AsyncParsableCommand { Test("container cgroup limits", testCgroupLimits), Test("container no serial console", testNoSerialConsole), Test("unix socket into guest", testUnixSocketIntoGuest), + Test("container non-closure constructor", testNonClosureConstructor), // Pods Test("pod single container", testPodSingleContainer),