[networks]: add decoder/encoder to Attachment and NetworkStatus (#1216)

## Type of Change
- [x] Bug fix
- [ ] New feature  
- [x] Breaking change
- [ ] Documentation update

## Motivation and Context

Adds backward-compatible JSON decoding/encoding fallback for
`Attachment` and `NetworkStatus` to prevent `keyNotFound` errors when
communicating with older `container-apiserver` versions.

Temporary fix but closes #1196

## Testing

Tests  based on comment
[here](https://github.com/apple/container/issues/1196#issuecomment-3902754548)

---------

Co-authored-by: Ed Saipetch <ed@twentybelow.com>
This commit is contained in:
Saehej Kang
2026-03-17 10:35:30 -07:00
committed by GitHub
co-authored by Ed Saipetch
parent 85ec7ed543
commit 9fd15f01bf
3 changed files with 86 additions and 1 deletions
@@ -51,4 +51,52 @@ public struct Attachment: Codable, Sendable {
self.macAddress = macAddress
self.mtu = mtu
}
enum CodingKeys: String, CodingKey {
case network
case hostname
case ipv4Address
case ipv4Gateway
case ipv6Address
case macAddress
case mtu
// TODO: retain for deserialization compatibility for now, remove later
case address
case gateway
}
/// Create a configuration from the supplied Decoder, initializing missing
/// values where possible to reasonable defaults.
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
network = try container.decode(String.self, forKey: .network)
hostname = try container.decode(String.self, forKey: .hostname)
if let address = try? container.decode(CIDRv4.self, forKey: .ipv4Address) {
ipv4Address = address
} else {
ipv4Address = try container.decode(CIDRv4.self, forKey: .address)
}
if let gateway = try? container.decode(IPv4Address.self, forKey: .ipv4Gateway) {
ipv4Gateway = gateway
} else {
ipv4Gateway = try container.decode(IPv4Address.self, forKey: .gateway)
}
ipv6Address = try container.decodeIfPresent(CIDRv6.self, forKey: .ipv6Address)
macAddress = try container.decodeIfPresent(MACAddress.self, forKey: .macAddress)
mtu = try container.decodeIfPresent(UInt32.self, forKey: .mtu)
}
/// Encode the configuration to the supplied Encoder.
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(network, forKey: .network)
try container.encode(hostname, forKey: .hostname)
try container.encode(ipv4Address, forKey: .ipv4Address)
try container.encode(ipv4Gateway, forKey: .ipv4Gateway)
try container.encodeIfPresent(ipv6Address, forKey: .ipv6Address)
try container.encodeIfPresent(macAddress, forKey: .macAddress)
try container.encodeIfPresent(mtu, forKey: .mtu)
}
}
@@ -80,7 +80,7 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
case ipv6Subnet
case labels
case pluginInfo
// TODO: retain for deserialization compatability for now, remove later
// TODO: retain for deserialization compatibility for now, remove later
case subnet
}
@@ -39,6 +39,43 @@ public struct NetworkStatus: Codable, Sendable {
self.ipv4Gateway = ipv4Gateway
self.ipv6Subnet = ipv6Subnet
}
enum CodingKeys: String, CodingKey {
case ipv4Subnet
case ipv4Gateway
case ipv6Subnet
// TODO: retain for deserialization compatibility for now, remove later
case address
case gateway
}
/// Create a configuration from the supplied Decoder, initializing missing
/// values where possible to reasonable defaults.
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let address = try? container.decode(CIDRv4.self, forKey: .ipv4Subnet) {
ipv4Subnet = address
} else {
ipv4Subnet = try container.decode(CIDRv4.self, forKey: .address)
}
if let gateway = try? container.decode(IPv4Address.self, forKey: .ipv4Gateway) {
ipv4Gateway = gateway
} else {
ipv4Gateway = try container.decode(IPv4Address.self, forKey: .gateway)
}
ipv6Subnet = try container.decodeIfPresent(String.self, forKey: .ipv6Subnet)
.map { try CIDRv6($0) }
}
/// Encode the configuration to the supplied Encoder.
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(ipv4Subnet, forKey: .ipv4Subnet)
try container.encode(ipv4Gateway, forKey: .ipv4Gateway)
try container.encodeIfPresent(ipv6Subnet, forKey: .ipv6Subnet)
}
}
/// The configuration and runtime attributes for a network.