import Foundation public enum VolumeInventory { /// Managed volumes are enriched with configured references. Persistent host /// mounts are displayed separately and can never be deleted as volumes. public static func combine(volumes: [Resource], containers: [Resource], includeHostMounts: Bool = true) -> [Resource] { var result = volumes var uses: [String: Set] = [:] var host: [String: Resource] = [:] for container in containers { guard let data = container.raw.data(using: .utf8), let row = try? JSONSerialization.jsonObject(with: data) as? [String: Any], let cfg = row["configuration"] as? [String: Any], let mounts = cfg["mounts"] as? [[String: Any]] else { continue } for mount in mounts { let type = mount["type"] as? [String: Any] ?? [:] let source = mount["source"] as? String ?? "" let destination = mount["destination"] as? String ?? "" let reference = "\(container.name):\(destination)" if let volume = type["volume"] as? [String: Any], let name = volume["name"] as? String { uses[name, default: []].insert(reference); continue } if let volume = volumes.first(where: { $0.source == source && !source.isEmpty }) { uses[volume.id, default: []].insert(reference); continue } guard includeHostMounts, !source.isEmpty, type["virtiofs"] != nil || type["block"] != nil else { continue } let id = "host:" + source var item = host[id] ?? Resource(id: id, name: URL(fileURLWithPath: source).lastPathComponent, state: "Referenced", image: "—", project: "", service: "", ports: "—", raw: ContainerService.pretty(mount)) item.source = source; item.storageKind = type["block"] == nil ? "Bind mount" : "Disk mount"; item.readOnlyStorage = true item.driver = type["block"] == nil ? "virtiofs" : "block"; item.format = "host"; item.usedBy = "" host[id] = item; uses[id, default: []].insert(reference) } } result += host.values for index in result.indices { let references = uses[result[index].id] ?? [] result[index].usedBy = references.isEmpty ? "No container references" : references.sorted().joined(separator: ", ") result[index].state = references.isEmpty ? "Unreferenced" : "Referenced" } return result.sorted { ($0.readOnlyStorage ? 1 : 0, $0.name) < ($1.readOnlyStorage ? 1 : 0, $1.name) } } }