mirror of
https://github.com/apple/container.git
synced 2026-09-23 16:15:37 +00:00
Use NetworkResource for network management in API server. (#1421)
- Part of #1404. - Evolves API to use NetworkResource that conforms to ManagedResource. - BREAKING CHANGE - compile time impact due to changes to Swift client API signatures. No change to persistent data or API server XPC protocols.
This commit is contained in:
@@ -272,7 +272,7 @@ extension Application {
|
||||
guard let defaultNetwork = try await networkClient.builtin else {
|
||||
throw ContainerizationError(.invalidState, message: "default network is not present")
|
||||
}
|
||||
guard case .running(_, _) = defaultNetwork else {
|
||||
guard defaultNetwork.status.phase == "running" else {
|
||||
throw ContainerizationError(.invalidState, message: "default network is not running")
|
||||
}
|
||||
config.networks = [
|
||||
|
||||
@@ -74,8 +74,8 @@ extension Application {
|
||||
pluginInfo: NetworkPluginInfo(plugin: self.plugin, variant: self.pluginVariant)
|
||||
)
|
||||
let networkClient = NetworkClient()
|
||||
let state = try await networkClient.create(configuration: config)
|
||||
print(state.id)
|
||||
let network = try await networkClient.create(configuration: config)
|
||||
print(network.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ extension Application {
|
||||
public mutating func run() async throws {
|
||||
let networkClient = NetworkClient()
|
||||
let uniqueNetworkNames = Set<String>(networkNames)
|
||||
let networks: [NetworkState]
|
||||
let networks: [NetworkResource]
|
||||
|
||||
if all {
|
||||
networks = try await networkClient.list()
|
||||
@@ -91,7 +91,7 @@ extension Application {
|
||||
|
||||
var failed = [String]()
|
||||
let _log = log
|
||||
try await withThrowingTaskGroup(of: NetworkState?.self) { group in
|
||||
try await withThrowingTaskGroup(of: NetworkResource?.self) { group in
|
||||
for network in networks {
|
||||
group.addTask {
|
||||
do {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import ArgumentParser
|
||||
import ContainerAPIClient
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
public struct NetworkInspect: AsyncLoggableCommand {
|
||||
@@ -35,11 +34,7 @@ extension Application {
|
||||
|
||||
public func run() async throws {
|
||||
let networkClient = NetworkClient()
|
||||
let items = try await networkClient.list().filter {
|
||||
networks.contains($0.id)
|
||||
}.map {
|
||||
PrintableNetwork($0)
|
||||
}
|
||||
let items = try await networkClient.list().filter { networks.contains($0.id) }
|
||||
try Output.emit(Output.renderJSON(items))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
import ArgumentParser
|
||||
import ContainerAPIClient
|
||||
import ContainerResource
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
public struct NetworkList: AsyncLoggableCommand {
|
||||
@@ -42,45 +39,7 @@ extension Application {
|
||||
public func run() async throws {
|
||||
let networkClient = NetworkClient()
|
||||
let networks = try await networkClient.list()
|
||||
let items = networks.map { PrintableNetwork($0) }
|
||||
try Output.render(json: items, display: items, format: format, quiet: quiet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PrintableNetwork: ListDisplayable {
|
||||
public static var tableHeader: [String] {
|
||||
["NETWORK", "STATE", "SUBNET"]
|
||||
}
|
||||
|
||||
public var tableRow: [String] {
|
||||
if let status {
|
||||
return [self.id, self.state, status.ipv4Subnet.description]
|
||||
}
|
||||
return [self.id, self.state, "none"]
|
||||
}
|
||||
|
||||
public var quietValue: String {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
public struct PrintableNetwork: Codable, Sendable {
|
||||
let id: String
|
||||
let state: String
|
||||
let config: NetworkConfiguration
|
||||
let status: NetworkStatus?
|
||||
|
||||
public init(_ network: NetworkState) {
|
||||
self.id = network.id
|
||||
self.state = network.state
|
||||
switch network {
|
||||
case .created(let config):
|
||||
self.config = config
|
||||
self.status = nil
|
||||
case .running(let config, let status):
|
||||
self.config = config
|
||||
self.status = status
|
||||
try Output.render(json: networks, display: networks, format: format, quiet: quiet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerResource
|
||||
|
||||
extension NetworkResource: ListDisplayable {
|
||||
public static var tableHeader: [String] {
|
||||
["NETWORK", "STATE", "SUBNET"]
|
||||
}
|
||||
|
||||
public var tableRow: [String] {
|
||||
[id, status.phase, status.ipv4Subnet?.description ?? "none"]
|
||||
}
|
||||
|
||||
public var quietValue: String {
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -119,16 +119,8 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
|
||||
}
|
||||
|
||||
private func validate() throws {
|
||||
guard id.isValidNetworkID() else {
|
||||
guard NetworkResource.nameValid(id) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid network ID: \(id)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
/// Ensure that the network ID has the correct syntax.
|
||||
fileprivate func isValidNetworkID() -> Bool {
|
||||
let pattern = #"^[a-z0-9](?:[a-z0-9._-]{0,61}[a-z0-9])?$"#
|
||||
return self.range(of: pattern, options: .regularExpression) != nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
|
||||
/// A network resource, representing a configured virtual network and its runtime status.
|
||||
///
|
||||
/// `NetworkResource` conforms to `ManagedResource` and separates the network's
|
||||
/// intrinsic configuration from its ephemeral runtime status — following the same
|
||||
/// config/status split used by Kubernetes and Docker. `config` is persisted;
|
||||
/// `status` reflects what the network plugin reports at runtime.
|
||||
///
|
||||
/// The JSON encoding uses a single `status` object containing a `phase` field
|
||||
/// alongside any runtime-allocated address properties, replacing the prior flat
|
||||
/// `state`/`status` pair in the CLI output.
|
||||
public struct NetworkResource: ManagedResource {
|
||||
/// The network's configuration — its persistent, intrinsic properties.
|
||||
public let config: NetworkConfiguration
|
||||
|
||||
/// The network's current status, including lifecycle phase and any
|
||||
/// runtime-allocated address properties.
|
||||
public let status: NetworkStatus
|
||||
|
||||
// MARK: ManagedResource
|
||||
|
||||
/// The unique identifier for this network. Identical to ``config/id``.
|
||||
public var id: String { config.id }
|
||||
|
||||
/// The user-assigned name for this network. For networks, name and ID are the same.
|
||||
public var name: String { config.id }
|
||||
|
||||
/// The time at which this network was created.
|
||||
public var creationDate: Date { config.creationDate }
|
||||
|
||||
/// Key-value labels for this network.
|
||||
public var labels: ResourceLabels { config.labels }
|
||||
|
||||
/// Returns `true` for a system-managed network that cannot be deleted by the user.
|
||||
public var isBuiltin: Bool { labels.isBuiltin }
|
||||
|
||||
/// Returns `true` if `name` is a syntactically valid network identifier.
|
||||
///
|
||||
/// Valid network names are lowercase alphanumeric strings of up to 63
|
||||
/// characters, allowing dots, hyphens, and underscores in interior positions.
|
||||
public static func nameValid(_ name: String) -> Bool {
|
||||
let pattern = #"^[a-z0-9](?:[a-z0-9._-]{0,61}[a-z0-9])?$"#
|
||||
return name.range(of: pattern, options: .regularExpression) != nil
|
||||
}
|
||||
|
||||
// MARK: Initialization
|
||||
|
||||
/// Creates a network resource.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - config: The network's intrinsic configuration.
|
||||
/// - networkStatus: The plugin-reported runtime status, or `nil` if the
|
||||
/// network is not yet running.
|
||||
public init(config: NetworkConfiguration, networkStatus: NetworkPluginStatus? = nil) {
|
||||
self.config = config
|
||||
self.status = networkStatus.map { NetworkStatus(running: $0) } ?? .created
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Conversion from NetworkState
|
||||
|
||||
extension NetworkResource {
|
||||
/// Creates a network resource from a ``NetworkState``.
|
||||
///
|
||||
/// Used when translating from the internal plugin-protocol type to the
|
||||
/// public API surface type.
|
||||
public init(_ networkState: NetworkState) {
|
||||
switch networkState {
|
||||
case .created(let config):
|
||||
self.init(config: config)
|
||||
case .running(let config, let status):
|
||||
self.init(config: config, networkStatus: status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Codable
|
||||
|
||||
extension NetworkResource {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case config
|
||||
case status
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(config, forKey: .config)
|
||||
try container.encode(status, forKey: .status)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.config = try container.decode(NetworkConfiguration.self, forKey: .config)
|
||||
self.status = try container.decode(NetworkStatus.self, forKey: .status)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
|
||||
public struct NetworkStatus: Codable, Sendable {
|
||||
public struct NetworkPluginStatus: Codable, Sendable {
|
||||
/// The address allocated for the network if no subnet was specified at
|
||||
/// creation time; otherwise, the subnet from the configuration.
|
||||
public let ipv4Subnet: CIDRv4
|
||||
@@ -83,7 +83,7 @@ public enum NetworkState: Codable, Sendable {
|
||||
// The network has been configured.
|
||||
case created(NetworkConfiguration)
|
||||
// The network is running.
|
||||
case running(NetworkConfiguration, NetworkStatus)
|
||||
case running(NetworkConfiguration, NetworkPluginStatus)
|
||||
|
||||
public var state: String {
|
||||
switch self {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
|
||||
/// The runtime status of a network resource.
|
||||
///
|
||||
/// `phase` names the current lifecycle stage; the address fields are present
|
||||
/// only when `phase` is `"running"` and are `nil` otherwise. Clients should
|
||||
/// treat unrecognised `phase` values as unknown forward-compatible stages rather
|
||||
/// than treating them as errors.
|
||||
public struct NetworkStatus: Codable, Sendable {
|
||||
/// The current lifecycle phase of the network.
|
||||
///
|
||||
/// Defined values: `"created"` (configured, plugin not yet active) and
|
||||
/// `"running"` (plugin active, subnet and gateway assigned).
|
||||
public let phase: String
|
||||
|
||||
/// The allocated IPv4 subnet. Present only when `phase` is `"running"`.
|
||||
public let ipv4Subnet: CIDRv4?
|
||||
|
||||
/// The IPv4 gateway address. Present only when `phase` is `"running"`.
|
||||
public let ipv4Gateway: IPv4Address?
|
||||
|
||||
/// The allocated IPv6 subnet. Present only when `phase` is `"running"` and
|
||||
/// the network has IPv6 enabled.
|
||||
public let ipv6Subnet: CIDRv6?
|
||||
|
||||
public init(
|
||||
phase: String,
|
||||
ipv4Subnet: CIDRv4? = nil,
|
||||
ipv4Gateway: IPv4Address? = nil,
|
||||
ipv6Subnet: CIDRv6? = nil
|
||||
) {
|
||||
self.phase = phase
|
||||
self.ipv4Subnet = ipv4Subnet
|
||||
self.ipv4Gateway = ipv4Gateway
|
||||
self.ipv6Subnet = ipv6Subnet
|
||||
}
|
||||
}
|
||||
|
||||
extension NetworkStatus {
|
||||
/// The status value for a network that is configured but not yet running.
|
||||
public static let created = NetworkStatus(phase: "created")
|
||||
|
||||
/// Creates a running-phase status from a ``NetworkPluginStatus``.
|
||||
init(running networkStatus: NetworkPluginStatus) {
|
||||
self.init(
|
||||
phase: "running",
|
||||
ipv4Subnet: networkStatus.ipv4Subnet,
|
||||
ipv4Gateway: networkStatus.ipv4Gateway,
|
||||
ipv6Subnet: networkStatus.ipv6Subnet
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@ import Foundation
|
||||
///
|
||||
/// ```swift
|
||||
/// let client = NetworkClient()
|
||||
/// let state = try await client.create(configuration: config)
|
||||
/// let network = try await client.create(configuration: config)
|
||||
/// let networks = try await client.list()
|
||||
/// try await client.delete(id: state.id)
|
||||
/// try await client.delete(id: network.id)
|
||||
/// ```
|
||||
public struct NetworkClient: Sendable {
|
||||
/// The Mach service name used to locate the container API server.
|
||||
@@ -67,13 +67,13 @@ public struct NetworkClient: Sendable {
|
||||
/// Creates a new network with the given configuration.
|
||||
///
|
||||
/// The API server launches a network plugin instance for the new network and
|
||||
/// returns a ``NetworkState`` reflecting the network once it is running.
|
||||
/// returns a ``NetworkResource`` reflecting the network once it is running.
|
||||
///
|
||||
/// - Parameter configuration: The configuration describing the network to create.
|
||||
/// - Returns: The running state of the newly created network.
|
||||
/// - Throws: ``ContainerizationError`` if the server does not return a valid
|
||||
/// network state, or if the underlying XPC call fails.
|
||||
public func create(configuration: NetworkConfiguration) async throws -> NetworkState {
|
||||
/// network resource, or if the underlying XPC call fails.
|
||||
public func create(configuration: NetworkConfiguration) async throws -> NetworkResource {
|
||||
let request = XPCMessage(route: .networkCreate)
|
||||
request.set(key: .networkId, value: configuration.id)
|
||||
|
||||
@@ -81,38 +81,51 @@ public struct NetworkClient: Sendable {
|
||||
request.set(key: .networkConfig, value: data)
|
||||
|
||||
let response = try await xpcSend(message: request)
|
||||
let responseData = response.dataNoCopy(key: .networkState)
|
||||
guard let responseData else {
|
||||
throw ContainerizationError(.invalidArgument, message: "network configuration not received")
|
||||
|
||||
// Prefer current encoding (≥ 0.12.0 server).
|
||||
if let resourceData = response.dataNoCopy(key: .networkResource) {
|
||||
return try JSONDecoder().decode(NetworkResource.self, from: resourceData)
|
||||
}
|
||||
let state = try JSONDecoder().decode(NetworkState.self, from: responseData)
|
||||
return state
|
||||
|
||||
// Fall back to pre-0.12.0 server: decode NetworkState and convert.
|
||||
if let stateData = response.dataNoCopy(key: .networkState) {
|
||||
let state = try JSONDecoder().decode(NetworkState.self, from: stateData)
|
||||
return NetworkResource(state)
|
||||
}
|
||||
|
||||
throw ContainerizationError(.invalidArgument, message: "network configuration not received")
|
||||
}
|
||||
|
||||
/// Returns the current state of all networks known to the API server.
|
||||
///
|
||||
/// - Returns: An array of ``NetworkState`` values, or an empty array if no
|
||||
/// - Returns: An array of ``NetworkResource`` values, or an empty array if no
|
||||
/// networks exist or the server returns no data.
|
||||
/// - Throws: ``ContainerizationError`` if the underlying XPC call fails.
|
||||
public func list() async throws -> [NetworkState] {
|
||||
public func list() async throws -> [NetworkResource] {
|
||||
let request = XPCMessage(route: .networkList)
|
||||
|
||||
let response = try await xpcSend(message: request, timeout: .seconds(1))
|
||||
let responseData = response.dataNoCopy(key: .networkStates)
|
||||
guard let responseData else {
|
||||
return []
|
||||
|
||||
// Prefer current encoding (≥ 0.12.0 server).
|
||||
if let resourceData = response.dataNoCopy(key: .networkResources) {
|
||||
return try JSONDecoder().decode([NetworkResource].self, from: resourceData)
|
||||
}
|
||||
let states = try JSONDecoder().decode([NetworkState].self, from: responseData)
|
||||
return states
|
||||
|
||||
// Fall back to pre-0.12.0 server: decode NetworkState and convert.
|
||||
if let stateData = response.dataNoCopy(key: .networkStates) {
|
||||
return try JSONDecoder().decode([NetworkState].self, from: stateData).map(NetworkResource.init)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
/// Returns the network with the given identifier.
|
||||
///
|
||||
/// - Parameter id: The identifier of the network to look up.
|
||||
/// - Returns: The ``NetworkState`` for the matching network.
|
||||
/// - Returns: The ``NetworkResource`` for the matching network.
|
||||
/// - Throws: ``ContainerizationError/notFound`` if no network with the given
|
||||
/// identifier exists, or a communication error if the XPC call fails.
|
||||
public func get(id: String) async throws -> NetworkState {
|
||||
public func get(id: String) async throws -> NetworkResource {
|
||||
let networks = try await list()
|
||||
guard let network = networks.first(where: { $0.id == id }) else {
|
||||
throw ContainerizationError(.notFound, message: "network \(id) not found")
|
||||
@@ -141,7 +154,7 @@ public struct NetworkClient: Sendable {
|
||||
/// built-in resource labels.
|
||||
///
|
||||
/// - Throws: ``ContainerizationError`` if the underlying XPC call fails.
|
||||
public var builtin: NetworkState? {
|
||||
public var builtin: NetworkResource? {
|
||||
get async throws {
|
||||
try await list().first { $0.isBuiltin }
|
||||
}
|
||||
|
||||
@@ -209,8 +209,8 @@ public struct Utility {
|
||||
networks: parsedNetworks
|
||||
)
|
||||
for attachmentConfiguration in config.networks {
|
||||
let network: NetworkState = try await networkClient.get(id: attachmentConfiguration.network)
|
||||
guard case .running(_, _) = network else {
|
||||
let network = try await networkClient.get(id: attachmentConfiguration.network)
|
||||
guard network.status.phase == "running" else {
|
||||
throw ContainerizationError(.invalidState, message: "network \(attachmentConfiguration.network) is not running")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,11 @@ public enum XPCKeys: String {
|
||||
case networkConfig
|
||||
case networkState
|
||||
case networkStates
|
||||
// Added in 0.12.0: NetworkResource encoding (status.phase shape).
|
||||
// DEPRECATED 0.12.0: networkState/networkStates retained for down-revision
|
||||
// client compatibility; remove at next major version boundary.
|
||||
case networkResource
|
||||
case networkResources
|
||||
|
||||
/// Kernel
|
||||
case kernel
|
||||
|
||||
@@ -32,11 +32,18 @@ public struct NetworksHarness: Sendable {
|
||||
|
||||
@Sendable
|
||||
public func list(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
let containers = try await service.list()
|
||||
let data = try JSONEncoder().encode(containers)
|
||||
let states = try await service.list()
|
||||
|
||||
let reply = message.reply()
|
||||
reply.set(key: .networkStates, value: data)
|
||||
|
||||
// Current encoding: NetworkResource with status.phase shape (≥ 0.12.0).
|
||||
let resources = states.map(NetworkResource.init)
|
||||
reply.set(key: .networkResources, value: try JSONEncoder().encode(resources))
|
||||
|
||||
// DEPRECATED 0.12.0 — retained for down-revision client compatibility.
|
||||
// Remove at next major version boundary.
|
||||
reply.set(key: .networkStates, value: try JSONEncoder().encode(states))
|
||||
|
||||
return reply
|
||||
}
|
||||
|
||||
@@ -50,10 +57,15 @@ public struct NetworksHarness: Sendable {
|
||||
let config = try JSONDecoder().decode(NetworkConfiguration.self, from: data)
|
||||
let networkState = try await service.create(configuration: config)
|
||||
|
||||
let networkData = try JSONEncoder().encode(networkState)
|
||||
|
||||
let reply = message.reply()
|
||||
reply.set(key: .networkState, value: networkData)
|
||||
|
||||
// Current encoding: NetworkResource with status.phase shape (≥ 0.12.0).
|
||||
reply.set(key: .networkResource, value: try JSONEncoder().encode(NetworkResource(networkState)))
|
||||
|
||||
// DEPRECATED 0.12.0 — retained for down-revision client compatibility.
|
||||
// Remove at next major version boundary.
|
||||
reply.set(key: .networkState, value: try JSONEncoder().encode(networkState))
|
||||
|
||||
return reply
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public actor AllocationOnlyVmnetNetwork: Network {
|
||||
let ipv4Subnet = configuration.ipv4Subnet ?? Self.defaultIPv4Subnet
|
||||
|
||||
let gateway = IPv4Address(ipv4Subnet.lower.value + 1)
|
||||
let status = NetworkStatus(
|
||||
let status = NetworkPluginStatus(
|
||||
ipv4Subnet: ipv4Subnet,
|
||||
ipv4Gateway: gateway,
|
||||
ipv6Subnet: nil,
|
||||
|
||||
@@ -80,7 +80,7 @@ public final class ReservedVmnetNetwork: Network {
|
||||
|
||||
let networkInfo = try startNetwork(configuration: configuration, log: log)
|
||||
|
||||
let networkStatus = NetworkStatus(
|
||||
let networkStatus = NetworkPluginStatus(
|
||||
ipv4Subnet: networkInfo.ipv4Subnet,
|
||||
ipv4Gateway: networkInfo.ipv4Gateway,
|
||||
ipv6Subnet: networkInfo.ipv6Subnet,
|
||||
|
||||
@@ -917,10 +917,10 @@ public actor SandboxService {
|
||||
let networkClient = NetworkClient()
|
||||
for allocatedAttach in allocatedAttachments {
|
||||
let state = try await networkClient.get(id: allocatedAttach.attachment.network)
|
||||
guard case .running(_, let status) = state else {
|
||||
guard state.status.phase == "running", let gateway = state.status.ipv4Gateway else {
|
||||
continue
|
||||
}
|
||||
return [status.ipv4Gateway.description]
|
||||
return [gateway.description]
|
||||
}
|
||||
|
||||
return []
|
||||
|
||||
@@ -46,9 +46,8 @@ class CLITest {
|
||||
|
||||
struct NetworkInspectOutput: Codable {
|
||||
let id: String
|
||||
let state: String
|
||||
let config: NetworkConfiguration
|
||||
let status: NetworkStatus?
|
||||
let status: NetworkStatus
|
||||
}
|
||||
|
||||
let testName: String
|
||||
|
||||
@@ -236,13 +236,13 @@ struct PrintableContainerDisplayTests {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - PrintableNetwork conformance tests
|
||||
// MARK: - NetworkResource ListDisplayable conformance tests
|
||||
|
||||
struct PrintableNetworkDisplayTests {
|
||||
struct NetworkResourceDisplayTests {
|
||||
@Test
|
||||
func tableHeaderHasThreeColumns() {
|
||||
#expect(PrintableNetwork.tableHeader.count == 3)
|
||||
#expect(PrintableNetwork.tableHeader == ["NETWORK", "STATE", "SUBNET"])
|
||||
#expect(NetworkResource.tableHeader.count == 3)
|
||||
#expect(NetworkResource.tableHeader == ["NETWORK", "STATE", "SUBNET"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user