mirror of https://github.com/cirruslabs/tart.git
tart run: replace --sync with --root-disk-opts (#879)
* VZDiskImageSynchronizationMode's "description" field is a dead code * Re-use the VZDiskImageSynchronizationMode extension * tart run: replace --sync with --root-disk-opts * VM: support root disk synchronization mode on macOS
This commit is contained in:
parent
5f2199ef3e
commit
3892cdb00d
|
|
@ -12,8 +12,24 @@ var vm: VM?
|
|||
struct IPNotFound: Error {
|
||||
}
|
||||
|
||||
extension VZDiskImageSynchronizationMode : LosslessStringConvertible {
|
||||
public init?(_ description: String) {
|
||||
@available(macOS 14, *)
|
||||
extension VZDiskSynchronizationMode {
|
||||
public init(_ description: String) throws {
|
||||
switch description {
|
||||
case "none":
|
||||
self = .none
|
||||
case "full":
|
||||
self = .full
|
||||
case "":
|
||||
self = .full
|
||||
default:
|
||||
throw RuntimeError.VMConfigurationError("unsupported disk synchronization mode: \"\(description)\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension VZDiskImageSynchronizationMode {
|
||||
public init(_ description: String) throws {
|
||||
switch description {
|
||||
case "none":
|
||||
self = .none
|
||||
|
|
@ -21,21 +37,10 @@ extension VZDiskImageSynchronizationMode : LosslessStringConvertible {
|
|||
self = .fsync
|
||||
case "full":
|
||||
self = .full
|
||||
case "":
|
||||
self = .full
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .none:
|
||||
return "none"
|
||||
case .fsync:
|
||||
return "fsync"
|
||||
case .full:
|
||||
return "full"
|
||||
@unknown default:
|
||||
return "unknown"
|
||||
throw RuntimeError.VMConfigurationError("unsupported disk image synchronization mode: \"\(description)\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,13 +173,19 @@ struct Run: AsyncParsableCommand {
|
|||
@Flag(help: ArgumentHelp("Restrict network access to the host-only network"))
|
||||
var netHost: Bool = false
|
||||
|
||||
@Option(help: ArgumentHelp("Set the root disk synchronization mode (Linux images only). Possible values: none, fsync, full.",
|
||||
@Option(help: ArgumentHelp("Set the root disk options (e.g. --root-disk-opts=\"ro\" or --root-disk-opts=\"sync=none\")",
|
||||
discussion: """
|
||||
'full' synchronizes data with the drive and ensures that it is written to permanent storage.
|
||||
'fsync' synchronizes data with the drive but doesn't guarantee that it is written to permanent storage.
|
||||
'none' doesn't synchronize data with the drive.
|
||||
"""))
|
||||
var sync: String = "full"
|
||||
Options are comma-separated and are as follows:
|
||||
|
||||
* ro — attach the root disk in read-only mode instead of the default read-write (e.g. --root-disk-opts="ro")
|
||||
|
||||
* sync=none — disable data synchronization with the permanent storage to increase performance at the cost of a higher chance of data loss (e.g. --root-disk-opts="sync=none")
|
||||
|
||||
* sync=fsync — enable data synchronization with the permanent storage, but don't ensure that it was actually written (e.g. --root-disk-opts="sync=fsync")
|
||||
|
||||
* sync=full — enable data synchronization with the permanent storage and ensure that it was actually written (e.g. --root-disk-opts="sync=full")
|
||||
""", valueName: "options"))
|
||||
var rootDiskOpts: String = ""
|
||||
|
||||
#if arch(arm64)
|
||||
@Flag(help: ArgumentHelp("Disables audio and entropy devices and switches to only Mac-specific input devices.", discussion: "Useful for running a VM that can be suspended via \"tart suspend\"."))
|
||||
|
|
@ -231,10 +242,6 @@ struct Run: AsyncParsableCommand {
|
|||
throw ValidationError("Seems you have a disk targeting x86 architecture (hence amd64 in the name). Please use an 'arm64' version of the disk.")
|
||||
}
|
||||
}
|
||||
|
||||
if(VZDiskImageSynchronizationMode(sync) == nil) {
|
||||
throw ValidationError("Invalid disk synchronization mode: \(sync)")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
|
@ -279,6 +286,9 @@ struct Run: AsyncParsableCommand {
|
|||
serialPorts.append(createSerialPortConfiguration(tty_read!, tty_write!))
|
||||
}
|
||||
|
||||
// Parse root disk options
|
||||
let diskOptions = DiskOptions(rootDiskOpts)
|
||||
|
||||
vm = try VM(
|
||||
vmDir: vmDir,
|
||||
network: userSpecifiedNetwork(vmDir: vmDir) ?? NetworkShared(),
|
||||
|
|
@ -288,7 +298,7 @@ struct Run: AsyncParsableCommand {
|
|||
suspendable: suspendable,
|
||||
audio: !noAudio,
|
||||
clipboard: !noClipboard,
|
||||
sync: VZDiskImageSynchronizationMode(sync) ?? .full
|
||||
sync: VZDiskImageSynchronizationMode(diskOptions.syncModeRaw)
|
||||
)
|
||||
|
||||
let vncImpl: VNC? = try {
|
||||
|
|
@ -741,13 +751,11 @@ struct AdditionalDisk {
|
|||
throw UnsupportedOSError("attaching Network Block Devices", "are")
|
||||
}
|
||||
|
||||
let syncMode = try parseSyncMode(syncModeRaw)
|
||||
|
||||
let nbdAttachment = try VZNetworkBlockDeviceStorageDeviceAttachment(
|
||||
url: diskURL!,
|
||||
timeout: 30,
|
||||
isForcedReadOnly: diskReadOnly,
|
||||
synchronizationMode: syncMode
|
||||
synchronizationMode: try VZDiskSynchronizationMode(syncModeRaw)
|
||||
)
|
||||
|
||||
return VZVirtioBlockDeviceConfiguration(attachment: nbdAttachment)
|
||||
|
|
@ -775,7 +783,7 @@ struct AdditionalDisk {
|
|||
}
|
||||
|
||||
let blockAttachment = try VZDiskBlockDeviceStorageDeviceAttachment(fileHandle: FileHandle(fileDescriptor: fd, closeOnDealloc: true),
|
||||
readOnly: diskReadOnly, synchronizationMode: try parseSyncMode(syncModeRaw))
|
||||
readOnly: diskReadOnly, synchronizationMode: try VZDiskSynchronizationMode(syncModeRaw))
|
||||
|
||||
return VZVirtioBlockDeviceConfiguration(attachment: blockAttachment)
|
||||
}
|
||||
|
|
@ -809,7 +817,7 @@ struct AdditionalDisk {
|
|||
url: diskFileURL,
|
||||
readOnly: diskReadOnly,
|
||||
cachingMode: .automatic,
|
||||
synchronizationMode: try Self.parseImageSyncMode(syncModeRaw)
|
||||
synchronizationMode: try VZDiskImageSynchronizationMode(syncModeRaw)
|
||||
)
|
||||
|
||||
return VZVirtioBlockDeviceConfiguration(attachment: diskImageAttachment)
|
||||
|
|
@ -817,58 +825,36 @@ struct AdditionalDisk {
|
|||
|
||||
static func parseOptions(_ parseFrom: String) -> (String, Bool, String) {
|
||||
var arguments = parseFrom.split(separator: ":")
|
||||
let options = arguments.last!.split(separator: ",")
|
||||
|
||||
var readOnly: Bool = false
|
||||
var syncModeRaw: String = ""
|
||||
let options = DiskOptions(String(arguments.last!))
|
||||
if options.foundAtLeastOneOption {
|
||||
arguments.removeLast()
|
||||
}
|
||||
|
||||
var foundAtLeastOneOption: Bool = false
|
||||
return (arguments.joined(separator: ":"), options.readOnly, options.syncModeRaw)
|
||||
}
|
||||
}
|
||||
|
||||
struct DiskOptions {
|
||||
var readOnly: Bool = false
|
||||
var syncModeRaw: String = ""
|
||||
var foundAtLeastOneOption: Bool = false
|
||||
|
||||
init(_ parseFrom: String) {
|
||||
let options = parseFrom.split(separator: ",")
|
||||
|
||||
for option in options {
|
||||
switch true {
|
||||
case option == "ro":
|
||||
readOnly = true
|
||||
foundAtLeastOneOption = true
|
||||
self.readOnly = true
|
||||
self.foundAtLeastOneOption = true
|
||||
case option.hasPrefix("sync="):
|
||||
syncModeRaw = String(option.dropFirst("sync=".count))
|
||||
foundAtLeastOneOption = true
|
||||
self.syncModeRaw = String(option.dropFirst("sync=".count))
|
||||
self.foundAtLeastOneOption = true
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if foundAtLeastOneOption {
|
||||
arguments.removeLast()
|
||||
}
|
||||
|
||||
return (arguments.joined(separator: ":"), readOnly, syncModeRaw)
|
||||
}
|
||||
|
||||
@available(macOS 14, *)
|
||||
static func parseSyncMode(_ parseFrom: String) throws -> VZDiskSynchronizationMode {
|
||||
switch parseFrom {
|
||||
case "none":
|
||||
return .none
|
||||
case "full":
|
||||
return .full
|
||||
case "":
|
||||
return .full
|
||||
default:
|
||||
throw RuntimeError.VMConfigurationError("unsupported disk synchronization mode: \"\(parseFrom)\"")
|
||||
}
|
||||
}
|
||||
|
||||
static func parseImageSyncMode(_ parseFrom: String) throws -> VZDiskImageSynchronizationMode {
|
||||
switch parseFrom {
|
||||
case "none":
|
||||
return .none
|
||||
case "full":
|
||||
return .full
|
||||
case "":
|
||||
return .full
|
||||
default:
|
||||
throw RuntimeError.VMConfigurationError("unsupported disk image synchronization mode: \"\(parseFrom)\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
let attachment: VZDiskImageStorageDeviceAttachment = vmConfig.os == .linux ?
|
||||
// Use "cached" caching mode for virtio drive to prevent fs corruption on linux
|
||||
try VZDiskImageStorageDeviceAttachment(url: diskURL, readOnly: false, cachingMode: .cached, synchronizationMode: sync) :
|
||||
try VZDiskImageStorageDeviceAttachment(url: diskURL, readOnly: false)
|
||||
try VZDiskImageStorageDeviceAttachment(url: diskURL, readOnly: false, cachingMode: .automatic, synchronizationMode: sync)
|
||||
|
||||
var device: VZStorageDeviceConfiguration
|
||||
if #available(macOS 14, *), vmConfig.os == .linux {
|
||||
|
|
|
|||
Loading…
Reference in New Issue