diff --git a/Sources/tart/OCI/Manifest.swift b/Sources/tart/OCI/Manifest.swift index 54f71de..3d4eddd 100644 --- a/Sources/tart/OCI/Manifest.swift +++ b/Sources/tart/OCI/Manifest.swift @@ -3,15 +3,38 @@ import Foundation let ociManifestMediaType = "application/vnd.oci.image.manifest.v1+json" let ociConfigMediaType = "application/vnd.oci.image.config.v1+json" +// Annotations +let uncompressedDiskSizeAnnotation = "org.cirruslabs.tart.uncompressed-disk-size" + struct OCIManifest: Codable, Equatable { var schemaVersion: Int = 2 var mediaType: String = ociManifestMediaType var config: OCIManifestConfig var layers: [OCIManifestLayer] = Array() + var annotations: Dictionary = Dictionary() + + init(config: OCIManifestConfig, layers: [OCIManifestLayer], uncompressedDiskSize: UInt64? = nil) { + self.config = config + self.layers = layers + + if let uncompressedDiskSize = uncompressedDiskSize { + annotations = [ + uncompressedDiskSizeAnnotation: String(uncompressedDiskSize) + ] + } + } func digest() throws -> String { try Digest.hash(JSONEncoder().encode(self)) } + + func uncompressedDiskSize() -> UInt64? { + guard let value = annotations[uncompressedDiskSizeAnnotation] else { + return nil + } + + return UInt64(value) + } } struct OCIManifestConfig: Codable, Equatable { diff --git a/Sources/tart/VMDirectory+OCI.swift b/Sources/tart/VMDirectory+OCI.swift index 11c57ad..4ac5a53 100644 --- a/Sources/tart/VMDirectory+OCI.swift +++ b/Sources/tart/VMDirectory+OCI.swift @@ -117,8 +117,10 @@ extension VMDirectory { // Read VM's compressed disk as chunks // and sequentially upload them as blobs let disk = try FileHandle(forReadingFrom: diskURL) + var diskReadBytes: UInt64 = 0 let compressingFilter = try InputFilter(.compress, using: .lz4, bufferCapacity: Self.bufferSizeBytes) { _ in let data = try disk.read(upToCount: Self.bufferSizeBytes) + diskReadBytes += UInt64(data?.count ?? 0) progress.completedUnitCount += Int64(data?.count ?? 0) @@ -146,7 +148,8 @@ extension VMDirectory { let ociConfigDigest = try await registry.pushBlob(fromData: ociConfigJSON) let manifest = OCIManifest( config: OCIManifestConfig(size: ociConfigJSON.count, digest: ociConfigDigest), - layers: layers + layers: layers, + uncompressedDiskSize: diskReadBytes ) // Manifest