//===----------------------------------------------------------------------===// // 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 ArgumentParser import ContainerAPIClient import ContainerPersistence import ContainerizationError import Foundation import MachineAPIClient extension Application { public struct MachineSet: AsyncLoggableCommand { public init() {} public static let configuration = CommandConfiguration( commandName: "set", abstract: "Set container machine configuration values" ) @OptionGroup public var logOptions: Flags.Logging @Option(name: [.short, .long], help: "Container machine ID (uses default if not specified)") public var name: String? @Argument( parsing: .remaining, help: ArgumentHelp( "Configuration values", discussion: MachineConfig.helpText(), valueName: "setting" ) ) public var rawArgs: [String] = [] public func run() async throws { guard !rawArgs.isEmpty else { throw ContainerizationError(.invalidArgument, message: "expected at least one configuration value (e.g., machine set cpus=4)") } let client = MachineClient() let resolvedName = try await resolveMachineId(name, client: client) let snapshot = try await client.inspect(id: resolvedName) let kwargs = Dictionary( try rawArgs.map { arg in let parts = arg.split(separator: "=", maxSplits: 1) guard parts.count == 2 else { throw ContainerizationError(.invalidArgument, message: "invalid argument format '\(arg)'. Expected 'key=value'") } return (String(parts[0]), String(parts[1])) }, uniquingKeysWith: { _, last in last } ) if kwargs["virtualization"] == "true" { try MachineCapabilities.requireNestedVirtualizationSupported() } var validatedKwargs = kwargs if let raw = kwargs["kernel"], !raw.isEmpty { validatedKwargs["kernel"] = try MachineConfig.validateKernelPath(raw).string } let newConfig = try snapshot.bootConfig.with(validatedKwargs) try await client.setConfig(id: resolvedName, bootConfig: newConfig) if snapshot.status == .running { FileHandle.standardError.write( Data("Note: Changes will take effect after stopping and restarting '\(resolvedName)'.\n".utf8)) } print(resolvedName) } } }