Skip to content

Commit 4a6084c

Browse files
authored
Remove network variant computation from API server. (#1814)
- Closes #1812. - The network plugin is the source of truth for the variant, if any, that applies to the network. Resolving a missing variant configuration option in the API server can create a situation where the variant the runtime uses for interface selection is incorrect. - Adds serial suites trait to tests to see whether it helps current CI issues. ## Type of Change - [x] Bug fix - [ ] New feature - [ ] Breaking change - [ ] Documentation update ## Motivation and Context Fixes a flaw in our interface strategy logic. ## Testing - [x] Tested locally - [ ] Added/updated tests - [ ] Added/updated docs
1 parent 137b3bd commit 4a6084c

9 files changed

Lines changed: 33 additions & 25 deletions

File tree

Sources/ContainerResource/Network/Attachment.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public struct Attachment: Codable, Sendable {
3333
public let macAddress: MACAddress?
3434
/// The MTU for the network interface.
3535
public let mtu: UInt32?
36+
/// The network plugin variant, used by the runtime to select an interface strategy.
37+
public let variant: String?
3638

3739
public init(
3840
network: String,
@@ -41,7 +43,8 @@ public struct Attachment: Codable, Sendable {
4143
ipv4Gateway: IPv4Address,
4244
ipv6Address: CIDRv6?,
4345
macAddress: MACAddress?,
44-
mtu: UInt32? = nil
46+
mtu: UInt32? = nil,
47+
variant: String? = nil
4548
) {
4649
self.network = network
4750
self.hostname = hostname
@@ -50,6 +53,7 @@ public struct Attachment: Codable, Sendable {
5053
self.ipv6Address = ipv6Address
5154
self.macAddress = macAddress
5255
self.mtu = mtu
56+
self.variant = variant
5357
}
5458

5559
enum CodingKeys: String, CodingKey {
@@ -60,6 +64,7 @@ public struct Attachment: Codable, Sendable {
6064
case ipv6Address
6165
case macAddress
6266
case mtu
67+
case variant
6368
// TODO: retain for deserialization compatibility for now, remove later
6469
case address
6570
case gateway
@@ -85,6 +90,7 @@ public struct Attachment: Codable, Sendable {
8590
ipv6Address = try container.decodeIfPresent(CIDRv6.self, forKey: .ipv6Address)
8691
macAddress = try container.decodeIfPresent(MACAddress.self, forKey: .macAddress)
8792
mtu = try container.decodeIfPresent(UInt32.self, forKey: .mtu)
93+
variant = try container.decodeIfPresent(String.self, forKey: .variant)
8894
}
8995

9096
/// Encode the configuration to the supplied Encoder.
@@ -98,5 +104,6 @@ public struct Attachment: Codable, Sendable {
98104
try container.encodeIfPresent(ipv6Address, forKey: .ipv6Address)
99105
try container.encodeIfPresent(macAddress, forKey: .macAddress)
100106
try container.encodeIfPresent(mtu, forKey: .mtu)
107+
try container.encodeIfPresent(variant, forKey: .variant)
101108
}
102109
}

Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,10 @@ public actor ContainersService {
422422

423423
var networkBootstrapInfos = [NetworkBootstrapInfo]()
424424
for n in config.networks {
425-
guard let (plugin, options) = try await self.networksService?.pluginConfiguration(id: n.network) else {
426-
throw ContainerizationError(.internalError, message: "failed to get plugin configuration for network \(n.network)")
425+
guard let plugin = try await self.networksService?.plugin(for: n.network) else {
426+
throw ContainerizationError(.internalError, message: "failed to get plugin for network \(n.network)")
427427
}
428-
networkBootstrapInfos.append(NetworkBootstrapInfo(plugin: plugin, options: options))
428+
networkBootstrapInfos.append(NetworkBootstrapInfo(plugin: plugin))
429429
}
430430

431431
do {

Sources/Services/ContainerAPIService/Server/Networks/NetworksService.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -314,19 +314,11 @@ public actor NetworksService {
314314
}
315315
}
316316

317-
public func pluginConfiguration(id: String) throws -> (plugin: String, options: [String: String]) {
317+
public func plugin(for id: String) throws -> String {
318318
guard let serviceState = serviceStates[id] else {
319319
throw ContainerizationError(.notFound, message: "no network for id \(id)")
320320
}
321-
var options = serviceState.configuration.options
322-
if options["variant"] == nil {
323-
if #available(macOS 26, *) {
324-
options["variant"] = "reserved"
325-
} else {
326-
options["variant"] = "allocationOnly"
327-
}
328-
}
329-
return (plugin: serviceState.configuration.plugin, options: options)
321+
return serviceState.configuration.plugin
330322
}
331323

332324
private static func getClient(configuration: NetworkConfiguration) throws -> ContainerNetworkClient.NetworkClient {

Sources/Services/Network/Server/DefaultNetworkService.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ public actor DefaultNetworkService: NetworkService {
7777
ipv4Address: try CIDRv4(ip, prefix: status.ipv4Subnet.prefix),
7878
ipv4Gateway: status.ipv4Gateway,
7979
ipv6Address: ipv6Address,
80-
macAddress: macAddress
80+
macAddress: macAddress,
81+
variant: network.variant
8182
)
8283
log.info(
8384
"allocated attachment",
@@ -146,7 +147,8 @@ public actor DefaultNetworkService: NetworkService {
146147
ipv4Address: ipv4Address,
147148
ipv4Gateway: status.ipv4Gateway,
148149
ipv6Address: ipv6Address,
149-
macAddress: macAddress
150+
macAddress: macAddress,
151+
variant: network.variant
150152
)
151153
log.debug(
152154
"lookup attachment",

Sources/Services/Network/Server/Network.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ public protocol Network: Sendable {
2222
/// The network's identifier.
2323
var id: String { get }
2424

25+
/// An operational hint passed back to the runtime in the allocate response.
26+
/// Together with the plugin name, the runtime uses this to select the appropriate
27+
/// interface strategy for the sandbox. A `nil` value indicates that the plugin
28+
/// has only a single, default variant.
29+
nonisolated var variant: String? { get }
30+
2531
/// The network's runtime status. `nil` before ``start()`` completes.
2632
var status: NetworkStatus? { get async }
2733

Sources/Services/NetworkVmnet/Server/AllocationOnlyVmnetNetwork.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public actor AllocationOnlyVmnetNetwork: Network {
5050

5151
public nonisolated var id: String { configuration.id }
5252

53+
public nonisolated var variant: String? { "allocationOnly" }
54+
5355
public var status: NetworkStatus? { _status }
5456

5557
public nonisolated func withAdditionalData(_ handler: (XPCMessage?) throws -> Void) throws {

Sources/Services/NetworkVmnet/Server/ReservedVmnetNetwork.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ public final class ReservedVmnetNetwork: ContainerNetworkServer.Network {
6363

6464
public nonisolated var id: String { configuration.id }
6565

66+
public nonisolated var variant: String? { "reserved" }
67+
6668
public var status: NetworkStatus? {
6769
stateMutex.withLock { $0.status }
6870
}

Sources/Services/Runtime/RuntimeClient/NetworkBootstrapInfo.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,12 @@
1717
import ContainerResource
1818

1919
/// Plugin info passed from the API server in the sandbox bootstrap message so the
20-
/// runtime can connect to the correct network helper and configure the interface.
20+
/// runtime can connect to the correct network helper.
2121
public struct NetworkBootstrapInfo: Codable, Sendable {
2222
/// The network plugin name identifying which network helper to contact.
2323
public let plugin: String
2424

25-
/// Plugin-specific options, including `variant` which selects the interface strategy.
26-
public let options: [String: String]
27-
28-
public init(plugin: String, options: [String: String] = [:]) {
25+
public init(plugin: String) {
2926
self.plugin = plugin
30-
self.options = options
3127
}
3228
}

Sources/Services/RuntimeLinux/Server/RuntimeService.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,14 @@ public actor RuntimeService {
193193
ipv4Gateway: attachment.ipv4Gateway,
194194
ipv6Address: attachment.ipv6Address,
195195
macAddress: attachment.macAddress,
196-
mtu: mtu
196+
mtu: mtu,
197+
variant: attachment.variant
197198
)
198199
}
199-
guard let iStrategy = self.interfaceStrategies[NetworkInterfaceKey(plugin: info.plugin, variant: info.options["variant"])] else {
200+
guard let iStrategy = self.interfaceStrategies[NetworkInterfaceKey(plugin: info.plugin, variant: attachment.variant)] else {
200201
throw ContainerizationError(
201202
.internalError,
202-
message: "no available interface strategy for network \(attachment.network), plugin=\(info.plugin) variant=\(info.options["variant"] ?? "nil")")
203+
message: "no available interface strategy for network \(attachment.network), plugin=\(info.plugin) variant=\(attachment.variant ?? "nil")")
203204
}
204205
let interface = try iStrategy.toInterface(
205206
attachment: attachment,

0 commit comments

Comments
 (0)