mirror of
https://github.com/apple/container.git
synced 2026-11-05 13:36:25 +00:00
173 lines
7.6 KiB
Swift
173 lines
7.6 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 SystemPackage
|
|
import Testing
|
|
import Yams
|
|
|
|
@Suite(.serialized)
|
|
struct TestClusterWriteConfigSerial {
|
|
|
|
private func kubeconfigPath() -> FilePath {
|
|
FilePath(FileManager.default.homeDirectoryForCurrentUser.path)
|
|
.appending(".kube")
|
|
.appending("config")
|
|
}
|
|
|
|
private func loadKubeconfig() throws -> [String: Any] {
|
|
let yaml = try String(contentsOfFile: kubeconfigPath().string, encoding: .utf8)
|
|
guard let parsed = try Yams.load(yaml: yaml) as? [String: Any] else {
|
|
throw CommandError.executionFailed("could not parse kubeconfig")
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
private func dumpKubeconfig(for name: String) {
|
|
let path = kubeconfigPath()
|
|
guard FileManager.default.fileExists(atPath: path.string) else {
|
|
print("[cluster-cfg] \(path.lastComponent?.string ?? "config") does not exist")
|
|
return
|
|
}
|
|
guard let kubeconfig = try? loadKubeconfig() else {
|
|
print("[cluster-cfg] \(path.lastComponent?.string ?? "config") could not be parsed")
|
|
return
|
|
}
|
|
let clusters = (kubeconfig["clusters"] as? [[String: Any]])?.compactMap { $0["name"] as? String } ?? []
|
|
let contexts = (kubeconfig["contexts"] as? [[String: Any]])?.compactMap { $0["name"] as? String } ?? []
|
|
let current = kubeconfig["current-context"] as? String ?? "<none>"
|
|
print("[cluster-cfg] kubeconfig: clusters=\(clusters) contexts=\(contexts) current-context=\(current)")
|
|
}
|
|
|
|
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 kubeconfigContains(name: String) -> Bool {
|
|
guard let kubeconfig = try? loadKubeconfig() else { return false }
|
|
let clusters = (kubeconfig["clusters"] as? [[String: Any]]) ?? []
|
|
return clusters.contains { $0["name"] as? String == name }
|
|
}
|
|
|
|
@Test func testWriteConfigMergesContext() 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-cfg] cluster create --name \(name)")
|
|
let result = try f.run(["cluster", "create", "--name", name])
|
|
print("[cluster-cfg] cluster create exit=\(result.status)")
|
|
if result.status != 0 {
|
|
print("[cluster-cfg] cluster create stderr: \(result.error)")
|
|
dumpEnv(f, clusterName: name)
|
|
f.dumpNodeDiagnostics(node: name)
|
|
}
|
|
try result.check()
|
|
|
|
print("[cluster-cfg] cluster write-config --name \(name)")
|
|
let writeResult = try f.run(["cluster", "write-config", "--name", name])
|
|
print("[cluster-cfg] cluster write-config exit=\(writeResult.status)")
|
|
if writeResult.status != 0 {
|
|
print("[cluster-cfg] cluster write-config stderr: \(writeResult.error)")
|
|
dumpEnv(f, clusterName: name)
|
|
}
|
|
#expect(writeResult.status == 0)
|
|
|
|
let kubeconfig = try loadKubeconfig()
|
|
|
|
let clusters = (kubeconfig["clusters"] as? [[String: Any]]) ?? []
|
|
#expect(clusters.contains { $0["name"] as? String == name })
|
|
|
|
let contexts = (kubeconfig["contexts"] as? [[String: Any]]) ?? []
|
|
#expect(contexts.contains { $0["name"] as? String == name })
|
|
|
|
let users = (kubeconfig["users"] as? [[String: Any]]) ?? []
|
|
#expect(users.contains { $0["name"] as? String == name })
|
|
}
|
|
}
|
|
|
|
@Test func testWriteConfigIsIdempotent() 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-cfg] cluster create --name \(name)")
|
|
let result = try f.run(["cluster", "create", "--name", name])
|
|
print("[cluster-cfg] cluster create exit=\(result.status)")
|
|
if result.status != 0 {
|
|
print("[cluster-cfg] cluster create stderr: \(result.error)")
|
|
dumpEnv(f, clusterName: name)
|
|
f.dumpNodeDiagnostics(node: name)
|
|
}
|
|
try result.check()
|
|
|
|
print("[cluster-cfg] cluster write-config --name \(name) (first)")
|
|
_ = try f.run(["cluster", "write-config", "--name", name])
|
|
print("[cluster-cfg] cluster write-config --name \(name) (second)")
|
|
let secondResult = try f.run(["cluster", "write-config", "--name", name])
|
|
print("[cluster-cfg] cluster write-config (second) exit=\(secondResult.status)")
|
|
#expect(secondResult.status == 0)
|
|
|
|
let kubeconfig = try loadKubeconfig()
|
|
let clusters = (kubeconfig["clusters"] as? [[String: Any]]) ?? []
|
|
let matchingClusters = clusters.filter { $0["name"] as? String == name }
|
|
#expect(matchingClusters.count == 1)
|
|
}
|
|
}
|
|
|
|
@Test func testDeleteClusterCleansUpKubeconfig() 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-cfg] cluster create --name \(name)")
|
|
let result = try f.run(["cluster", "create", "--name", name])
|
|
print("[cluster-cfg] cluster create exit=\(result.status)")
|
|
if result.status != 0 {
|
|
print("[cluster-cfg] cluster create stderr: \(result.error)")
|
|
dumpEnv(f, clusterName: name)
|
|
f.dumpNodeDiagnostics(node: name)
|
|
}
|
|
try result.check()
|
|
|
|
print("[cluster-cfg] cluster write-config --name \(name)")
|
|
_ = try f.run(["cluster", "write-config", "--name", name])
|
|
#expect(kubeconfigContains(name: name))
|
|
|
|
print("[cluster-cfg] cluster delete --name \(name)")
|
|
let deleteResult = try f.run(["cluster", "delete", "--name", name])
|
|
print("[cluster-cfg] cluster delete exit=\(deleteResult.status) stderr=\(deleteResult.error)")
|
|
#expect(deleteResult.status == 0)
|
|
|
|
#expect(!kubeconfigContains(name: name))
|
|
}
|
|
}
|
|
}
|