Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/ContainerCommands/Machine/MachineCreate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -88,6 +91,7 @@ extension Application {
"cpus": cpus.map { "\($0)" },
"memory": memory,
"home-mount": homeMount,
"home-mount-path": homeMountPath,
].compactMapValues { $0 }
)

Expand Down
24 changes: 21 additions & 3 deletions Sources/ContainerPersistence/MachineConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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/<name> directory.
public enum HomeMountOption: String, Sendable, Codable {
case ro
Expand All @@ -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>", "Number of virtual CPUs"),
("memory", "<size>", "Memory allocation (e.g., 2G, 1G). Default: half of system memory"),
("home-mount", "<string>", "User home directory mount option (ro, rw, none). Default: rw"),
("home-mount-path", "<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()
}
Expand All @@ -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 {
Expand All @@ -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"
)
}
}
}

Expand Down Expand Up @@ -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
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -628,6 +629,7 @@ extension MachineConfiguration {
sbin: FilePath,
initializedFile: FilePath,
homeMountOption: MachineConfig.HomeMountOption,
homeMountPath: String,
) async throws -> ContainerConfiguration {
var config = ContainerConfiguration(
id: cid,
Expand All @@ -642,7 +644,7 @@ extension MachineConfiguration {
)
)

let home = FileManager.default.homeDirectoryForCurrentUser.path
let home = homeMountPath
config.mounts = [
.virtiofs(
source: sbin.string,
Expand Down
4 changes: 3 additions & 1 deletion docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ container registry list [--format <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**

Expand All @@ -1084,6 +1084,7 @@ container machine create [<options>] <image>
* `--cpus <cpus>`: Number of virtual CPUs
* `--memory <memory>`: Memory allocation (e.g., 2G, 8G). Default: half of system memory
* `--home-mount <home-mount>`: User's home directory mount option (ro, rw, none). Default: rw
* `--home-mount-path <path>`: Custom home directory mount source path (default: current user home)

**Management Options**

Expand Down Expand Up @@ -1213,6 +1214,7 @@ container machine set [--name <name>] [--debug] <setting> ...
* `cpus=<number>`: Number of virtual CPUs
* `memory=<size>`: Memory allocation (e.g., 2G, 1G). Default: half of system memory
* `home-mount=<string>`: User home directory mount option (ro, rw, none). Default: rw
* `home-mount-path=<path>`: Custom home directory mount source path (default: current user home)

**Options**

Expand Down