diff --git a/Package.swift b/Package.swift index b14bffe..eb37bb1 100644 --- a/Package.swift +++ b/Package.swift @@ -6,12 +6,13 @@ import PackageDescription let package = Package( name: "SwiftkubeClient", platforms: [ - .macOS(.v10_13), .iOS(.v12), .tvOS(.v12), .watchOS(.v5) + .macOS(.v10_13), .iOS(.v12), .tvOS(.v12), .watchOS(.v5), ], products: [ .library( name: "SwiftkubeClient", - targets: ["SwiftkubeClient"]), + targets: ["SwiftkubeClient"] + ), ], dependencies: [ .package(name: "SwiftkubeModel", url: "https://github.com/swiftkube/model.git", .upToNextMajor(from: "0.4.0")), @@ -29,11 +30,13 @@ let package = Package( .product(name: "Logging", package: "swift-log"), .product(name: "Metrics", package: "swift-metrics"), .product(name: "Yams", package: "Yams"), - ]), + ] + ), .testTarget( name: "SwiftkubeClientTests", dependencies: [ - "SwiftkubeClient" - ]), + "SwiftkubeClient", + ] + ), ] ) diff --git a/Sources/SwiftkubeClient/Client/KubernetesClient.swift b/Sources/SwiftkubeClient/Client/KubernetesClient.swift index 4c2da52..70c8a14 100644 --- a/Sources/SwiftkubeClient/Client/KubernetesClient.swift +++ b/Sources/SwiftkubeClient/Client/KubernetesClient.swift @@ -100,7 +100,7 @@ public class KubernetesClient { guard let config = - (try? LocalFileConfigLoader().load(logger: logger)) ?? + (try? LocalKubeConfigLoader().load(logger: logger)) ?? (try? ServiceAccountConfigLoader().load(logger: logger)) else { return nil @@ -109,6 +109,28 @@ public class KubernetesClient { self.init(config: config, provider: provider, logger: logger) } + /// Create a new instance of the Kubernetes client. + /// + /// - Parameters: + /// - fromURL: The url to load the configuration from for this client instance. It can be a local file or remote URL. + /// - provider: Specify how `EventLoopGroup` will be created. + /// - logger: The logger to use for this client. + public convenience init?( + fromURL: URL, + provider: HTTPClient.EventLoopGroupProvider = .shared(MultiThreadedEventLoopGroup(numberOfThreads: 1)), + logger: Logger? = nil + ) { + let logger = logger ?? KubernetesClient.loggingDisabled + + guard + let config = try? LocalFileConfigLoader(fromURL: fromURL).load(logger: logger) + else { + return nil + } + + self.init(config: config, provider: provider, logger: logger) + } + /// Create a new instance of the Kubernetes client. /// /// - Parameters: diff --git a/Sources/SwiftkubeClient/Config/KubernetesClientConfig.swift b/Sources/SwiftkubeClient/Config/KubernetesClientConfig.swift index 24cdf9e..3ea260b 100644 --- a/Sources/SwiftkubeClient/Config/KubernetesClientConfig.swift +++ b/Sources/SwiftkubeClient/Config/KubernetesClientConfig.swift @@ -50,20 +50,14 @@ internal protocol KubernetesClientConfigLoader { func load(logger: Logger) throws -> KubernetesClientConfig? } -// MARK: - LocalFileConfigLoader +// MARK: - URLConfigLoader -internal struct LocalFileConfigLoader: KubernetesClientConfigLoader { +internal struct URLConfigLoader { - internal func load(logger: Logger) throws -> KubernetesClientConfig? { + internal func load(fromURL: URL, logger: Logger) throws -> KubernetesClientConfig? { let decoder = YAMLDecoder() - guard let homePath = ProcessInfo.processInfo.environment["HOME"] else { - logger.info("Skipping kubeconfig in $HOME/.kube/config because HOME env variable is not set.") - return nil - } - - let fileURL = URL(fileURLWithPath: homePath + "/.kube/config") - guard let contents = try? String(contentsOf: fileURL, encoding: .utf8) else { + guard let contents = try? String(contentsOf: fromURL, encoding: .utf8) else { return nil } @@ -105,6 +99,28 @@ internal struct LocalFileConfigLoader: KubernetesClientConfigLoader { } } +// MARK: - LocalFileConfigLoader + +internal struct LocalFileConfigLoader: KubernetesClientConfigLoader { + let fromURL: URL + func load(logger: Logger) throws -> KubernetesClientConfig? { + try? URLConfigLoader().load(fromURL: fromURL, logger: logger) + } +} + +// MARK: - LocalKubeConfigLoader + +internal struct LocalKubeConfigLoader: KubernetesClientConfigLoader { + func load(logger: Logger) throws -> KubernetesClientConfig? { + guard let homePath = ProcessInfo.processInfo.environment["HOME"] else { + logger.info("Skipping kubeconfig in $HOME/.kube/config because HOME env variable is not set.") + return nil + } + let kubeConfigURL = URL(fileURLWithPath: homePath + "/.kube/config") + return try? URLConfigLoader().load(fromURL: kubeConfigURL, logger: logger) + } +} + // MARK: - ServiceAccountConfigLoader internal struct ServiceAccountConfigLoader: KubernetesClientConfigLoader { diff --git a/Tests/SwiftkubeClientTests/NodeTests.swift b/Tests/SwiftkubeClientTests/NodeTests.swift index 72feca5..4a6458f 100644 --- a/Tests/SwiftkubeClientTests/NodeTests.swift +++ b/Tests/SwiftkubeClientTests/NodeTests.swift @@ -16,8 +16,8 @@ import AsyncHTTPClient import NIO -import SwiftkubeModel import SwiftkubeClient +import SwiftkubeModel import XCTest final class NodeTests: XCTestCase { diff --git a/Tests/SwiftkubeClientTests/RequestBuilderTests.swift b/Tests/SwiftkubeClientTests/RequestBuilderTests.swift index 810f65b..954267d 100644 --- a/Tests/SwiftkubeClientTests/RequestBuilderTests.swift +++ b/Tests/SwiftkubeClientTests/RequestBuilderTests.swift @@ -17,8 +17,8 @@ import AsyncHTTPClient import NIO import NIOHTTP1 -import SwiftkubeModel @testable import SwiftkubeClient +import SwiftkubeModel import XCTest final class RequestBuilderTests: XCTestCase { @@ -85,7 +85,7 @@ final class RequestBuilderTests: XCTestCase { let request = try? builder.to(.GET).in(.default).with(options: [ .labelSelector(.eq(["app": "nginx"])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "labelSelector=app%3Dnginx") } @@ -95,7 +95,7 @@ final class RequestBuilderTests: XCTestCase { let request = try? builder.to(.GET).in(.default).with(options: [ .labelSelector(.neq(["app": "nginx"])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "labelSelector=app!%3Dnginx") } @@ -105,7 +105,7 @@ final class RequestBuilderTests: XCTestCase { let request = try? builder.to(.GET).in(.default).with(options: [ .labelSelector(.in(["env": ["dev", "staging"]])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "labelSelector=env%20in%20(dev,staging)") } @@ -114,17 +114,16 @@ final class RequestBuilderTests: XCTestCase { let request = try? builder.to(.GET).in(.default).with(options: [ .labelSelector(.notIn(["env": ["dev", "staging"]])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "labelSelector=env%20notin%20(dev,staging)") } - func testGetWithListOptions_Exists() { let builder = RequestBuilder(config: config, gvk: gvk) let request = try? builder.to(.GET).in(.default).with(options: [ .labelSelector(.exists(["app", "env"])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "labelSelector=app,env") } @@ -134,7 +133,7 @@ final class RequestBuilderTests: XCTestCase { let request = try? builder.to(.GET).in(.default).with(options: [ .fieldSelector(.eq(["app": "nginx"])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "fieldSelector=app%3Dnginx") } @@ -144,7 +143,7 @@ final class RequestBuilderTests: XCTestCase { let request = try? builder.to(.GET).in(.default).with(options: [ .fieldSelector(.neq(["app": "nginx"])), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "fieldSelector=app!%3Dnginx") } @@ -152,9 +151,9 @@ final class RequestBuilderTests: XCTestCase { func testGetWithListOptions_Limit() { let builder = RequestBuilder(config: config, gvk: gvk) let request = try? builder.to(.GET).in(.default).with(options: [ - .limit(2) + .limit(2), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "limit=2") } @@ -162,9 +161,9 @@ final class RequestBuilderTests: XCTestCase { func testGetWithListOptions_Version() { let builder = RequestBuilder(config: config, gvk: gvk) let request = try? builder.to(.GET).in(.default).with(options: [ - .resourceVersion("20") + .resourceVersion("20"), ]) - .build() + .build() XCTAssertEqual(request?.url.query, "resourceVersion=20") } diff --git a/Tests/SwiftkubeClientTests/RetryStrategy.swift b/Tests/SwiftkubeClientTests/RetryStrategy.swift index 54ad473..fe351bf 100644 --- a/Tests/SwiftkubeClientTests/RetryStrategy.swift +++ b/Tests/SwiftkubeClientTests/RetryStrategy.swift @@ -86,7 +86,7 @@ final class RetryStrategyTests: XCTestCase { XCTAssertEqual(attempts, [ RetryAttempt(attempt: 1, delay: 0.0), RetryAttempt(attempt: 2, delay: 0.0), - RetryAttempt(attempt: 3, delay: 0.0) + RetryAttempt(attempt: 3, delay: 0.0), ]) } @@ -97,7 +97,7 @@ final class RetryStrategyTests: XCTestCase { XCTAssertEqual(attempts, [ RetryAttempt(attempt: 1, delay: 10.0), RetryAttempt(attempt: 2, delay: 20.0), - RetryAttempt(attempt: 3, delay: 30.0) + RetryAttempt(attempt: 3, delay: 30.0), ]) } @@ -108,7 +108,7 @@ final class RetryStrategyTests: XCTestCase { XCTAssertEqual(attempts, [ RetryAttempt(attempt: 1, delay: 0.0), RetryAttempt(attempt: 2, delay: 0.0), - RetryAttempt(attempt: 3, delay: 0.0) + RetryAttempt(attempt: 3, delay: 0.0), ]) }