mirror of
https://github.com/apple/container.git
synced 2026-08-24 02:24:19 -05:00
Reorganize Swift package targets for network plugin. (#1615)
- Part of #1404. - Updates containerization to 0.33.2. - Reorganizes network plugin targets into: - `ContainerNetworkClient` - network plugin client and default types - `ContainerNetworkServer` - separate protocols for `Network` which manages the underlying virtual network, `NetworkService`, which takes a network and implements the API, and an actor `NetworkHarness` that marshals between the API and the XPC protocol. The service-harness separation will help us ensure XPC protocol compatibility in both directions as we evolve the plugin APIs. - Removes `disableAllocator()` which is no longer used since #1545 switched over to using XPC connections between runtime and network plugin instances to track whether a network has attached containers.
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "d9d032f8b1ad94cf9006bd4b441ce1dad52802b04b1d7f7b30baedd32cee0e8b",
|
||||
"originHash" : "f3e6e0a1ee627e1f396b1565e72a50b179394e7011667ec4569dc53455ee06ed",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
@@ -15,8 +15,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/containerization.git",
|
||||
"state" : {
|
||||
"revision" : "9205a766302a18e06a0ce43f8b7e483625e3e50f",
|
||||
"version" : "0.33.1"
|
||||
"revision" : "2550dd49f1890702f6fe0171212050bbce9d3825",
|
||||
"version" : "0.33.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+30
-24
@@ -23,7 +23,7 @@ import PackageDescription
|
||||
let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0"
|
||||
let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified"
|
||||
let builderShimVersion = "0.12.0"
|
||||
let scVersion = "0.33.1"
|
||||
let scVersion = "0.33.2"
|
||||
|
||||
let package = Package(
|
||||
name: "container",
|
||||
@@ -34,7 +34,9 @@ let package = Package(
|
||||
.library(name: "ContainerAPIService", targets: ["ContainerAPIService"]),
|
||||
.library(name: "ContainerAPIClient", targets: ["ContainerAPIClient"]),
|
||||
.library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]),
|
||||
.library(name: "ContainerNetworkService", targets: ["ContainerNetworkService", "ContainerNetworkServiceClient"]),
|
||||
.library(name: "ContainerNetworkClient", targets: ["ContainerNetworkClient"]),
|
||||
.library(name: "ContainerNetworkServer", targets: ["ContainerNetworkServer"]),
|
||||
.library(name: "ContainerNetworkVmnetServer", targets: ["ContainerNetworkVmnetServer"]),
|
||||
.library(name: "ContainerResource", targets: ["ContainerResource"]),
|
||||
.library(name: "ContainerLog", targets: ["ContainerLog"]),
|
||||
.library(name: "ContainerPersistence", targets: ["ContainerPersistence"]),
|
||||
@@ -106,7 +108,6 @@ let package = Package(
|
||||
"ContainerBuild",
|
||||
"ContainerAPIClient",
|
||||
"ContainerLog",
|
||||
"ContainerNetworkService",
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
@@ -166,7 +167,7 @@ let package = Package(
|
||||
"ContainerAPIService",
|
||||
"ContainerAPIClient",
|
||||
"ContainerLog",
|
||||
"ContainerNetworkService",
|
||||
"ContainerNetworkClient",
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
@@ -188,7 +189,7 @@ let package = Package(
|
||||
.product(name: "SystemPackage", package: "swift-system"),
|
||||
"CVersion",
|
||||
"ContainerAPIClient",
|
||||
"ContainerNetworkServiceClient",
|
||||
"ContainerNetworkClient",
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
@@ -291,13 +292,12 @@ let package = Package(
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
.product(name: "ContainerizationIO", package: "containerization"),
|
||||
.product(name: "ContainerizationOS", package: "containerization"),
|
||||
"ContainerLog",
|
||||
"ContainerNetworkService",
|
||||
"ContainerNetworkServiceClient",
|
||||
"ContainerNetworkClient",
|
||||
"ContainerNetworkServer",
|
||||
"ContainerNetworkVmnetServer",
|
||||
"ContainerPersistence",
|
||||
"ContainerPlugin",
|
||||
"ContainerResource",
|
||||
@@ -308,36 +308,42 @@ let package = Package(
|
||||
exclude: ["config.toml"]
|
||||
),
|
||||
.target(
|
||||
name: "ContainerNetworkService",
|
||||
name: "ContainerNetworkClient",
|
||||
dependencies: [
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
.product(name: "ContainerizationOS", package: "containerization"),
|
||||
"ContainerNetworkServiceClient",
|
||||
"ContainerPersistence",
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
"ContainerResource",
|
||||
"ContainerXPC",
|
||||
],
|
||||
path: "Sources/Services/ContainerNetworkService/Server"
|
||||
path: "Sources/Services/Network/Client"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerNetworkServer",
|
||||
dependencies: [
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
"ContainerNetworkClient",
|
||||
"ContainerResource",
|
||||
"ContainerXPC",
|
||||
],
|
||||
path: "Sources/Services/Network/Server"
|
||||
),
|
||||
.testTarget(
|
||||
name: "ContainerNetworkServiceTests",
|
||||
name: "ContainerNetworkServerTests",
|
||||
dependencies: [
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
"ContainerNetworkService",
|
||||
"ContainerNetworkServer",
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "ContainerNetworkServiceClient",
|
||||
name: "ContainerNetworkVmnetServer",
|
||||
dependencies: [
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
"ContainerLog",
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
"ContainerNetworkServer",
|
||||
"ContainerResource",
|
||||
"ContainerXPC",
|
||||
],
|
||||
path: "Sources/Services/ContainerNetworkService/Client"
|
||||
path: "Sources/Services/NetworkVmnet/Server"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerRuntimeLinuxClient",
|
||||
@@ -371,7 +377,7 @@ let package = Package(
|
||||
.product(name: "ContainerizationOS", package: "containerization"),
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
"ContainerAPIClient",
|
||||
"ContainerNetworkServiceClient",
|
||||
"ContainerNetworkClient",
|
||||
"ContainerOS",
|
||||
"ContainerPersistence",
|
||||
"ContainerResource",
|
||||
|
||||
@@ -18,7 +18,6 @@ import ArgumentParser
|
||||
import ContainerAPIClient
|
||||
import ContainerAPIService
|
||||
import ContainerLog
|
||||
import ContainerNetworkService
|
||||
import ContainerPersistence
|
||||
import ContainerPlugin
|
||||
import ContainerResource
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
import ArgumentParser
|
||||
import ContainerLog
|
||||
import ContainerNetworkService
|
||||
import ContainerNetworkServiceClient
|
||||
import ContainerNetworkClient
|
||||
import ContainerNetworkServer
|
||||
import ContainerNetworkVmnetServer
|
||||
import ContainerPlugin
|
||||
import ContainerResource
|
||||
import ContainerXPC
|
||||
@@ -99,14 +100,14 @@ extension NetworkVmnetHelper {
|
||||
log: log
|
||||
)
|
||||
try await network.start()
|
||||
let server = try await NetworkService(network: network, log: log)
|
||||
let service = try await DefaultNetworkService(network: network, log: log)
|
||||
let harness = NetworkHarness(service: service)
|
||||
let xpc = XPCServer(
|
||||
identifier: serviceIdentifier,
|
||||
routes: [
|
||||
NetworkRoutes.state.rawValue: XPCServer.route(server.state),
|
||||
NetworkRoutes.allocate.rawValue: server.allocate,
|
||||
NetworkRoutes.lookup.rawValue: XPCServer.route(server.lookup),
|
||||
NetworkRoutes.disableAllocator.rawValue: XPCServer.route(server.disableAllocator),
|
||||
NetworkRoutes.state.rawValue: XPCServer.route(harness.state),
|
||||
NetworkRoutes.allocate.rawValue: harness.allocate,
|
||||
NetworkRoutes.lookup.rawValue: XPCServer.route(harness.lookup),
|
||||
],
|
||||
log: log
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerAPIClient
|
||||
import ContainerNetworkServiceClient
|
||||
import ContainerNetworkClient
|
||||
import ContainerPersistence
|
||||
import ContainerPlugin
|
||||
import ContainerResource
|
||||
@@ -30,7 +30,7 @@ import SystemPackage
|
||||
public actor NetworksService {
|
||||
struct NetworkServiceState {
|
||||
var networkState: NetworkState
|
||||
var client: ContainerNetworkServiceClient.NetworkClient
|
||||
var client: ContainerNetworkClient.NetworkClient
|
||||
}
|
||||
|
||||
private let pluginLoader: PluginLoader
|
||||
@@ -389,7 +389,7 @@ public actor NetworksService {
|
||||
return pluginInfo
|
||||
}
|
||||
|
||||
private static func getClient(configuration: NetworkConfiguration) throws -> ContainerNetworkServiceClient.NetworkClient {
|
||||
private static func getClient(configuration: NetworkConfiguration) throws -> ContainerNetworkClient.NetworkClient {
|
||||
guard let pluginInfo = configuration.pluginInfo else {
|
||||
throw ContainerizationError(.internalError, message: "network \(configuration.id) missing plugin information")
|
||||
}
|
||||
|
||||
-13
@@ -113,15 +113,6 @@ extension NetworkClient {
|
||||
}
|
||||
}
|
||||
|
||||
public func disableAllocator() async throws -> Bool {
|
||||
let request = XPCMessage(route: NetworkRoutes.disableAllocator.rawValue)
|
||||
|
||||
let client = createClient()
|
||||
|
||||
let response = try await client.send(request)
|
||||
return try response.allocatorDisabled()
|
||||
}
|
||||
|
||||
private func createClient() -> XPCClient {
|
||||
XPCClient(service: machServiceLabel)
|
||||
}
|
||||
@@ -135,10 +126,6 @@ extension XPCMessage {
|
||||
return XPCMessage(object: additionalData)
|
||||
}
|
||||
|
||||
public func allocatorDisabled() throws -> Bool {
|
||||
self.bool(key: NetworkKeys.allocatorDisabled.rawValue)
|
||||
}
|
||||
|
||||
public func attachment() throws -> Attachment {
|
||||
let data = self.dataNoCopy(key: NetworkKeys.attachment.rawValue)
|
||||
guard let data else {
|
||||
-1
@@ -16,7 +16,6 @@
|
||||
|
||||
public enum NetworkKeys: String {
|
||||
case additionalData
|
||||
case allocatorDisabled
|
||||
case attachment
|
||||
case hostname
|
||||
case macAddress
|
||||
-2
@@ -19,8 +19,6 @@ public enum NetworkRoutes: String {
|
||||
case state = "com.apple.container.network/state"
|
||||
/// Allocates parameters for attaching a sandbox to the network.
|
||||
case allocate = "com.apple.container.network/allocate"
|
||||
/// Disables the allocator if no sandboxes are attached.
|
||||
case disableAllocator = "com.apple.container.network/disableAllocator"
|
||||
/// Retrieves the allocation for a hostname.
|
||||
case lookup = "com.apple.container.network/lookup"
|
||||
}
|
||||
-5
@@ -52,11 +52,6 @@ actor AttachmentAllocator {
|
||||
return index
|
||||
}
|
||||
|
||||
/// If no addresses are allocated, prevent future allocations and return true.
|
||||
func disableAllocator() async -> Bool {
|
||||
allocator.disableAllocator()
|
||||
}
|
||||
|
||||
/// Retrieve the allocator index for a hostname.
|
||||
func lookup(hostname: String) async throws -> UInt32? {
|
||||
hostnames[hostname]
|
||||
+20
-58
@@ -14,15 +14,13 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerNetworkServiceClient
|
||||
import ContainerResource
|
||||
import ContainerXPC
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
import Logging
|
||||
|
||||
public actor NetworkService: Sendable {
|
||||
public actor DefaultNetworkService: NetworkService {
|
||||
private let network: any Network
|
||||
private let log: Logger
|
||||
private var allocator: AttachmentAllocator
|
||||
@@ -50,15 +48,16 @@ public actor NetworkService: Sendable {
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func state(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
let reply = message.reply()
|
||||
let state = await network.state
|
||||
try reply.setState(state)
|
||||
return reply
|
||||
public func state() async throws -> NetworkState {
|
||||
await network.state
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func allocate(_ message: XPCMessage, _ session: XPCServerSession) async throws -> XPCMessage {
|
||||
public func allocate(
|
||||
hostname: String,
|
||||
macAddress: MACAddress?,
|
||||
session: XPCServerSession
|
||||
) async throws -> (attachment: Attachment, additionalData: XPCMessage?) {
|
||||
log.debug("enter", metadata: ["func": "\(#function)"])
|
||||
defer { log.debug("exit", metadata: ["func": "\(#function)"]) }
|
||||
|
||||
@@ -67,11 +66,7 @@ public actor NetworkService: Sendable {
|
||||
throw ContainerizationError(.invalidState, message: "invalid network state - network \(state.id) must be running")
|
||||
}
|
||||
|
||||
let hostname = try message.hostname()
|
||||
let macAddress =
|
||||
try message.string(key: NetworkKeys.macAddress.rawValue)
|
||||
.map { try MACAddress($0) }
|
||||
?? MACAddress((UInt64.random(in: 0...UInt64.max) & 0x0cff_ffff_ffff) | 0xf200_0000_0000)
|
||||
let macAddress = macAddress ?? MACAddress((UInt64.random(in: 0...UInt64.max) & 0x0cff_ffff_ffff) | 0xf200_0000_0000)
|
||||
let index = try await allocator.allocate(hostname: hostname)
|
||||
let ipv6Address = try status.ipv6Subnet
|
||||
.map { try CIDRv6(macAddress.ipv6Address(network: $0.lower), prefix: $0.prefix) }
|
||||
@@ -93,12 +88,10 @@ public actor NetworkService: Sendable {
|
||||
"ipv6Address": "\(attachment.ipv6Address?.description ?? "unavailable")",
|
||||
"macAddress": "\(attachment.macAddress?.description ?? "unspecified")",
|
||||
])
|
||||
let reply = message.reply()
|
||||
try reply.setAttachment(attachment)
|
||||
|
||||
var additionalData: XPCMessage?
|
||||
try network.withAdditionalData {
|
||||
if let additionalData = $0 {
|
||||
try reply.setAdditionalData(additionalData.underlying)
|
||||
}
|
||||
additionalData = $0
|
||||
}
|
||||
macAddresses[index] = macAddress
|
||||
|
||||
@@ -110,7 +103,7 @@ public actor NetworkService: Sendable {
|
||||
}
|
||||
allocationsBySession[session]!.append((hostname: hostname, index: index))
|
||||
|
||||
return reply
|
||||
return (attachment: attachment, additionalData: additionalData)
|
||||
}
|
||||
|
||||
private func releaseSession(_ session: XPCServerSession) async {
|
||||
@@ -125,7 +118,7 @@ public actor NetworkService: Sendable {
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func lookup(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
public func lookup(hostname: String) async throws -> Attachment? {
|
||||
log.debug("enter", metadata: ["func": "\(#function)"])
|
||||
defer { log.debug("exit", metadata: ["func": "\(#function)"]) }
|
||||
|
||||
@@ -134,15 +127,16 @@ public actor NetworkService: Sendable {
|
||||
throw ContainerizationError(.invalidState, message: "invalid network state - network \(state.id) must be running")
|
||||
}
|
||||
|
||||
let hostname = try message.hostname()
|
||||
// Invariant: hostname -> index if and only if index -> MAC address
|
||||
let index = try await allocator.lookup(hostname: hostname)
|
||||
let reply = message.reply()
|
||||
guard let index else {
|
||||
return reply
|
||||
return nil
|
||||
}
|
||||
guard let macAddress = macAddresses[index] else {
|
||||
return reply
|
||||
return nil
|
||||
}
|
||||
|
||||
// populate attachment
|
||||
let address = IPv4Address(index)
|
||||
let subnet = status.ipv4Subnet
|
||||
let ipv4Address = try CIDRv4(address, prefix: subnet.prefix)
|
||||
@@ -162,39 +156,7 @@ public actor NetworkService: Sendable {
|
||||
"hostname": "\(hostname)",
|
||||
"address": "\(address)",
|
||||
])
|
||||
try reply.setAttachment(attachment)
|
||||
return reply
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func disableAllocator(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
log.debug("enter", metadata: ["func": "\(#function)"])
|
||||
defer { log.debug("exit", metadata: ["func": "\(#function)"]) }
|
||||
|
||||
let success = await allocator.disableAllocator()
|
||||
log.info("attempted allocator disable", metadata: ["success": "\(success)"])
|
||||
let reply = message.reply()
|
||||
reply.setAllocatorDisabled(success)
|
||||
return reply
|
||||
}
|
||||
}
|
||||
|
||||
extension XPCMessage {
|
||||
fileprivate func setAdditionalData(_ additionalData: xpc_object_t) throws {
|
||||
xpc_dictionary_set_value(self.underlying, NetworkKeys.additionalData.rawValue, additionalData)
|
||||
}
|
||||
|
||||
fileprivate func setAllocatorDisabled(_ allocatorDisabled: Bool) {
|
||||
self.set(key: NetworkKeys.allocatorDisabled.rawValue, value: allocatorDisabled)
|
||||
}
|
||||
|
||||
fileprivate func setAttachment(_ attachment: Attachment) throws {
|
||||
let data = try JSONEncoder().encode(attachment)
|
||||
self.set(key: NetworkKeys.attachment.rawValue, value: data)
|
||||
}
|
||||
|
||||
fileprivate func setState(_ state: NetworkState) throws {
|
||||
let data = try JSONEncoder().encode(state)
|
||||
self.set(key: NetworkKeys.state.rawValue, value: data)
|
||||
return attachment
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerNetworkClient
|
||||
import ContainerResource
|
||||
import ContainerXPC
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
|
||||
public actor NetworkHarness: Sendable {
|
||||
private let service: NetworkService
|
||||
|
||||
public init(service: NetworkService) {
|
||||
self.service = service
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func state(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
let reply = message.reply()
|
||||
let state = try await service.state()
|
||||
try reply.setState(state)
|
||||
return reply
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func allocate(_ message: XPCMessage, _ session: XPCServerSession) async throws -> XPCMessage {
|
||||
let hostname = try message.hostname()
|
||||
let macAddress =
|
||||
try message.string(key: NetworkKeys.macAddress.rawValue)
|
||||
.map { try MACAddress($0) }
|
||||
|
||||
let (attachment:attachment, additionalData:additionalData) = try await service.allocate(
|
||||
hostname: hostname,
|
||||
macAddress: macAddress,
|
||||
session: session
|
||||
)
|
||||
|
||||
let reply = message.reply()
|
||||
try reply.setAttachment(attachment)
|
||||
if let additionalData {
|
||||
try reply.setAdditionalData(additionalData.underlying)
|
||||
}
|
||||
|
||||
return reply
|
||||
}
|
||||
|
||||
@Sendable
|
||||
public func lookup(_ message: XPCMessage) async throws -> XPCMessage {
|
||||
let hostname = try message.hostname()
|
||||
let reply = message.reply()
|
||||
guard let attachment = try await service.lookup(hostname: hostname) else {
|
||||
return reply
|
||||
}
|
||||
|
||||
try reply.setAttachment(attachment)
|
||||
return reply
|
||||
}
|
||||
}
|
||||
|
||||
extension XPCMessage {
|
||||
fileprivate func setAdditionalData(_ additionalData: xpc_object_t) throws {
|
||||
xpc_dictionary_set_value(self.underlying, NetworkKeys.additionalData.rawValue, additionalData)
|
||||
}
|
||||
|
||||
fileprivate func setAttachment(_ attachment: Attachment) throws {
|
||||
let data = try JSONEncoder().encode(attachment)
|
||||
self.set(key: NetworkKeys.attachment.rawValue, value: data)
|
||||
}
|
||||
|
||||
fileprivate func setState(_ state: NetworkState) throws {
|
||||
let data = try JSONEncoder().encode(state)
|
||||
self.set(key: NetworkKeys.state.rawValue, value: data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
import ContainerXPC
|
||||
import ContainerizationExtras
|
||||
|
||||
/// A network service
|
||||
public protocol NetworkService: Sendable {
|
||||
/// Gets the properties of the realized network.
|
||||
func state() async throws -> NetworkState
|
||||
|
||||
/// Register a hostname and allocate associated addresses.
|
||||
func allocate(
|
||||
hostname: String,
|
||||
macAddress: MACAddress?,
|
||||
session: XPCServerSession
|
||||
) async throws -> (attachment: Attachment, additionalData: XPCMessage?)
|
||||
|
||||
/// Return the attachment for a hostname if it is registered with the network.
|
||||
func lookup(hostname: String) async throws -> Attachment?
|
||||
}
|
||||
+1
-1
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerNetworkServer
|
||||
import ContainerResource
|
||||
import ContainerXPC
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
import Logging
|
||||
|
||||
public actor AllocationOnlyVmnetNetwork: Network {
|
||||
+2
-4
@@ -14,22 +14,20 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerNetworkServer
|
||||
import ContainerResource
|
||||
import ContainerXPC
|
||||
import Containerization
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import Dispatch
|
||||
import Foundation
|
||||
import Logging
|
||||
import Synchronization
|
||||
import SystemConfiguration
|
||||
import XPC
|
||||
import vmnet
|
||||
|
||||
/// Creates a vmnet network with reservation APIs.
|
||||
@available(macOS 26, *)
|
||||
public final class ReservedVmnetNetwork: Network {
|
||||
public final class ReservedVmnetNetwork: ContainerNetworkServer.Network {
|
||||
private struct State {
|
||||
var networkState: NetworkState
|
||||
var network: vmnet_network_ref?
|
||||
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerNetworkServiceClient
|
||||
import ContainerNetworkClient
|
||||
import ContainerOS
|
||||
import ContainerPersistence
|
||||
import ContainerResource
|
||||
@@ -177,7 +177,7 @@ public actor RuntimeService {
|
||||
do {
|
||||
for (index, info) in networkBootstrapInfos.enumerated() {
|
||||
let attachmentConfig = config.networks[index]
|
||||
let client = ContainerNetworkServiceClient.NetworkClient(id: attachmentConfig.network, plugin: info.pluginInfo.plugin)
|
||||
let client = ContainerNetworkClient.NetworkClient(id: attachmentConfig.network, plugin: info.pluginInfo.plugin)
|
||||
let session = client.connect()
|
||||
sessions.append(session)
|
||||
var (attachment, additionalData) = try await client.allocate(
|
||||
|
||||
+1
-50
@@ -14,11 +14,9 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import Testing
|
||||
|
||||
@testable import ContainerNetworkService
|
||||
@testable import ContainerNetworkServer
|
||||
|
||||
struct AttachmentAllocatorTest {
|
||||
@Test func testAllocateSingleHostname() async throws {
|
||||
@@ -145,53 +143,6 @@ struct AttachmentAllocatorTest {
|
||||
#expect(finalAddress4 == newAddress)
|
||||
}
|
||||
|
||||
@Test func testDisableAllocatorWhenEmpty() async throws {
|
||||
let allocator = try AttachmentAllocator(lower: 100, size: 10)
|
||||
|
||||
let disabled = await allocator.disableAllocator()
|
||||
|
||||
#expect(disabled == true)
|
||||
|
||||
// After disabling, allocation should fail
|
||||
await #expect(throws: Error.self) {
|
||||
try await allocator.allocate(hostname: "test-host")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func testDisableAllocatorWhenNotEmpty() async throws {
|
||||
let allocator = try AttachmentAllocator(lower: 100, size: 10)
|
||||
|
||||
_ = try await allocator.allocate(hostname: "test-host")
|
||||
|
||||
let disabled = await allocator.disableAllocator()
|
||||
|
||||
#expect(disabled == false)
|
||||
|
||||
// Since disable failed, should still be able to allocate
|
||||
let address = try await allocator.allocate(hostname: "another-host")
|
||||
#expect(address >= 100)
|
||||
#expect(address < 110)
|
||||
}
|
||||
|
||||
@Test func testDisableAfterDeallocatingAll() async throws {
|
||||
let allocator = try AttachmentAllocator(lower: 100, size: 10)
|
||||
|
||||
_ = try await allocator.allocate(hostname: "host1")
|
||||
_ = try await allocator.allocate(hostname: "host2")
|
||||
|
||||
try await allocator.deallocate(hostname: "host1")
|
||||
try await allocator.deallocate(hostname: "host2")
|
||||
|
||||
let disabled = await allocator.disableAllocator()
|
||||
|
||||
#expect(disabled == true)
|
||||
|
||||
// After disabling, allocation should fail
|
||||
await #expect(throws: Error.self) {
|
||||
try await allocator.allocate(hostname: "test-host")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func testMultipleDeallocationsOfSameHostname() async throws {
|
||||
let allocator = try AttachmentAllocator(lower: 100, size: 10)
|
||||
|
||||
@@ -21,8 +21,9 @@ opts+=("--target" "ContainerAPIClient")
|
||||
opts+=("--target" "ContainerRuntimeClient")
|
||||
opts+=("--target" "ContainerRuntimeLinuxClient")
|
||||
opts+=("--target" "ContainerRuntimeLinuxServer")
|
||||
opts+=("--target" "ContainerNetworkService")
|
||||
opts+=("--target" "ContainerNetworkServiceClient")
|
||||
opts+=("--target" "ContainerNetworkClient")
|
||||
opts+=("--target" "ContainerNetworkServer")
|
||||
opts+=("--target" "ContainerNetworkVmnetServer")
|
||||
opts+=("--target" "ContainerImagesService")
|
||||
opts+=("--target" "ContainerImagesServiceClient")
|
||||
opts+=("--target" "ContainerResource")
|
||||
|
||||
Reference in New Issue
Block a user