diff --git a/Sources/ContainerizationOCI/Spec.swift b/Sources/ContainerizationOCI/Spec.swift index 5171925c..5c89c3ea 100644 --- a/Sources/ContainerizationOCI/Spec.swift +++ b/Sources/ContainerizationOCI/Spec.swift @@ -426,6 +426,22 @@ public struct Linux: Codable, Sendable { public var mountLabel: String public var personality: LinuxPersonality? + public enum CodingKeys: String, CodingKey { + case uidMappings + case gidMappings + case sysctl + case resources + case cgroupsPath + case namespaces + case devices + case seccomp + case rootfsPropagation + case maskedPaths + case readonlyPaths + case mountLabel + case personality + } + public init( uidMappings: [LinuxIDMapping] = [], gidMappings: [LinuxIDMapping] = [], @@ -455,6 +471,43 @@ public struct Linux: Codable, Sendable { self.mountLabel = mountLabel self.personality = personality } + + public init(from decoder: Decoder) throws { + self.init() + + let container = try decoder.container(keyedBy: CodingKeys.self) + if let uidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .uidMappings) { + self.uidMappings = uidMappings + } + if let gidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .gidMappings) { + self.gidMappings = gidMappings + } + self.sysctl = try container.decodeIfPresent([String: String].self, forKey: .sysctl) + self.resources = try container.decodeIfPresent(LinuxResources.self, forKey: .resources) + if let cgroupsPath = try container.decodeIfPresent(String.self, forKey: .cgroupsPath) { + self.cgroupsPath = cgroupsPath + } + if let namespaces = try container.decodeIfPresent([LinuxNamespace].self, forKey: .namespaces) { + self.namespaces = namespaces + } + if let devices = try container.decodeIfPresent([LinuxDevice].self, forKey: .devices) { + self.devices = devices + } + self.seccomp = try container.decodeIfPresent(LinuxSeccomp.self, forKey: .seccomp) + if let rootfsPropagation = try container.decodeIfPresent(String.self, forKey: .rootfsPropagation) { + self.rootfsPropagation = rootfsPropagation + } + if let maskedPaths = try container.decodeIfPresent([String].self, forKey: .maskedPaths) { + self.maskedPaths = maskedPaths + } + if let readonlyPaths = try container.decodeIfPresent([String].self, forKey: .readonlyPaths) { + self.readonlyPaths = readonlyPaths + } + if let mountLabel = try container.decodeIfPresent(String.self, forKey: .mountLabel) { + self.mountLabel = mountLabel + } + self.personality = try container.decodeIfPresent(LinuxPersonality.self, forKey: .personality) + } } public struct LinuxNamespace: Codable, Sendable { diff --git a/Tests/ContainerizationOCITests/OCISpecTests.swift b/Tests/ContainerizationOCITests/OCISpecTests.swift index 3507abff..9218bfdf 100644 --- a/Tests/ContainerizationOCITests/OCISpecTests.swift +++ b/Tests/ContainerizationOCITests/OCISpecTests.swift @@ -143,4 +143,26 @@ struct OCISpecTests { #expect(decodedSpec.uidMappings == nil) #expect(decodedSpec.gidMappings == nil) } + + @Test func minimalCapabilitiesDecode() throws { + let minCapabilitiesSpec = + """ + { + "ociVersion": "1.1.0", + "capabilities": { + "permitted": [ + "CAP_SYS_ADMIN" + ] + }, + "linux": {} + } + """ + + guard let data = minCapabilitiesSpec.data(using: .utf8) else { + Issue.record("test capabilities spec is not valid: \(minCapabilitiesSpec)") + return + } + + let _ = try JSONDecoder().decode(ContainerizationOCI.Spec.self, from: data) + } }