tailscale/tstest/tailmac/Swift/TailMac/RestoreImage.swift
Jonathan Nobels 8fad8c4b9b
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>
2024-08-19 15:01:19 -04:00

59 lines
1.9 KiB
Swift

// 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()
}
}