chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the Containerization 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if os(macOS)
|
||||
|
||||
import Containerization
|
||||
import ContainerizationArchive
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@testable import Containerization
|
||||
|
||||
private struct NilGatewayInterface: Interface {
|
||||
let ipv4Address: CIDRv4
|
||||
let ipv4Gateway: IPv4Address? = nil
|
||||
let macAddress: MACAddress? = nil
|
||||
|
||||
init() {
|
||||
self.ipv4Address = try! CIDRv4("192.168.64.2/24")
|
||||
}
|
||||
}
|
||||
|
||||
private struct NilGatewayNetwork: Network {
|
||||
mutating func createInterface(_ id: String) throws -> Interface? {
|
||||
NilGatewayInterface()
|
||||
}
|
||||
|
||||
mutating func releaseInterface(_ id: String) throws {}
|
||||
}
|
||||
|
||||
@Suite
|
||||
struct ContainerManagerTests {
|
||||
@Test func testCreateThrowsWhenGatewayMissing() async throws {
|
||||
let fm = FileManager.default
|
||||
let root = fm.uniqueTemporaryDirectory(create: true)
|
||||
defer { try? fm.removeItem(at: root) }
|
||||
|
||||
let kernelPath = root.appendingPathComponent("vmlinux")
|
||||
fm.createFile(atPath: kernelPath.path, contents: Data(), attributes: nil)
|
||||
let initfsPath = root.appendingPathComponent("initfs.ext4")
|
||||
fm.createFile(atPath: initfsPath.path, contents: Data(), attributes: nil)
|
||||
|
||||
let kernel = Kernel(path: kernelPath, platform: .linuxArm)
|
||||
let initfs = Mount.block(format: "ext4", source: initfsPath.path, destination: "/")
|
||||
|
||||
var manager = try ContainerManager(
|
||||
kernel: kernel,
|
||||
initfs: initfs,
|
||||
root: root,
|
||||
network: NilGatewayNetwork()
|
||||
)
|
||||
|
||||
let tempDir = fm.uniqueTemporaryDirectory()
|
||||
defer { try? fm.removeItem(at: tempDir) }
|
||||
|
||||
let tarPath = Foundation.Bundle.module.url(forResource: "scratch", withExtension: "tar")!
|
||||
let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)
|
||||
let rejectedPaths = try reader.extractContents(to: tempDir)
|
||||
#expect(rejectedPaths.isEmpty)
|
||||
|
||||
let images = try await manager.imageStore.load(from: tempDir)
|
||||
let image = images.first!
|
||||
|
||||
let rootfsPath = root.appendingPathComponent("rootfs.ext4")
|
||||
fm.createFile(atPath: rootfsPath.path, contents: Data(), attributes: nil)
|
||||
let rootfs = Mount.block(format: "ext4", source: rootfsPath.path, destination: "/")
|
||||
|
||||
do {
|
||||
_ = try await manager.create("test-nil-gateway", image: image, rootfs: rootfs) { _ in }
|
||||
#expect(Bool(false), "expected invalidState error for missing ipv4 gateway")
|
||||
} catch let error as ContainerizationError {
|
||||
#expect(error.code == .invalidState)
|
||||
#expect(error.message.contains("missing ipv4 gateway"))
|
||||
} catch {
|
||||
#expect(Bool(false), "unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func testNetworkingFalseSkipsInterfaceCreation() async throws {
|
||||
let fm = FileManager.default
|
||||
let root = fm.uniqueTemporaryDirectory(create: true)
|
||||
defer { try? fm.removeItem(at: root) }
|
||||
|
||||
let kernelPath = root.appendingPathComponent("vmlinux")
|
||||
fm.createFile(atPath: kernelPath.path, contents: Data(), attributes: nil)
|
||||
let initfsPath = root.appendingPathComponent("initfs.ext4")
|
||||
fm.createFile(atPath: initfsPath.path, contents: Data(), attributes: nil)
|
||||
|
||||
let kernel = Kernel(path: kernelPath, platform: .linuxArm)
|
||||
let initfs = Mount.block(format: "ext4", source: initfsPath.path, destination: "/")
|
||||
|
||||
// Use NilGatewayNetwork — with networking: true this would throw invalidState,
|
||||
// but with networking: false the network's createInterface() is never called.
|
||||
var manager = try ContainerManager(
|
||||
kernel: kernel,
|
||||
initfs: initfs,
|
||||
root: root,
|
||||
network: NilGatewayNetwork()
|
||||
)
|
||||
|
||||
let tempDir = fm.uniqueTemporaryDirectory()
|
||||
defer { try? fm.removeItem(at: tempDir) }
|
||||
|
||||
let tarPath = Foundation.Bundle.module.url(forResource: "scratch", withExtension: "tar")!
|
||||
let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)
|
||||
let rejectedPaths = try reader.extractContents(to: tempDir)
|
||||
#expect(rejectedPaths.isEmpty)
|
||||
|
||||
let images = try await manager.imageStore.load(from: tempDir)
|
||||
let image = images.first!
|
||||
|
||||
let rootfsPath = root.appendingPathComponent("rootfs.ext4")
|
||||
fm.createFile(atPath: rootfsPath.path, contents: Data(), attributes: nil)
|
||||
let rootfs = Mount.block(format: "ext4", source: rootfsPath.path, destination: "/")
|
||||
|
||||
// With networking: false, NilGatewayNetwork.createInterface() is never called,
|
||||
// so we should not get the "missing ipv4 gateway" error.
|
||||
// The container creation will fail for other reasons (dummy VMM), but the
|
||||
// configuration closure should see empty interfaces.
|
||||
var closureWasCalled = false
|
||||
do {
|
||||
_ = try await manager.create(
|
||||
"test-no-networking",
|
||||
image: image,
|
||||
rootfs: rootfs,
|
||||
networking: false
|
||||
) { config in
|
||||
closureWasCalled = true
|
||||
#expect(config.interfaces.isEmpty)
|
||||
#expect(config.dns == nil)
|
||||
}
|
||||
} catch {
|
||||
// Container creation may fail due to dummy kernel/VMM — that's expected.
|
||||
// The key assertion is in the configuration closure above.
|
||||
let description = String(describing: error)
|
||||
#expect(!description.contains("missing ipv4 gateway"))
|
||||
}
|
||||
#expect(closureWasCalled, "configuration closure must be invoked to validate interfaces")
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user