LinuxContainer: Allow reuse after being stopped (#240)

After the container is stopped it's a bit odd how we don't allow the
object to be restarted. If someone wanted to continuously rerun the same
container they'd need to make a new object every time even though all
guest state is blown away on stop() so it's a clean slate.

This change makes it so that you can create+start again after stop
(although create stands out like a sore thumb now as stop -> create is
just strange).
This commit is contained in:
Danny Canter
2025-08-06 09:24:07 -07:00
committed by GitHub
parent 14239b02c3
commit 4d2f73d300
3 changed files with 53 additions and 1 deletions
@@ -194,7 +194,7 @@ public final class LinuxContainer: Container, Sendable {
mutating func setCreating() throws {
switch self {
case .initialized:
case .initialized, .stopped:
self = .creating(.init())
default:
throw ContainerizationError(
+1
View File
@@ -211,6 +211,7 @@ struct IntegrationSuite: AsyncParsableCommand {
"container mount": testMounts,
"nested virt": testNestedVirtualizationEnabled,
"container manager": testContainerManagerCreate,
"container reuse": testContainerReuse,
]
var passed = 0
+51
View File
@@ -121,6 +121,57 @@ extension IntegrationSuite {
}
}
func testContainerReuse() async throws {
let id = "test-container-reuse"
// Get the kernel from bootstrap
let bs = try await bootstrap()
// Create ContainerManager with kernel and initfs reference
let manager = try ContainerManager(vmm: bs.vmm)
defer {
try? manager.delete(id)
}
let buffer = BufferWriter()
let container = try await manager.create(
id,
image: bs.image,
rootfs: bs.rootfs
) { config in
config.process.arguments = ["/bin/echo", "ContainerManager test"]
config.process.stdout = buffer
}
// Start the container
try await container.create()
try await container.start()
// Wait for completion
var status = try await container.wait()
guard status == 0 else {
throw IntegrationError.assert(msg: "process status \(status) != 0")
}
try await container.stop()
// Recreate things.
try await container.create()
try await container.start()
// Wait for completion.. again.
status = try await container.wait()
guard status == 0 else {
throw IntegrationError.assert(msg: "process status \(status) != 0")
}
let output = String(data: buffer.data, encoding: .utf8)
let expected = "ContainerManager test\nContainerManager test\n"
guard output == expected else {
throw IntegrationError.assert(
msg: "process should have returned '\(expected)' != '\(output ?? "nil")'")
}
}
private func createMountDirectory() throws -> URL {
let dir = FileManager.default.uniqueTemporaryDirectory(create: true)
try "hello".write(to: dir.appendingPathComponent("hi.txt"), atomically: true, encoding: .utf8)