mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-05 20:26:47 +02:00
Add a --headless flag to the Host.app Run subcommand for running
macOS VMs without a GUI, enabling use from test frameworks.
Key changes:
- HostCli.swift: When --headless is set, run the VM via VMController
+ RunLoop.main.run() instead of NSApplicationMain. Using the
RunLoop (not dispatchMain) is required because VZ framework
callbacks depend on RunLoop sources.
- VMController.swift: Add headless parameter to createVirtualMachine
that configures a single socket-based NIC (no NAT NIC). This
matches the NIC configuration used when creating/saving VMs, so
saved state restoration works correctly. A NIC count mismatch
causes VZ to silently fail to execute guest code.
- TailMacConfigHelper.swift: Clean up socket network device logging.
- Config.swift: Move VM storage from ~/VM.bundle to
~/.cache/tailscale/vmtest/macos/.
- TailMac.swift: Fix dispatchMain→RunLoop.main.run() in the create
command (same VZ RunLoop requirement).
Updates #13038
Change-Id: Iea51c043aa92e8fc6257139b9f0e2e7677072fa2
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
52 lines
1.6 KiB
Swift
52 lines
1.6 KiB
Swift
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
import Cocoa
|
|
import Foundation
|
|
import Virtualization
|
|
import ArgumentParser
|
|
|
|
@main
|
|
struct HostCli: ParsableCommand {
|
|
static var configuration = CommandConfiguration(
|
|
abstract: "A utility for running virtual machines",
|
|
subcommands: [Run.self],
|
|
defaultSubcommand: Run.self)
|
|
}
|
|
|
|
var config: Config = Config()
|
|
|
|
extension HostCli {
|
|
struct Run: ParsableCommand {
|
|
@Option var id: String
|
|
@Option var share: String?
|
|
@Flag(help: "Run without GUI (for automated testing)") var headless: Bool = false
|
|
|
|
mutating func run() {
|
|
config = Config(id)
|
|
config.sharedDir = share
|
|
print("Running vm with identifier \(id) and sharedDir \(share ?? "<none>")")
|
|
|
|
if headless {
|
|
DispatchQueue.main.async {
|
|
let controller = VMController()
|
|
controller.createVirtualMachine(headless: true)
|
|
|
|
let fileManager = FileManager.default
|
|
if fileManager.fileExists(atPath: config.saveFileURL.path) {
|
|
print("Restoring virtual machine state from \(config.saveFileURL)")
|
|
controller.restoreVirtualMachine()
|
|
} else {
|
|
print("Starting virtual machine")
|
|
controller.startVirtualMachine()
|
|
}
|
|
}
|
|
RunLoop.main.run()
|
|
} else {
|
|
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|