mirror of https://github.com/cirruslabs/tart.git
Show both size and actual size of files (#742)
Right now we show only actual size of files on disk which excludes empty blocks of the recently introduced sparced format in #671. This makes impossible to get info about disk size that we just set via `tart set`. Here is an example of `tart list` output before the change: ``` Source Name Size State local sonoma-base 22 stopped local sonoma-vanilla 18 stopped local sonoma-xcode 67 stopped local ubuntu 1 stopped oci ghcr.io/cirruslabs/macos-sonoma-base:latest 22 stopped oci ghcr.io/cirruslabs/macos-sonoma-base@sha256:16c1593bbaf787b20b3c0bc094c5b6baf71c937d22c2e4596da85ac55c92e6cc 22 stopped oci ghcr.io/cirruslabs/macos-sonoma-vanilla:14.3 17 stopped oci ghcr.io/cirruslabs/macos-sonoma-vanilla@sha256:23c4e853d48d00a4333346d66a32b2b5aad900cc0dc10e7ecb9dbe67b6f587f4 17 stopped oci ghcr.io/cirruslabs/macos-sonoma-xcode:latest 67 stopped oci ghcr.io/cirruslabs/macos-sonoma-xcode@sha256:d0cb8d01424a68b89e0f16f5371bf2152b2c115bd886341a6ba8da42121d1f41 67 stopped oci ghcr.io/cirruslabs/ubuntu:22.04 1 stopped oci ghcr.io/cirruslabs/ubuntu@sha256:037763feb7a15d6077edeb7a097738c34313637d16036764b4c196d28d8b429c 1 stopped ``` And here is the output after the change: ``` Source Name Disk Size State local sonoma-base 50 22 stopped local sonoma-vanilla 50 18 stopped local sonoma-xcode 90 67 stopped local ubuntu 20 1 stopped oci ghcr.io/cirruslabs/macos-sonoma-base:latest 50 22 stopped oci ghcr.io/cirruslabs/macos-sonoma-base@sha256:16c1593bbaf787b20b3c0bc094c5b6baf71c937d22c2e4596da85ac55c92e6cc 50 22 stopped oci ghcr.io/cirruslabs/macos-sonoma-vanilla:14.3 50 17 stopped oci ghcr.io/cirruslabs/macos-sonoma-vanilla@sha256:23c4e853d48d00a4333346d66a32b2b5aad900cc0dc10e7ecb9dbe67b6f587f4 50 17 stopped oci ghcr.io/cirruslabs/macos-sonoma-xcode:latest 90 67 stopped oci ghcr.io/cirruslabs/macos-sonoma-xcode@sha256:d0cb8d01424a68b89e0f16f5371bf2152b2c115bd886341a6ba8da42121d1f41 90 67 stopped oci ghcr.io/cirruslabs/ubuntu:22.04 20 1 stopped oci ghcr.io/cirruslabs/ubuntu@sha256:037763feb7a15d6077edeb7a097738c34313637d16036764b4c196d28d8b429c 20 1 stopped ``` Additionally, `tart get` will print actual size with a 3 decimal point precision which will help to track growth in disk images for our templates. `tart get` before: ``` CPU Memory Disk Display State 4 8192 67 1024x768 stopped ``` `tart get` after: ``` CPU Memory Disk Size Display State 4 8192 90 67.333 1024x768 stopped ```
This commit is contained in:
parent
a40e104c03
commit
5c7743b7cd
|
|
@ -73,7 +73,7 @@ struct Clone: AsyncParsableCommand {
|
|||
// APFS is doing copy-on-write so the above cloning operation (just copying files on disk)
|
||||
// is not actually claiming new space until the VM is started and it writes something to disk.
|
||||
// So once we clone the VM let's try to claim a little bit of space for the VM to run.
|
||||
try Prune.reclaimIfNeeded(UInt64(sourceVM.sizeBytes()), sourceVM)
|
||||
try Prune.reclaimIfNeeded(UInt64(sourceVM.allocatedSizeBytes()), sourceVM)
|
||||
}, onCancel: {
|
||||
try? FileManager.default.removeItem(at: tmpVMDir.baseURL)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ fileprivate struct VMInfo: Encodable {
|
|||
let CPU: Int
|
||||
let Memory: UInt64
|
||||
let Disk: Int
|
||||
let Size: String
|
||||
let Display: String
|
||||
let Running: Bool
|
||||
let State: String
|
||||
|
|
@ -22,10 +23,9 @@ struct Get: AsyncParsableCommand {
|
|||
func run() async throws {
|
||||
let vmDir = try VMStorageLocal().open(name)
|
||||
let vmConfig = try VMConfig(fromURL: vmDir.configURL)
|
||||
let diskSizeInGb = try vmDir.sizeGB()
|
||||
let memorySizeInMb = vmConfig.memorySize / 1024 / 1024
|
||||
|
||||
let info = VMInfo(CPU: vmConfig.cpuCount, Memory: memorySizeInMb, Disk: diskSizeInGb,
|
||||
let info = VMInfo(CPU: vmConfig.cpuCount, Memory: memorySizeInMb, Disk: try vmDir.sizeGB(), Size: String(format: "%.3f", Float(try vmDir.allocatedSizeBytes()) / 1000 / 1000 / 1000),
|
||||
Display: vmConfig.display.description, Running: try vmDir.running(), State: try vmDir.state())
|
||||
print(format.renderSingle(info))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import SwiftUI
|
|||
fileprivate struct VMInfo: Encodable {
|
||||
let Source: String
|
||||
let Name: String
|
||||
let Disk: Int
|
||||
let Size: Int
|
||||
let Running: Bool
|
||||
let State: String
|
||||
|
|
@ -37,13 +38,13 @@ struct List: AsyncParsableCommand {
|
|||
|
||||
if source == nil || source == "local" {
|
||||
infos += sortedInfos(try VMStorageLocal().list().map { (name, vmDir) in
|
||||
try VMInfo(Source: "local", Name: name, Size: vmDir.sizeGB(), Running: vmDir.running(), State: vmDir.state())
|
||||
try VMInfo(Source: "local", Name: name, Disk: vmDir.sizeGB(), Size: vmDir.allocatedSizeGB(), Running: vmDir.running(), State: vmDir.state())
|
||||
})
|
||||
}
|
||||
|
||||
if source == nil || source == "oci" {
|
||||
infos += sortedInfos(try VMStorageOCI().list().map { (name, vmDir, _) in
|
||||
try VMInfo(Source: "oci", Name: name, Size: vmDir.sizeGB(), Running: vmDir.running(), State: vmDir.state())
|
||||
try VMInfo(Source: "oci", Name: name, Disk: vmDir.sizeGB(), Size: vmDir.allocatedSizeGB(), Running: vmDir.running(), State: vmDir.state())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ struct Prune: AsyncParsableCommand {
|
|||
var prunablesToDelete: [Prunable] = []
|
||||
|
||||
for prunable in prunables {
|
||||
let prunableSizeBytes = UInt64(try prunable.sizeBytes())
|
||||
let prunableSizeBytes = UInt64(try prunable.allocatedSizeBytes())
|
||||
|
||||
if prunableSizeBytes <= spaceBudgetBytes {
|
||||
// Don't mark for deletion as
|
||||
|
|
@ -158,7 +158,7 @@ struct Prune: AsyncParsableCommand {
|
|||
.sorted { try $0.accessDate() < $1.accessDate() }
|
||||
|
||||
// Does it even make sense to start?
|
||||
let cacheUsedBytes = try prunables.map { try $0.sizeBytes() }.reduce(0, +)
|
||||
let cacheUsedBytes = try prunables.map { try $0.allocatedSizeBytes() }.reduce(0, +)
|
||||
if cacheUsedBytes < reclaimBytes {
|
||||
return
|
||||
}
|
||||
|
|
@ -177,9 +177,9 @@ struct Prune: AsyncParsableCommand {
|
|||
continue
|
||||
}
|
||||
|
||||
try SentrySDK.span?.setData(value: prunable.sizeBytes(), key: prunable.url.path)
|
||||
try SentrySDK.span?.setData(value: prunable.allocatedSizeBytes(), key: prunable.url.path)
|
||||
|
||||
cacheReclaimedBytes += try prunable.sizeBytes()
|
||||
cacheReclaimedBytes += try prunable.allocatedSizeBytes()
|
||||
|
||||
try prunable.delete()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,8 @@ protocol Prunable {
|
|||
var url: URL { get }
|
||||
func delete() throws
|
||||
func accessDate() throws -> Date
|
||||
// size on disk as seen in Finder including empty blocks
|
||||
func sizeBytes() throws -> Int
|
||||
// actual size on disk without empty blocks
|
||||
func allocatedSizeBytes() throws -> Int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ extension URL: Prunable {
|
|||
try FileManager.default.removeItem(at: self)
|
||||
}
|
||||
|
||||
func sizeBytes() throws -> Int {
|
||||
func allocatedSizeBytes() throws -> Int {
|
||||
try resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize!
|
||||
}
|
||||
|
||||
func sizeBytes() throws -> Int {
|
||||
try resourceValues(forKeys: [.totalFileSizeKey]).totalFileSize!
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,6 +165,14 @@ struct VMDirectory: Prunable {
|
|||
try baseURL.accessDate()
|
||||
}
|
||||
|
||||
func allocatedSizeBytes() throws -> Int {
|
||||
try configURL.allocatedSizeBytes() + diskURL.allocatedSizeBytes() + nvramURL.allocatedSizeBytes()
|
||||
}
|
||||
|
||||
func allocatedSizeGB() throws -> Int {
|
||||
try allocatedSizeBytes() / 1000 / 1000 / 1000
|
||||
}
|
||||
|
||||
func sizeBytes() throws -> Int {
|
||||
try configURL.sizeBytes() + diskURL.sizeBytes() + nvramURL.sizeBytes()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue