mirror of
https://github.com/apple/container.git
synced 2026-11-05 13:36:25 +00:00
181 lines
8.1 KiB
Swift
181 lines
8.1 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// 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 ContainerTestSupport
|
|
import Foundation
|
|
import Testing
|
|
|
|
@Suite(.serialized)
|
|
struct TestClusterNetworkingSerial {
|
|
|
|
private static let testImage = WarmupImage.alpine320
|
|
|
|
private func dumpEnv(_ f: ContainerFixture, clusterName: String) {
|
|
print("=== ENV DUMP [\(clusterName)] ===")
|
|
if let result = try? f.run(["system", "status"]) {
|
|
print("[system status]\n\(result.output)")
|
|
}
|
|
if let result = try? f.run(["list"]) {
|
|
print("[container list]\n\(result.output)")
|
|
}
|
|
if let result = try? f.run(["inspect", clusterName]) {
|
|
print("[inspect \(clusterName)] stdout: \(result.output) stderr: \(result.error)")
|
|
}
|
|
print("=== END ENV DUMP ===")
|
|
}
|
|
|
|
private func waitForPod(_ f: ContainerFixture, node: String, podName: String, timeoutSeconds: Int) throws {
|
|
print("[cluster-net] waitForPod \(podName) on \(node) (timeout=\(timeoutSeconds)s)")
|
|
let (_, status) = try f.kubectl(
|
|
node: node,
|
|
args: [
|
|
"wait", "--for=condition=Ready", "pod/\(podName)", "--timeout=\(timeoutSeconds)s",
|
|
])
|
|
guard status == 0 else {
|
|
let (podStatus, _) = try f.kubectl(node: node, args: ["get", "pod", podName, "--no-headers"])
|
|
print("[cluster-net] pod \(podName) status: \(podStatus.trimmingCharacters(in: .whitespacesAndNewlines))")
|
|
let (podDesc, _) = try f.kubectl(node: node, args: ["describe", "pod", podName])
|
|
print("[cluster-net] pod \(podName) describe:\n\(podDesc.prefix(1000))")
|
|
throw CommandError.executionFailed("pod \(podName) did not become ready within \(timeoutSeconds)s")
|
|
}
|
|
print("[cluster-net] pod \(podName) is Ready")
|
|
}
|
|
|
|
// Verify that a pod schedules, reaches Running, and can be exec'd into.
|
|
@Test func testPodsScheduleAndRun() async throws {
|
|
try await ContainerFixture.with { f in
|
|
let name = "cluster-\(f.testID)"
|
|
f.addCleanup { _ = try? f.run(["cluster", "delete", "--name", name]) }
|
|
|
|
try f.restoreWarmupImage(.kindestNodeV1_35_5)
|
|
print("[cluster-net] cluster create --name \(name)")
|
|
let result = try f.run(["cluster", "create", "--name", name])
|
|
print("[cluster-net] cluster create exit=\(result.status)")
|
|
if result.status != 0 {
|
|
print("[cluster-net] cluster create stderr: \(result.error)")
|
|
dumpEnv(f, clusterName: name)
|
|
f.dumpNodeDiagnostics(node: name)
|
|
}
|
|
try result.check()
|
|
|
|
print("[cluster-net] pulling \(Self.testImage.rawValue)")
|
|
try f.restoreWarmupImage(Self.testImage)
|
|
|
|
print("[cluster-net] cluster load-image --name \(name) \(Self.testImage.rawValue)")
|
|
let loadResult = try f.run(["cluster", "load-image", "--name", name, Self.testImage.rawValue])
|
|
print("[cluster-net] cluster load-image exit=\(loadResult.status)")
|
|
if loadResult.status != 0 { print("[cluster-net] cluster load-image stderr: \(loadResult.error)") }
|
|
#expect(loadResult.status == 0)
|
|
|
|
let (_, createStatus) = try f.kubectl(
|
|
node: name,
|
|
args: [
|
|
"run", "test-pod",
|
|
"--image=\(Self.testImage.rawValue)",
|
|
"--image-pull-policy=Never",
|
|
"--restart=Never",
|
|
"--", "sleep", "300",
|
|
])
|
|
#expect(createStatus == 0)
|
|
|
|
try waitForPod(f, node: name, podName: "test-pod", timeoutSeconds: 300)
|
|
|
|
let (output, execStatus) = try f.kubectl(
|
|
node: name,
|
|
args: [
|
|
"exec", "test-pod", "--", "echo", "hello",
|
|
])
|
|
#expect(execStatus == 0)
|
|
#expect(output.contains("hello"))
|
|
}
|
|
}
|
|
|
|
// Verify pod-to-service communication and CoreDNS resolution on a single node.
|
|
@Test func testPodToServiceCommunication() async throws {
|
|
try await ContainerFixture.with { f in
|
|
let name = "cluster-\(f.testID)"
|
|
f.addCleanup { _ = try? f.run(["cluster", "delete", "--name", name]) }
|
|
|
|
try f.restoreWarmupImage(.kindestNodeV1_35_5)
|
|
print("[cluster-net] cluster create --name \(name)")
|
|
let result = try f.run(["cluster", "create", "--name", name])
|
|
print("[cluster-net] cluster create exit=\(result.status)")
|
|
if result.status != 0 {
|
|
print("[cluster-net] cluster create stderr: \(result.error)")
|
|
dumpEnv(f, clusterName: name)
|
|
}
|
|
try result.check()
|
|
|
|
print("[cluster-net] pulling \(Self.testImage.rawValue)")
|
|
try f.restoreWarmupImage(Self.testImage)
|
|
|
|
print("[cluster-net] cluster load-image --name \(name) \(Self.testImage.rawValue)")
|
|
let loadResult = try f.run(["cluster", "load-image", "--name", name, Self.testImage.rawValue])
|
|
print("[cluster-net] cluster load-image exit=\(loadResult.status)")
|
|
if loadResult.status != 0 { print("[cluster-net] cluster load-image stderr: \(loadResult.error)") }
|
|
#expect(loadResult.status == 0)
|
|
|
|
// Server: alpine busybox httpd serving a static response.
|
|
let (_, serverStatus) = try f.kubectl(
|
|
node: name,
|
|
args: [
|
|
"run", "server",
|
|
"--image=\(Self.testImage.rawValue)",
|
|
"--image-pull-policy=Never",
|
|
"--restart=Never",
|
|
"--port=8080",
|
|
"--", "sh", "-c",
|
|
"while true; do printf 'HTTP/1.1 200 OK\\r\\nContent-Length: 2\\r\\nConnection: close\\r\\n\\r\\nok' | nc -l -p 8080; done",
|
|
])
|
|
#expect(serverStatus == 0)
|
|
try waitForPod(f, node: name, podName: "server", timeoutSeconds: 300)
|
|
|
|
// Expose server as a ClusterIP service.
|
|
let (_, exposeStatus) = try f.kubectl(
|
|
node: name,
|
|
args: [
|
|
"expose", "pod", "server", "--port=8080", "--name=server-svc",
|
|
])
|
|
#expect(exposeStatus == 0)
|
|
|
|
// Client pod that stays alive so we can exec into it.
|
|
let (_, clientStatus) = try f.kubectl(
|
|
node: name,
|
|
args: [
|
|
"run", "client",
|
|
"--image=\(Self.testImage.rawValue)",
|
|
"--image-pull-policy=Never",
|
|
"--restart=Never",
|
|
"--", "sleep", "300",
|
|
])
|
|
#expect(clientStatus == 0)
|
|
try waitForPod(f, node: name, podName: "client", timeoutSeconds: 300)
|
|
|
|
// Reach server via the service DNS name — exercises CoreDNS + kube-proxy.
|
|
print("[cluster-net] wget from client to server-svc:8080")
|
|
let (response, wgetStatus) = try f.kubectl(
|
|
node: name,
|
|
args: [
|
|
"exec", "client", "--", "sh", "-c",
|
|
"sleep 2 && wget -qO- http://server-svc:8080",
|
|
])
|
|
print("[cluster-net] wget exit=\(wgetStatus) response=\(response.trimmingCharacters(in: .whitespacesAndNewlines))")
|
|
#expect(wgetStatus == 0)
|
|
#expect(response.contains("ok"))
|
|
}
|
|
}
|
|
}
|