tstest/tailmac: add support for mounting host directories in the guest (#13957)
updates tailscale/corp#24197 tailmac run now supports the --share option which will allow you to specify a directory on the host which can be mounted in the guest using mount_virtiofs vmshare <path>. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
parent
0f9a054cba
commit
aecb0ab76b
@ -14,6 +14,7 @@ class Config: Codable {
|
|||||||
var mac = "52:cc:cc:cc:cc:01"
|
var mac = "52:cc:cc:cc:cc:01"
|
||||||
var ethermac = "52:cc:cc:cc:ce:01"
|
var ethermac = "52:cc:cc:cc:ce:01"
|
||||||
var port: UInt32 = 51009
|
var port: UInt32 = 51009
|
||||||
|
var sharedDir: String?
|
||||||
|
|
||||||
// The virtual machines ID. Also double as the directory name under which
|
// The virtual machines ID. Also double as the directory name under which
|
||||||
// we will store configuration, block device, etc.
|
// we will store configuration, block device, etc.
|
||||||
|
@ -141,5 +141,18 @@ struct TailMacConfigHelper {
|
|||||||
func createKeyboardConfiguration() -> VZKeyboardConfiguration {
|
func createKeyboardConfiguration() -> VZKeyboardConfiguration {
|
||||||
return VZMacKeyboardConfiguration()
|
return VZMacKeyboardConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createDirectoryShareConfiguration(tag: String) -> VZDirectorySharingDeviceConfiguration? {
|
||||||
|
guard let dir = config.sharedDir else { return nil }
|
||||||
|
|
||||||
|
let sharedDir = VZSharedDirectory(url: URL(fileURLWithPath: dir), readOnly: false)
|
||||||
|
let share = VZSingleDirectoryShare(directory: sharedDir)
|
||||||
|
|
||||||
|
// Create the VZVirtioFileSystemDeviceConfiguration and assign it a unique tag.
|
||||||
|
let sharingConfiguration = VZVirtioFileSystemDeviceConfiguration(tag: tag)
|
||||||
|
sharingConfiguration.share = share
|
||||||
|
|
||||||
|
return sharingConfiguration
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,10 +19,12 @@ var config: Config = Config()
|
|||||||
extension HostCli {
|
extension HostCli {
|
||||||
struct Run: ParsableCommand {
|
struct Run: ParsableCommand {
|
||||||
@Option var id: String
|
@Option var id: String
|
||||||
|
@Option var share: String?
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("Running vm with identifier \(id)")
|
|
||||||
config = Config(id)
|
config = Config(id)
|
||||||
|
config.sharedDir = share
|
||||||
|
print("Running vm with identifier \(id) and sharedDir \(share ?? "<none>")")
|
||||||
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,13 @@ class VMController: NSObject, VZVirtualMachineDelegate {
|
|||||||
virtualMachineConfiguration.keyboards = [helper.createKeyboardConfiguration()]
|
virtualMachineConfiguration.keyboards = [helper.createKeyboardConfiguration()]
|
||||||
virtualMachineConfiguration.socketDevices = [helper.createSocketDeviceConfiguration()]
|
virtualMachineConfiguration.socketDevices = [helper.createSocketDeviceConfiguration()]
|
||||||
|
|
||||||
|
if let dir = config.sharedDir, let shareConfig = helper.createDirectoryShareConfiguration(tag: "vmshare") {
|
||||||
|
print("Sharing \(dir) as vmshare. Use: mount_virtiofs vmshare <path> in the guest to mount.")
|
||||||
|
virtualMachineConfiguration.directorySharingDevices = [shareConfig]
|
||||||
|
} else {
|
||||||
|
print("No shared directory created. \(config.sharedDir ?? "none") was requested.")
|
||||||
|
}
|
||||||
|
|
||||||
try! virtualMachineConfiguration.validate()
|
try! virtualMachineConfiguration.validate()
|
||||||
try! virtualMachineConfiguration.validateSaveRestoreSupport()
|
try! virtualMachineConfiguration.validateSaveRestoreSupport()
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ extension Tailmac {
|
|||||||
extension Tailmac {
|
extension Tailmac {
|
||||||
struct Run: ParsableCommand {
|
struct Run: ParsableCommand {
|
||||||
@Option(help: "The vm identifier") var id: String
|
@Option(help: "The vm identifier") var id: String
|
||||||
|
@Option(help: "Optional share directory") var share: String?
|
||||||
@Flag(help: "Tail the TailMac log output instead of returning immediatly") var tail
|
@Flag(help: "Tail the TailMac log output instead of returning immediatly") var tail
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
@ -115,7 +116,12 @@ extension Tailmac {
|
|||||||
fatalError("Could not find Host.app at \(appPath). This must be co-located with the tailmac utility")
|
fatalError("Could not find Host.app at \(appPath). This must be co-located with the tailmac utility")
|
||||||
}
|
}
|
||||||
|
|
||||||
process.arguments = ["run", "--id", id]
|
var args = ["run", "--id", id]
|
||||||
|
if let share {
|
||||||
|
args.append("--share")
|
||||||
|
args.append(share)
|
||||||
|
}
|
||||||
|
process.arguments = args
|
||||||
|
|
||||||
do {
|
do {
|
||||||
process.standardOutput = stdOutPipe
|
process.standardOutput = stdOutPipe
|
||||||
@ -124,26 +130,18 @@ extension Tailmac {
|
|||||||
fatalError("Unable to launch the vm process")
|
fatalError("Unable to launch the vm process")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This doesn't print until we exit which is not ideal, but at least we
|
|
||||||
// get the output
|
|
||||||
if tail != 0 {
|
if tail != 0 {
|
||||||
|
// (jonathan)TODO: How do we get the process output in real time?
|
||||||
|
// The child process only seems to flush to stdout on completion
|
||||||
let outHandle = stdOutPipe.fileHandleForReading
|
let outHandle = stdOutPipe.fileHandleForReading
|
||||||
|
outHandle.readabilityHandler = { handle in
|
||||||
let queue = OperationQueue()
|
let data = handle.availableData
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
forName: NSNotification.Name.NSFileHandleDataAvailable,
|
|
||||||
object: outHandle, queue: queue)
|
|
||||||
{
|
|
||||||
notification -> Void in
|
|
||||||
let data = outHandle.availableData
|
|
||||||
if data.count > 0 {
|
if data.count > 0 {
|
||||||
if let str = String(data: data, encoding: String.Encoding.utf8) {
|
if let str = String(data: data, encoding: String.Encoding.utf8) {
|
||||||
print(str)
|
print(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outHandle.waitForDataInBackgroundAndNotify()
|
|
||||||
}
|
}
|
||||||
outHandle.waitForDataInBackgroundAndNotify()
|
|
||||||
process.waitUntilExit()
|
process.waitUntilExit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user