Skip to content

Commit 5ac4066

Browse files
authored
Add custom decoder to Linux struct for optional fields (#455)
Allows decoding of minimal OCI specs with empty linux objects by providing default values for missing fields. Also added unit test to ensure that empty Linux struct {} works correctly with the fix.
1 parent 8865bc9 commit 5ac4066

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

Sources/ContainerizationOCI/Spec.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,22 @@ public struct Linux: Codable, Sendable {
426426
public var mountLabel: String
427427
public var personality: LinuxPersonality?
428428

429+
public enum CodingKeys: String, CodingKey {
430+
case uidMappings
431+
case gidMappings
432+
case sysctl
433+
case resources
434+
case cgroupsPath
435+
case namespaces
436+
case devices
437+
case seccomp
438+
case rootfsPropagation
439+
case maskedPaths
440+
case readonlyPaths
441+
case mountLabel
442+
case personality
443+
}
444+
429445
public init(
430446
uidMappings: [LinuxIDMapping] = [],
431447
gidMappings: [LinuxIDMapping] = [],
@@ -455,6 +471,43 @@ public struct Linux: Codable, Sendable {
455471
self.mountLabel = mountLabel
456472
self.personality = personality
457473
}
474+
475+
public init(from decoder: Decoder) throws {
476+
self.init()
477+
478+
let container = try decoder.container(keyedBy: CodingKeys.self)
479+
if let uidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .uidMappings) {
480+
self.uidMappings = uidMappings
481+
}
482+
if let gidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .gidMappings) {
483+
self.gidMappings = gidMappings
484+
}
485+
self.sysctl = try container.decodeIfPresent([String: String].self, forKey: .sysctl)
486+
self.resources = try container.decodeIfPresent(LinuxResources.self, forKey: .resources)
487+
if let cgroupsPath = try container.decodeIfPresent(String.self, forKey: .cgroupsPath) {
488+
self.cgroupsPath = cgroupsPath
489+
}
490+
if let namespaces = try container.decodeIfPresent([LinuxNamespace].self, forKey: .namespaces) {
491+
self.namespaces = namespaces
492+
}
493+
if let devices = try container.decodeIfPresent([LinuxDevice].self, forKey: .devices) {
494+
self.devices = devices
495+
}
496+
self.seccomp = try container.decodeIfPresent(LinuxSeccomp.self, forKey: .seccomp)
497+
if let rootfsPropagation = try container.decodeIfPresent(String.self, forKey: .rootfsPropagation) {
498+
self.rootfsPropagation = rootfsPropagation
499+
}
500+
if let maskedPaths = try container.decodeIfPresent([String].self, forKey: .maskedPaths) {
501+
self.maskedPaths = maskedPaths
502+
}
503+
if let readonlyPaths = try container.decodeIfPresent([String].self, forKey: .readonlyPaths) {
504+
self.readonlyPaths = readonlyPaths
505+
}
506+
if let mountLabel = try container.decodeIfPresent(String.self, forKey: .mountLabel) {
507+
self.mountLabel = mountLabel
508+
}
509+
self.personality = try container.decodeIfPresent(LinuxPersonality.self, forKey: .personality)
510+
}
458511
}
459512

460513
public struct LinuxNamespace: Codable, Sendable {

Tests/ContainerizationOCITests/OCISpecTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,26 @@ struct OCISpecTests {
143143
#expect(decodedSpec.uidMappings == nil)
144144
#expect(decodedSpec.gidMappings == nil)
145145
}
146+
147+
@Test func minimalCapabilitiesDecode() throws {
148+
let minCapabilitiesSpec =
149+
"""
150+
{
151+
"ociVersion": "1.1.0",
152+
"capabilities": {
153+
"permitted": [
154+
"CAP_SYS_ADMIN"
155+
]
156+
},
157+
"linux": {}
158+
}
159+
"""
160+
161+
guard let data = minCapabilitiesSpec.data(using: .utf8) else {
162+
Issue.record("test capabilities spec is not valid: \(minCapabilitiesSpec)")
163+
return
164+
}
165+
166+
let _ = try JSONDecoder().decode(ContainerizationOCI.Spec.self, from: data)
167+
}
146168
}

0 commit comments

Comments
 (0)