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>
pull/13959/head
Jonathan Nobels 4 weeks ago committed by GitHub
parent 0f9a054cba
commit aecb0ab76b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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…
Cancel
Save