diff --git a/Sources/ContainerCommands/Machine/MachineCreate.swift b/Sources/ContainerCommands/Machine/MachineCreate.swift index 12881a7b5..869369979 100644 --- a/Sources/ContainerCommands/Machine/MachineCreate.swift +++ b/Sources/ContainerCommands/Machine/MachineCreate.swift @@ -64,6 +64,9 @@ extension Application { @Option(name: .long, help: "User's home directory mount option (ro, rw, none). Default: rw") public var homeMount: String? + @Option(name: .long, help: "Custom home directory mount source path") + public var homeMountPath: String? + @Argument(help: "Container image reference (e.g., alpine:3.22)") var image: String @@ -88,6 +91,7 @@ extension Application { "cpus": cpus.map { "\($0)" }, "memory": memory, "home-mount": homeMount, + "home-mount-path": homeMountPath, ].compactMapValues { $0 } ) diff --git a/Sources/ContainerPersistence/MachineConfig.swift b/Sources/ContainerPersistence/MachineConfig.swift index 62878fc16..20c03d0b6 100644 --- a/Sources/ContainerPersistence/MachineConfig.swift +++ b/Sources/ContainerPersistence/MachineConfig.swift @@ -37,6 +37,10 @@ public struct MachineConfig: Codable, Sendable { public static let defaultHomeMount: HomeMountOption = .rw + public static var defaultHomeMountPath: String { + FileManager.default.homeDirectoryForCurrentUser.path + } + /// Home mount option for the /Users/ directory. public enum HomeMountOption: String, Sendable, Codable { case ro @@ -50,18 +54,22 @@ public struct MachineConfig: Codable, Sendable { public let memory: MemorySize /// Home mount configuration. nil = system default. public let homeMount: HomeMountOption + /// Custom home directory mount source path. + public let homeMountPath: String /// Settable keys and their descriptions, for CLI help text generation. public static let settableKeys: [(key: String, valueName: String, description: String)] = [ ("cpus", "", "Number of virtual CPUs"), ("memory", "", "Memory allocation (e.g., 2G, 1G). Default: half of system memory"), ("home-mount", "", "User home directory mount option (ro, rw, none). Default: rw"), + ("home-mount-path", "", "Custom home directory mount source path. Default: current user home"), ] - public init(cpus: Int?, memory: MemorySize?, homeMount: HomeMountOption?) throws { + public init(cpus: Int?, memory: MemorySize?, homeMount: HomeMountOption?, homeMountPath: String? = nil) throws { self.cpus = cpus ?? Self.defaultCPUs self.memory = memory ?? Self.defaultMemory self.homeMount = homeMount ?? Self.defaultHomeMount + self.homeMountPath = homeMountPath ?? Self.defaultHomeMountPath try self.validate() } @@ -72,8 +80,9 @@ public struct MachineConfig: Codable, Sendable { let cpus = try container.decodeIfPresent(Int.self, forKey: .cpus) let memory = try container.decodeIfPresent(MemorySize.self, forKey: .memory) let homeMount = try container.decodeIfPresent(HomeMountOption.self, forKey: .homeMount) + let homeMountPath = try container.decodeIfPresent(String.self, forKey: .homeMountPath) - try self.init(cpus: cpus, memory: memory, homeMount: homeMount) + try self.init(cpus: cpus, memory: memory, homeMount: homeMount, homeMountPath: homeMountPath) } private func validate() throws { @@ -90,6 +99,13 @@ public struct MachineConfig: Codable, Sendable { message: "invalid memory value '\(self.memory)'. Must be greater than 1gb." ) } + + guard !self.homeMountPath.isEmpty else { + throw ContainerizationError( + .invalidArgument, + message: "home mount path must not be empty" + ) + } } } @@ -117,11 +133,13 @@ extension MachineConfig { let cpus = try kwargs["cpus"].map { try Self.parseInt($0, for: "cpus") } let memory = try kwargs["memory"].map { try MemorySize($0) } let homeMount = try kwargs["home-mount"].map { try Self.parseHomeMount($0) } + let homeMountPath = kwargs["home-mount-path"] return try .init( cpus: cpus ?? self.cpus, memory: memory ?? self.memory, - homeMount: homeMount ?? self.homeMount + homeMount: homeMount ?? self.homeMount, + homeMountPath: homeMountPath ?? self.homeMountPath ) } diff --git a/Sources/Services/MachineAPIService/Server/MachinesService.swift b/Sources/Services/MachineAPIService/Server/MachinesService.swift index a80cc7698..3f89f6505 100644 --- a/Sources/Services/MachineAPIService/Server/MachinesService.swift +++ b/Sources/Services/MachineAPIService/Server/MachinesService.swift @@ -363,6 +363,7 @@ public actor MachinesService { sbin: path.appending(MachineBundle.sbinDirectory), initializedFile: path.appending(MachineBundle.initializedFile), homeMountOption: bootConfig.homeMount, + homeMountPath: bootConfig.homeMountPath, ) config.resources.cpus = bootConfig.cpus @@ -628,6 +629,7 @@ extension MachineConfiguration { sbin: FilePath, initializedFile: FilePath, homeMountOption: MachineConfig.HomeMountOption, + homeMountPath: String, ) async throws -> ContainerConfiguration { var config = ContainerConfiguration( id: cid, @@ -642,7 +644,7 @@ extension MachineConfiguration { ) ) - let home = FileManager.default.homeDirectoryForCurrentUser.path + let home = homeMountPath config.mounts = [ .virtiofs( source: sbin.string, diff --git a/docs/command-reference.md b/docs/command-reference.md index 1492c7f28..fe8078efc 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -1064,7 +1064,7 @@ container registry list [--format ] [--quiet] [--debug] ### `container machine create` -Creates a container machine from an image and boots it. Use `--cpus`, `--memory`, and `--home-mount` to configure it, or `--no-boot` to create it without booting. +Creates a container machine from an image and boots it. Use `--cpus`, `--memory`, `--home-mount`, and `--home-mount-path` to configure it, or `--no-boot` to create it without booting. **Usage** @@ -1084,6 +1084,7 @@ container machine create [] * `--cpus `: Number of virtual CPUs * `--memory `: Memory allocation (e.g., 2G, 8G). Default: half of system memory * `--home-mount `: User's home directory mount option (ro, rw, none). Default: rw +* `--home-mount-path `: Custom home directory mount source path (default: current user home) **Management Options** @@ -1213,6 +1214,7 @@ container machine set [--name ] [--debug] ... * `cpus=`: Number of virtual CPUs * `memory=`: Memory allocation (e.g., 2G, 1G). Default: half of system memory * `home-mount=`: User home directory mount option (ro, rw, none). Default: rw +* `home-mount-path=`: Custom home directory mount source path (default: current user home) **Options**