diff --git a/Sources/tart/Commands/Push.swift b/Sources/tart/Commands/Push.swift index 5b48b21..ce67bdd 100644 --- a/Sources/tart/Commands/Push.swift +++ b/Sources/tart/Commands/Push.swift @@ -31,9 +31,6 @@ struct Push: AsyncParsableCommand { discussion: "Can be specified multiple times to attach multiple labels.")) var labels: [String] = [] - @Option(help: .hidden) - var diskFormat: String = "v2" - @Flag(help: ArgumentHelp("cache pushed images locally", discussion: "Increases disk usage, but saves time if you're going to pull the pushed images later.")) var populateCache: Bool = false @@ -85,7 +82,6 @@ struct Push: AsyncParsableCommand { registry: registry, references: references, chunkSizeMb: chunkSize, - diskFormat: diskFormat, concurrency: concurrency, labels: parseLabels() ) diff --git a/Sources/tart/OCI/Layerizer/DiskV1.swift b/Sources/tart/OCI/Layerizer/DiskV1.swift deleted file mode 100644 index 9c59a00..0000000 --- a/Sources/tart/OCI/Layerizer/DiskV1.swift +++ /dev/null @@ -1,75 +0,0 @@ -import Foundation -import Compression - -class DiskV1: Disk { - private static let bufferSizeBytes = 4 * 1024 * 1024 - private static let layerLimitBytes = 500 * 1000 * 1000 - - static func push(diskURL: URL, registry: Registry, chunkSizeMb: Int, concurrency: UInt, progress: Progress) async throws -> [OCIManifestLayer] { - var pushedLayers: [OCIManifestLayer] = [] - - // Open the disk file - let mappedDisk = try Data(contentsOf: diskURL, options: [.alwaysMapped]) - var mappedDiskReadOffset = 0 - - // Compress the disk file as a single stream - let compressingFilter = try InputFilter(.compress, using: .lz4, bufferCapacity: Self.bufferSizeBytes) { (length: Int) -> Data? in - // Determine the size of the next chunk - let bytesRead = min(length, mappedDisk.count - mappedDiskReadOffset) - - // Read the next uncompressed chunk - let data = mappedDisk.subdata(in: mappedDiskReadOffset ..< mappedDiskReadOffset + bytesRead) - - // Advance the offset - mappedDiskReadOffset += bytesRead - - // Provide the uncompressed chunk to the compressing filter - return data - } - - // Cut the compressed stream into layers, each equal exactly ``Self.layerLimitBytes`` bytes, - // except for the last one, which may be smaller - while let compressedData = try compressingFilter.readData(ofLength: Self.layerLimitBytes) { - let layerDigest = try await registry.pushBlob(fromData: compressedData, chunkSizeMb: chunkSizeMb) - - pushedLayers.append(OCIManifestLayer( - mediaType: diskV1MediaType, - size: compressedData.count, - digest: layerDigest - )) - - // Update progress using an absolute value - progress.completedUnitCount = Int64(mappedDiskReadOffset) - } - - return pushedLayers - } - - static func pull(registry: Registry, diskLayers: [OCIManifestLayer], diskURL: URL, concurrency: UInt, progress: Progress, localLayerCache: LocalLayerCache? = nil, deduplicate: Bool = false) async throws { - if !FileManager.default.createFile(atPath: diskURL.path, contents: nil) { - throw OCIError.FailedToCreateVmFile - } - - // Open the disk file - let disk = try FileHandle(forWritingTo: diskURL) - defer { try! disk.close() } - - // Decompress the layers onto the disk in a single stream - let filter = try OutputFilter(.decompress, using: .lz4, bufferCapacity: Self.bufferSizeBytes) { data in - if let data = data { - try disk.write(contentsOf: data) - } - } - - for diskLayer in diskLayers { - try await registry.pullBlob(diskLayer.digest) { data in - try filter.write(data) - - // Update the progress - progress.completedUnitCount += Int64(data.count) - } - } - - try filter.finalize() - } -} diff --git a/Sources/tart/OCI/Manifest.swift b/Sources/tart/OCI/Manifest.swift index 363ab6a..897bd97 100644 --- a/Sources/tart/OCI/Manifest.swift +++ b/Sources/tart/OCI/Manifest.swift @@ -6,7 +6,6 @@ let ociConfigMediaType = "application/vnd.oci.image.config.v1+json" // Layer media types let configMediaType = "application/vnd.cirruslabs.tart.config.v1" -let diskV1MediaType = "application/vnd.cirruslabs.tart.disk.v1" let diskV2MediaType = "application/vnd.cirruslabs.tart.disk.v2" let nvramMediaType = "application/vnd.cirruslabs.tart.nvram.v1" diff --git a/Sources/tart/VMDirectory+OCI.swift b/Sources/tart/VMDirectory+OCI.swift index 54cac16..50d3c55 100644 --- a/Sources/tart/VMDirectory+OCI.swift +++ b/Sources/tart/VMDirectory+OCI.swift @@ -29,16 +29,8 @@ extension VMDirectory { try configFile.close() // Pull VM's disk layers and decompress them into a disk file - let diskImplType: Disk.Type - let layers: [OCIManifestLayer] - - if manifest.layers.contains(where: { $0.mediaType == diskV1MediaType }) { - diskImplType = DiskV1.self - layers = manifest.layers.filter { $0.mediaType == diskV1MediaType } - } else if manifest.layers.contains(where: { $0.mediaType == diskV2MediaType }) { - diskImplType = DiskV2.self - layers = manifest.layers.filter { $0.mediaType == diskV2MediaType } - } else { + let layers = manifest.layers.filter { $0.mediaType == diskV2MediaType } + if layers.isEmpty { throw OCIError.ShouldBeAtLeastOneLayer } @@ -55,10 +47,10 @@ extension VMDirectory { ProgressObserver(progress).log(defaultLogger) do { - try await diskImplType.pull(registry: registry, diskLayers: layers, diskURL: diskURL, - concurrency: concurrency, progress: progress, - localLayerCache: localLayerCache, - deduplicate: deduplicate) + try await DiskV2.pull(registry: registry, diskLayers: layers, diskURL: diskURL, + concurrency: concurrency, progress: progress, + localLayerCache: localLayerCache, + deduplicate: deduplicate) } catch let error where error is FilterError { throw RuntimeError.PullFailed("failed to decompress disk: \(error.localizedDescription)") } @@ -90,7 +82,7 @@ extension VMDirectory { try manifest.toJSON().write(to: manifestURL) } - func pushToRegistry(registry: Registry, references: [String], chunkSizeMb: Int, diskFormat: String, concurrency: UInt, labels: [String: String] = [:]) async throws -> RemoteName { + func pushToRegistry(registry: Registry, references: [String], chunkSizeMb: Int, concurrency: UInt, labels: [String: String] = [:]) async throws -> RemoteName { var layers = Array() // Read VM's config and push it as blob @@ -111,14 +103,7 @@ extension VMDirectory { let progress = Progress(totalUnitCount: diskSize) ProgressObserver(progress).log(defaultLogger) - switch diskFormat { - case "v1": - layers.append(contentsOf: try await DiskV1.push(diskURL: diskURL, registry: registry, chunkSizeMb: chunkSizeMb, concurrency: concurrency, progress: progress)) - case "v2": - layers.append(contentsOf: try await DiskV2.push(diskURL: diskURL, registry: registry, chunkSizeMb: chunkSizeMb, concurrency: concurrency, progress: progress)) - default: - throw RuntimeError.OCIUnsupportedDiskFormat(diskFormat) - } + layers.append(contentsOf: try await DiskV2.push(diskURL: diskURL, registry: registry, chunkSizeMb: chunkSizeMb, concurrency: concurrency, progress: progress)) // Read VM's NVRAM and push it as blob defaultLogger.appendNewLine("pushing NVRAM...") diff --git a/Sources/tart/VMStorageHelper.swift b/Sources/tart/VMStorageHelper.swift index 3e3eb58..89a47ad 100644 --- a/Sources/tart/VMStorageHelper.swift +++ b/Sources/tart/VMStorageHelper.swift @@ -74,7 +74,6 @@ enum RuntimeError : Error { case ImportFailed(_ message: String) case SoftnetFailed(_ message: String) case OCIStorageError(_ message: String) - case OCIUnsupportedDiskFormat(_ format: String) case SuspendFailed(_ message: String) case PullFailed(_ message: String) case VirtualMachineLimitExceeded(_ hint: String) @@ -139,8 +138,6 @@ extension RuntimeError : CustomStringConvertible { return "Softnet failed: \(message)" case .OCIStorageError(let message): return "OCI storage error: \(message)" - case .OCIUnsupportedDiskFormat(let format): - return "OCI disk format \(format) is not supported by this version of Tart" case .SuspendFailed(let message): return "Failed to suspend the VM: \(message)" case .PullFailed(let message): diff --git a/Tests/TartTests/LayerizerTests.swift b/Tests/TartTests/LayerizerTests.swift index e170f3c..7ee3f54 100644 --- a/Tests/TartTests/LayerizerTests.swift +++ b/Tests/TartTests/LayerizerTests.swift @@ -24,27 +24,6 @@ final class LayerizerTests: XCTestCase { registryRunner = nil } - func testDiskV1() async throws { - // Original disk file to be pushed to the registry - let originalDiskFileURL = try fileWithRandomData(sizeBytes: 5 * 1024 * 1024 * 1024) - addTeardownBlock { - try FileManager.default.removeItem(at: originalDiskFileURL) - } - - // Disk file to be pulled from the registry - // and compared against the original disk file - let pulledDiskFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - - print("pushing disk...") - let diskLayers = try await DiskV1.push(diskURL: originalDiskFileURL, registry: registry, chunkSizeMb: 0, concurrency: 4, progress: Progress()) - - print("pulling disk...") - try await DiskV1.pull(registry: registry, diskLayers: diskLayers, diskURL: pulledDiskFileURL, concurrency: 16, progress: Progress()) - - print("comparing disks...") - try XCTAssertEqual(Digest.hash(originalDiskFileURL), Digest.hash(pulledDiskFileURL)) - } - func testDiskV2() async throws { // Original disk file to be pushed to the registry let originalDiskFileURL = try fileWithRandomData(sizeBytes: 5 * 1024 * 1024 * 1024)