//===----------------------------------------------------------------------===// // 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 import Testing /// Integration tests for container machine runtime commands: stop, inspect, run, set. /// Serialized because VM operations share system resources (memory, CPU) and concurrent /// VMs could interfere with each other or exhaust available resources. @Suite(.serialized) struct TestCLIMachineRuntimeSerial { private let machineImage = "ghcr.io/linuxcontainers/alpine:3.20" // MARK: - Stop tests @Test func testStopRunningMachine() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let before = try f.doMachineInspect(name: name) #expect(before.startedDate != nil, "running machine should have startedDate") let output = try f.doMachineStop(name: name) #expect(output == name, "stop should output the machine name") let after = try f.doMachineInspect(name: name) #expect(after.status == "stopped") #expect(after.startedDate == nil, "stopped machine should not have startedDate") } } @Test func testStopIdempotent() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let output = try f.doMachineStop(name: name) #expect(output == name, "stop on already-stopped machine should succeed") let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "stopped") } } @Test func testStopNonExistentMachine() async throws { try await ContainerFixture.with { f in let name = "nonexistent-\(f.testID)" let result = try f.runMachine(["stop", name]) #expect(result.status != 0) #expect(result.error.contains("not found")) } } // MARK: - Inspect tests @Test func testInspectStoppedMachine() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "stopped") #expect(snapshot.startedDate == nil) #expect(snapshot.id == name) #expect(snapshot.image.reference.contains("alpine")) #expect(snapshot.createdDate != nil) #expect(snapshot.ipAddress == nil) #expect(snapshot.cpus > 0) #expect(snapshot.memory > 0) } } @Test func testInspectRunningMachine() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "running") #expect(snapshot.startedDate != nil) #expect(snapshot.id == name) #expect(snapshot.platform.os == "linux") #expect(snapshot.ipAddress != nil) } } @Test func testInspectNonExistentMachine() async throws { try await ContainerFixture.with { f in let name = "nonexistent-\(f.testID)" let result = try f.runMachine(["inspect", name]) #expect(result.status != 0) #expect(result.error.contains("not found")) } } // MARK: - Run tests @Test func testRunSimpleCommand() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let output = try f.doMachineRun(name: name, root: true, command: ["echo", "hello"]) #expect(output.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") } } @Test func testRunAutoBoots() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let before = try f.doMachineInspect(name: name) #expect(before.status == "stopped") let output = try f.doMachineRun(name: name, root: true, command: ["echo", "autoboot"]) #expect(output.trimmingCharacters(in: .whitespacesAndNewlines) == "autoboot") let after = try f.doMachineInspect(name: name) #expect(after.status == "running") } } @Test func testRunAsRoot() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let uid = try f.doMachineRun(name: name, root: true, command: ["id", "-u"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(uid == "0", "running with --root should execute as uid 0") } } @Test func testRunAsHostUser() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let hostUid = getuid() let uid = try f.doMachineRun(name: name, command: ["id", "-u"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(uid == "\(hostUid)", "default run should use host user's UID") } } @Test func testRunWithEnvironment() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let output = try f.doMachineRun( name: name, root: true, env: ["MY_VAR=hello_world"], command: ["echo", "$MY_VAR"]) #expect(output.trimmingCharacters(in: .whitespacesAndNewlines) == "hello_world") } } @Test func testRunWithCwd() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let output = try f.doMachineRun(name: name, root: true, cwd: "/tmp", command: ["pwd"]) #expect(output.trimmingCharacters(in: .whitespacesAndNewlines) == "/tmp") } } @Test func testRunNonExistentMachine() async throws { try await ContainerFixture.with { f in let name = "nonexistent-\(f.testID)" let result = try f.runMachine(["run", "-n", name, "echo", "test"]) #expect(result.status != 0) #expect(result.error.contains("not found")) } } @Test func testRunDefaultHostsEntries() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let inspect = try f.doMachineInspect(name: name) let ip = try #require(inspect.ipAddress, "running machine should have an IP address") let output = try f.doMachineRun(name: name, root: true, command: ["cat", "/etc/hosts"]) let lines = output.split(separator: "\n") let expectedEntries = [("127.0.0.1", "localhost"), (ip, name)] for (i, line) in lines.enumerated() { guard i < expectedEntries.count else { break } let words = line.split(separator: " ").map { String($0) } #expect(words.count >= 2) #expect(expectedEntries[i].0 == words[0]) #expect(expectedEntries[i].1 == words[1]) } } } @Test func testRunCommandInShell() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let output = try f.doMachineRun(name: name, root: true, command: ["echo", "$0"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(output == "/bin/sh", "alpine shell should expand $0 to /bin/sh") } } @Test func testRunCommandExitCode() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["run", "-n", name, "--root", "exit", "42"]) #expect(result.status == 42, "exit code should propagate from command") } } @Test func testRunMultipleEnvVars() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine([ "run", "-n", name, "--root", "-e", "VAR1=one", "-e", "VAR2=two", "-e", "VAR3=three", "echo", "$VAR1-$VAR2-$VAR3", ]) #expect(result.status == 0) #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == "one-two-three") } } @Test func testRunWithUid() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["run", "-n", name, "--uid", "1000", "id", "-u"]) #expect(result.status == 0) #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == "1000") } } @Test func testRunWithGid() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["run", "-n", name, "--root", "--gid", "1000", "id", "-G"]) #expect(result.status == 0) #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == "0 1000") } } @Test func testRunWithUserFlag() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine([ "run", "-n", name, "--user", "1000:1000", "echo", "$(id -u):$(id -g)", ]) #expect(result.status == 0) #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == "1000:1000") } } @Test func testRunWithEnvFile() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let envFile = f.testDir.appending("test.env") try "TEST_VAR=from_file\n".write(toFile: envFile.string, atomically: true, encoding: .utf8) let result = try f.runMachine([ "run", "-n", name, "--root", "--env-file", envFile.string, "echo", "$TEST_VAR", ]) #expect(result.status == 0) #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == "from_file") } } // MARK: - List tests @Test func testListRunningMachines() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["ls"]).check() #expect(result.output.contains(name)) } } @Test func testListAllMachines() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let result = try f.runMachine(["ls"]).check() #expect(result.output.contains(name), "stopped machine should appear in list") } } @Test func testListQuietMode() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["ls", "-q"]).check() #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == name) } } @Test func testListJsonFormat() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["ls", "--format", "json"]).check() let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let items = try decoder.decode([MachineListItem].self, from: result.outputData) let item = items.first { $0.id == name } try #require(item != nil) #expect(item?.status == "running") #expect(item?.ipAddress != nil) #expect(item?.cpus ?? 0 > 0) #expect(item?.memory ?? 0 > 0) #expect(item?.createdDate != nil) } } // MARK: - set-default tests @Test func testSetDefault() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let result = try f.runMachine(["set-default", name]).check() #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == name) let list = try f.runMachine(["ls"]).check() #expect(list.output.contains("*"), "machine should be marked as default in list") } } @Test func testSetDefaultNonExistent() async throws { try await ContainerFixture.with { f in let result = try f.runMachine(["set-default", "nonexistent-\(f.testID)"]) #expect(result.status != 0) #expect(result.error.contains("not found")) } } @Test func testSetDefaultSwitching() async throws { try await ContainerFixture.with { f in let name1 = "\(f.testID)-m1" let name2 = "\(f.testID)-m2" f.addCleanup { f.cleanupMachine(name1) f.cleanupMachine(name2) } try f.doMachineCreate(name: name1, image: machineImage) try f.doMachineCreate(name: name2, image: machineImage) try f.runMachine(["set-default", name1]).check() let result = try f.runMachine(["set-default", name2]).check() #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == name2) let snapshot = try f.doMachineInspect() #expect(snapshot.id == name2, "inspect without ID should use the new default") } } @Test func testInspectUsesDefault() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.runMachine(["set-default", name]).check() let snapshot = try f.doMachineInspect() #expect(snapshot.id == name) } } @Test func testRunUsesDefault() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.runMachine(["set-default", name]).check() try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["run", "--root", "echo", "default-test"]) #expect(result.status == 0) #expect(result.output.trimmingCharacters(in: .whitespacesAndNewlines) == "default-test") } } // MARK: - User / home tests @Test func testFirstBootCreatesSudoersEntry() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let username = NSUserName() let output = try f.doMachineRun( name: name, root: true, command: ["cat", "/etc/sudoers.d/\(username)"] ) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(output == "\(username) ALL=(ALL) NOPASSWD:ALL") } } @Test func testUserSetupIdempotent() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let uid1 = try f.doMachineRun(name: name, command: ["id", "-u"]) .trimmingCharacters(in: .whitespacesAndNewlines) let uid2 = try f.doMachineRun(name: name, command: ["id", "-u"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(uid1 == uid2) #expect(uid1 == "\(getuid())") } } @Test func testHostUserHasCorrectHome() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let home = try f.doMachineRun(name: name, command: ["echo", "$HOME"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(home == "/home/\(NSUserName())") } } // MARK: - set tests @Test func testSetCpus() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.runMachine(["set", "--name", name, "cpus=4"]).check() let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.cpus == 4) } } @Test func testSetMemory() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.runMachine(["set", "--name", name, "memory=8G"]).check() let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.memory == UInt64(8 * 1024 * 1024 * 1024)) } } @Test func testSetMultiple() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.runMachine(["set", "--name", name, "cpus=2", "memory=4G"]).check() let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.cpus == 2) #expect(snapshot.memory == UInt64(4 * 1024 * 1024 * 1024)) } } @Test func testSetInvalidKey() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let result = try f.runMachine(["set", "--name", name, "bogus=value"]) #expect(result.status != 0) } } @Test func testSetRunningWarning() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let result = try f.runMachine(["set", "--name", name, "cpus=2"]) #expect(result.status == 0) #expect(result.error.contains("will take effect")) } } @Test func testSetHomeMount() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.runMachine(["set", "--name", name, "home-mount=ro"]).check() let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.homeMount == "ro") } } @Test func testSetHomeMountInvalid() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) let result = try f.runMachine(["set", "--name", name, "home-mount=badvalue"]) #expect(result.status != 0) } } // MARK: - Create config flag tests @Test func testCreateWithCpus() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage, extraArgs: ["--cpus", "2"]) let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.cpus == 2) } } @Test func testGuestCpuCountMatchesRequested() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage, extraArgs: ["--cpus", "2"]) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let guestCpus = try f.doMachineRun(name: name, root: true, command: ["nproc"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(guestCpus == "2") } } @Test func testCreateWithMemory() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage, extraArgs: ["--memory", "2G"]) let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.memory == UInt64(2 * 1024 * 1024 * 1024)) } } @Test func testCreateWithHomeMount() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage, extraArgs: ["--home-mount", "none"]) let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.homeMount == "none") } } @Test func testCreateAutoBoots() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } // Omit --no-boot so the machine boots automatically. try f.runMachine(["create", "--name", name, machineImage]).check() let snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "running") #expect(snapshot.startedDate != nil) } } @Test func testAmd64PlatformSupported() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate( name: name, image: "alpine:3.22", extraArgs: ["--platform", "linux/amd64"]) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let arch = try f.doMachineRun(name: name, root: true, command: ["uname", "-m"]) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(arch == "x86_64") } } // MARK: - Logs tests @Test func testLogs() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") try f.doMachineStop(name: name) let boot = try f.runMachine(["logs", "--boot", name]).check() #expect(!boot.output.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) let stdio = try f.runMachine(["logs", name]) #expect(stdio.status == 0) } } @Test func testLogsWhileRunning() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let boot = try f.runMachine(["logs", "--boot", name]).check() #expect(!boot.output.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) let stdio = try f.runMachine(["logs", name]) #expect(stdio.status == 0) } } @Test func testLogsNonExistentMachine() async throws { try await ContainerFixture.with { f in let result = try f.runMachine(["logs", "nonexistent-\(f.testID)"]) #expect(result.status != 0) #expect(result.error.contains("not found")) } } // MARK: - Machine isolation from container commands @Test func testMachineNotInContainerList() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let inspect = try f.doMachineInspect(name: name) let containerId = try #require(inspect.containerId) let running = try f.run(["ls", "-q"]).check() #expect(!running.output.contains(containerId)) let all = try f.run(["ls", "-a", "-q"]).check() #expect(!all.output.contains(containerId)) } } @Test func testMachineNotDeletedByRmAll() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let inspect = try f.doMachineInspect(name: name) let containerId = try #require(inspect.containerId) try f.run(["delete", "--all"]).check() let containerInspect = try f.inspectContainer(containerId) #expect( containerInspect.status.state == "running", "machine container should survive 'container delete --all'") } } @Test func testMachineNotKilledByKillAll() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let inspect = try f.doMachineInspect(name: name) let containerId = try #require(inspect.containerId) try f.run(["kill", "--all"]).check() let containerInspect = try f.inspectContainer(containerId) #expect( containerInspect.status.state == "running", "machine container should survive 'container kill --all'") } } @Test func testMachineNotStoppedByStopAll() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let inspect = try f.doMachineInspect(name: name) let containerId = try #require(inspect.containerId) try f.run(["stop", "--all"]).check() let containerInspect = try f.inspectContainer(containerId) #expect( containerInspect.status.state == "running", "machine container should survive 'container stop --all'") } } @Test func testContainerExitState() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") let inspect = try f.doMachineInspect(name: name) let containerId = try #require( inspect.containerId, "running machine should have a containerId") try f.doStop(containerId) try await f.waitForMachineStatus(name, status: "stopped") let after = try f.doMachineInspect(name: name) #expect(after.status == "stopped") #expect(after.containerId == nil) #expect(after.startedDate == nil) } } // MARK: - Lifecycle test @Test func testFullLifecycle() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } try f.doMachineCreate(name: name, image: machineImage) var snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "stopped") try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "running") let output = try f.doMachineRun(name: name, root: true, command: ["hostname"]) #expect(!output.isEmpty) try f.doMachineStop(name: name) snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "stopped") try f.doMachineBoot(name: name) try await f.waitForMachineStatus(name, status: "running") snapshot = try f.doMachineInspect(name: name) #expect(snapshot.status == "running") } } // MARK: - SSH forwarding @Test func testSSHForwarding() async throws { try await ContainerFixture.with { f in let name = "\(f.testID)-machine" f.addCleanup { f.cleanupMachine(name) } let socketPath = try f.makeFakeSSHAgentSocket() try await Task.sleep(for: .seconds(1)) try f.doMachineCreate(name: name, image: machineImage) try f.runMachine( ["run", "--root", "-n", name, "true"], env: ["SSH_AUTH_SOCK": socketPath] ).check() try await f.waitForMachineStatus(name, status: "running") let sockValue = try f.doMachineRun( name: name, root: true, command: ["echo", "$SSH_AUTH_SOCK"] ) .trimmingCharacters(in: .whitespacesAndNewlines) #expect(sockValue == "/var/host-services/ssh-auth.sock") let exists = try f.doMachineRun( name: name, root: true, command: [ "[ -S /var/host-services/ssh-auth.sock ]", "&&", "echo", "exists", "||", "echo", "missing", ] ).trimmingCharacters(in: .whitespacesAndNewlines) #expect(exists == "exists") } } }