✓ Exact match of API objects in both client and the Kubernetes version.
- API objects mismatches either due to the removal of old API or the addition of new API. However, everything the
client and Kubernetes have in common will work.
Examples
Concrete examples for using the Swiftkube tooling reside in the Swiftkube:Examples
repository.
Usage
Creating a client
To create a client just import SwiftkubeClient and init an instance.
import SwiftkubeClient
let client = try KubernetesClient()
You should shut down the KubernetesClient instance when you’re done using it, which in turn shuts down the underlying
HTTPClient. Thus, you shouldn’t call client.syncShutdown() before all requests have finished. You can also shut down
the client asynchronously in an async/await context or by providing a DispatchQueue for the completion callback.
// when finished close the client
try client.syncShutdown()
// async/await
try await client.shutdown()
// DispatchQueue
let queue: DispatchQueue = ...
client.shutdown(queue: queue) { (error: Error?) in
print(error)
}
Configuring the client
The client tries to resolve a kube config automatically from different sources in the following order:
Kube config file in the user’s $HOME/.kube/config directory
ServiceAccount token located at /var/run/secrets/kubernetes.io/serviceaccount/token and a mounted CA certificate,
if it’s running in Kubernetes.
Alternatively it can be configured manually, for example:
Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel
// Create a resource instance and post it
let configMap = core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name: "test"),
data: ["foo": "bar"]
)
try cm = try await client.configMaps.create(inNamespace: .default, configMap)
// Or inline via a builder
let pod = try await client.pods.create(inNamespace: .default) {
sk.pod {
$0.metadata = sk.metadata(name: "nginx")
$0.spec = sk.podSpec {
$0.containers = [
sk.container(name: "nginx") {
$0.image = "nginx"
}
]
}
}
}
Watch a resource
You can watch for Kubernetes events about specific objects via the watch API.
Watching resources opens a persistent connection to the API server. The connection is represented by a SwiftkubeClientTask
instance, that acts as an active “subscription” to the events stream.
The task instance must be started explicitly via SwiftkubeClientTask/start(), which returns an
AsyncThrowingStream, that starts yielding items immediately as they are received from the Kubernetes API server.
The async stream buffers its results if there are no active consumers. The AsyncThrowingStream.BufferingPolicy.unbounded
buffering policy is used, which should be taken into consideration.
let task: SwiftkubeClientTask = client.pods.watch(in: .allNamespaces)
let stream = task.start()
for try await event in stream {
print(event)
}
You can also pass ListOptions to filter, i.e. select the required objects:
let options = [
.labelSelector(.eq(["app": "nginx"])),
.labelSelector(.exists(["env"]))
]
let task = client.pods.watch(in: .default, options: options)
The client reconnects automatically and restarts the watch upon encountering non-recoverable errors. The
reconnect-behaviour can be controlled by passing an instance of RetryStrategy.
The default strategy is 10 retry attempts with a fixed 5 seconds delay between each attempt. The initial delay is one
second. A jitter of 0.2 seconds is applied.
Passing RetryStrategy.never disables any reconnection attempts.
let strategy = RetryStrategy(
policy: .maxAttemtps(20),
backoff: .exponentiaBackoff(maxDelay: 60, multiplier: 2.0),
initialDelay = 5.0,
jitter = 0.2
)
let task = client.pods.watch(in: .default, retryStrategy: strategy)
for try await event in task.stream() {
print(event)
}
The task must be cancelled when it is no longer needed:
task.cancel()
Follow logs
The follow API resembles the watch, but instead of events, it emits the log lines.
The client does not reconnect on errors in follow mode.
let task = client.pods.follow(in: .default, name: "nginx", container: "app")
for try await line in task.start() {
print(line)
}
// The task can be cancelled later to stop following logs
task.cancel()
Discovery
The client provides a discovery interface for the API server, which can be used to retrieve the server version, the API
groups and the API resources for a specific group version.
let version: Info = try await client.discovery.serverVersion()
let groups: meta.v1.APIGroupList = try await client.discovery.serverGroups()
let resources: meta.v1.APIResourceList = try await client.discovery.serverResources(forGroupVersion: "apps/v1")
Loading from external sources
A resource can be loaded from a file or a URL:
// Load from URL, e.g. a file
let url = URL(fileURLWithPath: "/path/to/manifest.yaml")
let deployment = try apps.v1.Deployment.load(contentsOf: url)
Type-erased usage
Often when working with Kubernetes the concrete type of the resource is not known or not relevant, e.g. when creating
resources from a YAML manifest file. Other times the type or kind of the resource must be derived at runtime given its
string representation.
Leveraging SwiftkubeModel‘s type-erased resource implementations UnstructuredResource and its corresponding
List-Type UnstructuredResourceList it is possible to have a generic client instance, which must be initialized
with a GroupVersionResource type:
guard let gvr = try? GroupVersionResource(for: "deployment") else {
// handle this
}
// Get by name
let resource: UnstructuredResource = try await client.for(gvr: gvr).get(in: .default , name: "nginx")
// List all
let resources: UnstructuredResourceList = try await client.for(gvr: gvr).list(in: .allNamespaces)
GroupVersionKind & GroupVersionResource
A GroupVersionKind & GroupVersionResource can be initialized from:
KubernetesAPIResource instance
KubernetesAPIResource type
Full API Group string
Lower-cased singular resource kind
Lower-cased plural resource name
Lower-cased short resource name
let deployment = ..
let gvk = GroupVersionKind(of: deployment)
let gvr = GroupVersionResource(of: deployment)
let gvk = GroupVersionKind(of: apps.v1.Deployment.self)
let gvr = GroupVersionResource(for: "configmaps")
let gvk = GroupVersionKind(for: "cm")
let gvr = GroupVersionResource(for: "cm")
// etc.
CRD Support
SwiftkubeClient supports Custom Resource Definitions (CRDs) natively. For example, a CRD manifest can be loaded
from a YAML file or created programmatically, and then created via the client DSL:
let crd = apiextensions.v1.CustomResourceDefinition.load(contentsOf: URL(filePath: "/path/to/crd.yaml"))
try await client.apiExtensionsV1.customResourceDefinitions.create(crd)
The KubernetesClient can now be “extended”, in order to manage the Custom Resources. One way would be to use the
UnstructuredResource described in the previous section given some GroupVersionResource.
However, the client can work with any object that implement the relevant marker protocols, which allows for custom types
to be defined and used directly.
KubernetesAPIResource marks the object as a Kubernetes resource that has a corresponding API endpoint
NamespacedResource & ClusterScopedResource to indicate whether the resource is namespaced or cluster-scoped
ReadableResource activates the get, list and watch API for the resource
CreatableResource activates the create API for the resource
ReplaceableResource activates the update API for the resource
DeletableResource activates the delete API for the resource
CollectionDeletableResource activate the deleteAll API for the resource
ScalableResource activates the scale API for the resource
MetadataHavingResource indicates, that the resource has a metadata field of type meta.v1.ObjectMeta?
StatusHavingResource indicate, that the resource has a scale field (w/o assuming its type)
The following custom structs can be defined:
struct CronTab: KubernetesAPIResource, NamespacedResource, MetadataHavingResource,
ReadableResource, CreatableResource, ListableResource {
typealias List = CronTabList
var apiVersion = "example.com/v1"
var kind = "CronTab"
var metadata: meta.v1.ObjectMeta?
var spec: CronTabSpec
}
struct CronTabSpec: Codable, Hashable {
var cronSpec: String
var image: String
var replicas: Int
}
struct CronTabList: KubernetesResourceList {
var apiVersion = "example.com/v1"
var kind = "crontabs"
var items: [CronTab]
}
Now, the new Custom Resource can be used like any other Kubernetes resource:
let gvr = GroupVersionResource(
group: "example.com",
version: "v1",
resource: "crontabs"
)
let cronTabClient = client.for(CronTab.self, gvr: gvr)
let cronTab = CronTab(
metadata: meta.v1.ObjectMeta(name: "new-cron"),
spec: CronTabSpec(
cronSpec : "* * * * */5",
image: "some-cron-image",
replicas: 2
)
)
let new = try await cronTabClient.create(in: .default, cronTab)
let cronTabs: CronTabList = try await cronTabClient.list(in: .allNamespaces)
Metrics
KubernetesClient uses SwiftMetrics to collect metric information about the
requests count and latencies.
The following metrics are gathered:
sk_http_requests_total(counter): the total count of the requests made by the client.
sk_http_request_errors_total(counter): the total number of requests made, that returned a http error.
sk_request_errors_total(counter): the total number of requests that couldn’t be dispatched due to non-http errors.
sk_http_request_duration_seconds(timer): the complete request durations.
Collecting the metrics
To collect the metrics you have to bootstrap a metrics backend in your application. For example, you can collect the
metrics to prometheus via SwiftPrometheus:
import Metrics
import Prometheus
let prom = PrometheusClient()
MetricsSystem.bootstrap(prom)
and expose a /metrics endpoint for scraping:
// if using vapor
app.get("metrics") { request -> EventLoopFuture<String> in
let promise = request.eventLoop.makePromise(of: String.self)
try MetricsSystem.prometheus().collect(into: promise)
return promise.futureResult
}
Installation
To use the SwiftkubeClient in a SwiftPM project, add the following line to the dependencies in your Package.swift file:
Table of contents
Overview
Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.
/scaleAPI/statusAPICompatibility Matrix
0.6.x0.7.x...0.9.x0.10.x0.11.x0.12.x...0.13.x0.14.x0.15.x✓Exact match of API objects in both client and the Kubernetes version.-API objects mismatches either due to the removal of old API or the addition of new API. However, everything theExamples
Concrete examples for using the
Swiftkubetooling reside in the Swiftkube:Examples repository.Usage
Creating a client
To create a client just import
SwiftkubeClientand init an instance.You should shut down the
KubernetesClientinstance when you’re done using it, which in turn shuts down the underlyingHTTPClient. Thus, you shouldn’t callclient.syncShutdown()before all requests have finished. You can also shut down the client asynchronously in an async/await context or by providing aDispatchQueuefor the completion callback.Configuring the client
The client tries to resolve a
kube configautomatically from different sources in the following order:$HOME/.kube/configdirectoryServiceAccounttoken located at/var/run/secrets/kubernetes.io/serviceaccount/tokenand a mounted CA certificate,Alternatively it can be configured manually, for example:
Client authentication
The following authentication schemes are supported:
.basicAuth(username: String, password: String).bearer(token: String).x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)Client DSL
SwiftkubeClientdefines convenience API to work with Kubernetes resources. Using this DSL is the same for all resources.The client exposes asynchronous functions using the new Swift concurrency model.
List resources
You can filter the listed resources or limit the returned list size via the
ListOptions:Get a resource
You can also provide the following
ReadOptions:Delete a resource
You can pass an instance of
meta.v1.DeleteOptionsto control the behaviour of the delete operation:Create and update a resource
Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel
Watch a resource
You can watch for Kubernetes events about specific objects via the
watchAPI.Watching resources opens a persistent connection to the API server. The connection is represented by a
SwiftkubeClientTaskinstance, that acts as an active “subscription” to the events stream.The task instance must be started explicitly via
SwiftkubeClientTask/start(), which returns anAsyncThrowingStream, that starts yielding items immediately as they are received from the Kubernetes API server.You can also pass
ListOptionsto filter, i.e. select the required objects:The client reconnects automatically and restarts the watch upon encountering non-recoverable errors. The reconnect-behaviour can be controlled by passing an instance of
RetryStrategy.The default strategy is 10 retry attempts with a fixed 5 seconds delay between each attempt. The initial delay is one second. A jitter of 0.2 seconds is applied.
Passing
RetryStrategy.neverdisables any reconnection attempts.The task must be cancelled when it is no longer needed:
Follow logs
The
followAPI resembles thewatch, but instead of events, it emits the log lines.followmode.Discovery
The client provides a discovery interface for the API server, which can be used to retrieve the server version, the API groups and the API resources for a specific group version.
Loading from external sources
A resource can be loaded from a file or a URL:
Type-erased usage
Often when working with Kubernetes the concrete type of the resource is not known or not relevant, e.g. when creating resources from a YAML manifest file. Other times the type or kind of the resource must be derived at runtime given its string representation.
Leveraging
SwiftkubeModel‘s type-erased resource implementationsUnstructuredResourceand its corresponding List-TypeUnstructuredResourceListit is possible to have a generic client instance, which must be initialized with aGroupVersionResourcetype:GroupVersionKind & GroupVersionResource
A
GroupVersionKind&GroupVersionResourcecan be initialized from:KubernetesAPIResourceinstanceKubernetesAPIResourcetypeCRD Support
SwiftkubeClientsupports Custom Resource Definitions (CRDs) natively. For example, a CRD manifest can be loaded from a YAML file or created programmatically, and then created via the client DSL:The
KubernetesClientcan now be “extended”, in order to manage the Custom Resources. One way would be to use theUnstructuredResourcedescribed in the previous section given someGroupVersionResource.However, the client can work with any object that implement the relevant marker protocols, which allows for custom types to be defined and used directly.
Here is a complete example to clarify.
Given the following CRD:
The marker protocols are:
KubernetesAPIResourcemarks the object as a Kubernetes resource that has a corresponding API endpointNamespacedResource&ClusterScopedResourceto indicate whether the resource is namespaced or cluster-scopedReadableResourceactivates theget,listandwatchAPI for the resourceCreatableResourceactivates thecreateAPI for the resourceReplaceableResourceactivates theupdateAPI for the resourceDeletableResourceactivates thedeleteAPI for the resourceCollectionDeletableResourceactivate thedeleteAllAPI for the resourceScalableResourceactivates thescaleAPI for the resourceMetadataHavingResourceindicates, that the resource has ametadatafield of typemeta.v1.ObjectMeta?StatusHavingResourceindicate, that the resource has ascalefield (w/o assuming its type)The following custom structs can be defined:
Now, the new Custom Resource can be used like any other Kubernetes resource:
Metrics
KubernetesClientuses SwiftMetrics to collect metric information about the requests count and latencies.The following metrics are gathered:
sk_http_requests_total(counter): the total count of the requests made by the client.sk_http_request_errors_total(counter): the total number of requests made, that returned a http error.sk_request_errors_total(counter): the total number of requests that couldn’t be dispatched due to non-http errors.sk_http_request_duration_seconds(timer): the complete request durations.Collecting the metrics
To collect the metrics you have to bootstrap a metrics backend in your application. For example, you can collect the metrics to prometheus via
SwiftPrometheus:and expose a
/metricsendpoint for scraping:Installation
To use the
SwiftkubeClientin a SwiftPM project, add the following line to the dependencies in yourPackage.swiftfile:then include it as a dependency in your target:
Then run
swift build.License
Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.