mirror of https://github.com/cirruslabs/tart.git
186 lines
7.6 KiB
Swift
186 lines
7.6 KiB
Swift
import Foundation
|
|
import XCTest
|
|
@testable import tart
|
|
|
|
#if canImport(DiskImageKit)
|
|
import DiskImageKit
|
|
|
|
@available(macOS 27.0, *)
|
|
final class VMDirectoryDiskImageStackTests: XCTestCase {
|
|
override func setUpWithError() throws {
|
|
try super.setUpWithError()
|
|
|
|
if #unavailable(macOS 27.0) {
|
|
throw XCTSkip("DiskImageKit tests require macOS 27 or newer")
|
|
}
|
|
}
|
|
|
|
func testBaseBlockLayoutReadsRawAndASIFImages() throws {
|
|
let directory = try temporaryDirectory()
|
|
let rawURL = directory.appendingPathComponent("base.raw")
|
|
let asifURL = directory.appendingPathComponent("base.asif")
|
|
_ = try DiskImage(creating: .raw(url: rawURL, blockCount: 8))
|
|
_ = try DiskImage(creating: .asif(url: asifURL, blockCount: 16, blockSize: .bytes512))
|
|
|
|
XCTAssertEqual(try DiskImageStack.baseBlockLayout(at: rawURL, expectedFormat: .raw).blockCount, 8)
|
|
XCTAssertEqual(try DiskImageStack.baseBlockLayout(at: asifURL, expectedFormat: .asif).blockCount, 16)
|
|
}
|
|
|
|
func testCloneAsStackedBasePinsFlatManifestAndCreatesOverlay() throws {
|
|
let contentStore = try temporaryContentStore()
|
|
let source = try flatSource()
|
|
let destination = try temporaryVMDirectory()
|
|
|
|
try source.cloneAsStackedBase(to: destination, generateMAC: false, contentStore: contentStore)
|
|
|
|
XCTAssertTrue(destination.isStackedVM)
|
|
XCTAssertFalse(FileManager.default.fileExists(atPath: destination.diskURL.path))
|
|
|
|
let contentDigest = try Digest.hash(source.diskURL)
|
|
let manifest = try OCIManifest(fromJSON: Data(contentsOf: destination.manifestURL))
|
|
guard case .flat(let base) = try manifest.tartDiskRepresentation() else {
|
|
return XCTFail("expected a pinned base-only manifest")
|
|
}
|
|
XCTAssertEqual(base.contentDigest, contentDigest)
|
|
XCTAssertEqual(manifest.diskBlockSize(), 512)
|
|
XCTAssertEqual(manifest.diskBlockCount(), 8)
|
|
|
|
let stack = try destination.diskImageStack(contentStore: contentStore)
|
|
XCTAssertEqual(stack.baseURL, try contentStore.contentURL(for: contentDigest))
|
|
XCTAssertTrue(FileManager.default.fileExists(atPath: destination.overlayURL.path))
|
|
}
|
|
|
|
func testCloneAsStackedBaseSupportsASIFDisk() throws {
|
|
let contentStore = try temporaryContentStore()
|
|
let source = try flatSource(diskFormat: .asif)
|
|
let destination = try temporaryVMDirectory()
|
|
|
|
try source.cloneAsStackedBase(to: destination, generateMAC: false, contentStore: contentStore)
|
|
|
|
let stack = try destination.diskImageStack(contentStore: contentStore)
|
|
XCTAssertEqual(stack.baseFormat, .asif)
|
|
XCTAssertTrue(destination.isStackedVM)
|
|
_ = try stack.makeAttachment()
|
|
}
|
|
|
|
func testStackedCloneCanCopyOrCreateWritableOverlay() throws {
|
|
let contentStore = try temporaryContentStore()
|
|
let source = try flatSource()
|
|
let stacked = try temporaryVMDirectory()
|
|
try source.cloneAsStackedBase(to: stacked, generateMAC: false, contentStore: contentStore)
|
|
|
|
let copied = try temporaryVMDirectory()
|
|
try stacked.cloneStacked(to: copied, copyWritableOverlay: true, generateMAC: false, contentStore: contentStore)
|
|
XCTAssertEqual(try Digest.hash(copied.overlayURL), try Digest.hash(stacked.overlayURL))
|
|
|
|
let fresh = try temporaryVMDirectory()
|
|
try stacked.cloneStacked(to: fresh, copyWritableOverlay: false, generateMAC: false, contentStore: contentStore)
|
|
XCTAssertTrue(fresh.isStackedVM)
|
|
XCTAssertTrue(FileManager.default.fileExists(atPath: fresh.overlayURL.path))
|
|
}
|
|
|
|
func testResizeDiskGrowsWritableOverlay() throws {
|
|
let contentStore = try temporaryContentStore()
|
|
let source = try flatSource()
|
|
let stacked = try temporaryVMDirectory()
|
|
try source.cloneAsStackedBase(to: stacked, generateMAC: false, contentStore: contentStore)
|
|
|
|
try stacked.resizeDisk(1, contentStore: contentStore)
|
|
|
|
let image = try DiskImage(opening: .open(url: stacked.overlayURL, mode: .readOnly))
|
|
XCTAssertEqual(image.blockCount, 1_000_000_000 / 512)
|
|
XCTAssertEqual(try stacked.diskSizeBytes(), 1_000_000_000)
|
|
}
|
|
|
|
func testResolvesPublishedOverlayFromManifestAndCache() throws {
|
|
let contentStore = try temporaryContentStore()
|
|
let source = try flatSource()
|
|
let baseOnly = try temporaryVMDirectory()
|
|
try source.cloneAsStackedBase(to: baseOnly, generateMAC: false, contentStore: contentStore)
|
|
|
|
let contentDigest = try Digest.hash(baseOnly.overlayURL)
|
|
let temporaryContentURL = try contentStore.temporaryContentURL(for: contentDigest)
|
|
try FileManager.default.copyItem(at: baseOnly.overlayURL, to: temporaryContentURL)
|
|
_ = try contentStore.install(temporaryContentURL, contentDigest: contentDigest)
|
|
|
|
var manifest = try OCIManifest(fromJSON: Data(contentsOf: baseOnly.manifestURL))
|
|
var overlay = OCIManifestLayer(
|
|
mediaType: asifOverlayMediaType,
|
|
size: 1,
|
|
digest: "sha256:overlay-transport",
|
|
uncompressedSize: 1,
|
|
uncompressedContentDigest: "sha256:overlay-chunk"
|
|
)
|
|
overlay.annotations?[diskFileContentDigestAnnotation] = contentDigest
|
|
overlay.annotations?[diskFileChunkCountAnnotation] = "1"
|
|
manifest.layers.insert(overlay, at: manifest.layers.count - 1)
|
|
|
|
let destination = try temporaryVMDirectory()
|
|
try FileManager.default.copyItem(at: baseOnly.configURL, to: destination.configURL)
|
|
try FileManager.default.copyItem(at: baseOnly.nvramURL, to: destination.nvramURL)
|
|
try manifest.toJSON().write(to: destination.manifestURL)
|
|
|
|
let stack = try destination.diskImageStack(contentStore: contentStore)
|
|
XCTAssertEqual(stack.immutableOverlayURLs, [try contentStore.contentURL(for: contentDigest)])
|
|
try stack.createWritableOverlay()
|
|
_ = try stack.makeAttachment()
|
|
}
|
|
|
|
private func flatSource(diskFormat: DiskImageFormat = .raw) throws -> VMDirectory {
|
|
let vmDir = try temporaryVMDirectory()
|
|
let config = VMConfig(
|
|
platform: Linux(),
|
|
cpuCountMin: 2,
|
|
memorySizeMin: 512 * 1024 * 1024,
|
|
diskFormat: diskFormat
|
|
)
|
|
try config.save(toURL: vmDir.configURL)
|
|
XCTAssertTrue(FileManager.default.createFile(atPath: vmDir.nvramURL.path, contents: Data()))
|
|
switch diskFormat {
|
|
case .raw:
|
|
_ = try DiskImage(creating: .raw(url: vmDir.diskURL, blockCount: 8))
|
|
case .asif:
|
|
_ = try DiskImage(creating: .asif(url: vmDir.diskURL, blockCount: 8, blockSize: .bytes512))
|
|
}
|
|
|
|
let diskChunk = OCIManifestLayer(
|
|
mediaType: diskV2MediaType,
|
|
size: 1,
|
|
digest: "sha256:transport",
|
|
uncompressedSize: 4096,
|
|
uncompressedContentDigest: "sha256:chunk"
|
|
)
|
|
let manifest = OCIManifest(
|
|
config: OCIManifestConfig(size: 1, digest: "sha256:oci-config"),
|
|
layers: [
|
|
OCIManifestLayer(mediaType: configMediaType, size: 1, digest: "sha256:config"),
|
|
diskChunk,
|
|
OCIManifestLayer(mediaType: nvramMediaType, size: 1, digest: "sha256:nvram"),
|
|
]
|
|
)
|
|
try manifest.toJSON().write(to: vmDir.manifestURL)
|
|
|
|
return vmDir
|
|
}
|
|
|
|
private func temporaryContentStore() throws -> ContentStore {
|
|
let url = try temporaryDirectory()
|
|
return try ContentStore(baseURL: url)
|
|
}
|
|
|
|
private func temporaryVMDirectory() throws -> VMDirectory {
|
|
VMDirectory(baseURL: try temporaryDirectory())
|
|
}
|
|
|
|
private func temporaryDirectory() throws -> URL {
|
|
let url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false)
|
|
addTeardownBlock {
|
|
try? FileManager.default.removeItem(at: url)
|
|
}
|
|
|
|
return url
|
|
}
|
|
}
|
|
#endif
|