tstest/tailmac: add customized macOS virtualization tooling (#13146)
updates tailcale/corp#22371 Adds custom macOS vm tooling. See the README for the general gist, but this will spin up VMs with unixgram capable network interfaces listening to a named socket, and with a virtio socket device for host-guest communication. We can add other devices like consoles, serial, etc as needed. The whole things is buildable with a single make command, and everything is controllable via the command line using the TailMac utility. This should all be generally functional but takes a few shortcuts with error handling and the like. The virtio socket device support has not been tested and may require some refinement. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Foundation
|
||||
import Virtualization
|
||||
|
||||
class RestoreImage: NSObject {
|
||||
private var downloadObserver: NSKeyValueObservation?
|
||||
|
||||
// MARK: Observe the download progress.
|
||||
|
||||
var restoreImageURL: URL
|
||||
|
||||
init(_ dest: URL) {
|
||||
restoreImageURL = dest
|
||||
}
|
||||
|
||||
public func download(completionHandler: @escaping () -> Void) {
|
||||
print("Attempting to download latest available restore image.")
|
||||
VZMacOSRestoreImage.fetchLatestSupported { [self](result: Result<VZMacOSRestoreImage, Error>) in
|
||||
switch result {
|
||||
case let .failure(error):
|
||||
fatalError(error.localizedDescription)
|
||||
|
||||
case let .success(restoreImage):
|
||||
downloadRestoreImage(restoreImage: restoreImage, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func downloadRestoreImage(restoreImage: VZMacOSRestoreImage, completionHandler: @escaping () -> Void) {
|
||||
let downloadTask = URLSession.shared.downloadTask(with: restoreImage.url) { localURL, response, error in
|
||||
if let error = error {
|
||||
fatalError("Download failed. \(error.localizedDescription).")
|
||||
}
|
||||
|
||||
do {
|
||||
try FileManager.default.moveItem(at: localURL!, to: self.restoreImageURL)
|
||||
} catch {
|
||||
fatalError("Failed to move downloaded restore image to \(self.restoreImageURL) \(error).")
|
||||
}
|
||||
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
var lastPct = 0
|
||||
downloadObserver = downloadTask.progress.observe(\.fractionCompleted, options: [.initial, .new]) { (progress, change) in
|
||||
let pct = Int(change.newValue! * 100)
|
||||
if pct != lastPct {
|
||||
print("Restore image download progress: \(pct)%")
|
||||
lastPct = pct
|
||||
}
|
||||
}
|
||||
downloadTask.resume()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user