Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Kubernetes 1.34.6Swift Package ManagerCI Status

Table of contents

Overview

Swift client for talking to a Kubernetes cluster via a fluent DSL based on SwiftNIO and the AysncHTTPClient.

  • Covers all Kubernetes API Groups in v1.34.6
  • Automatic configuration discovery
  • DSL style API
    • For all API Groups/Versions
  • Generic client support
  • Swift-Logging support
  • Loading resources from external sources
    • from files
    • from URLs
  • Read Options
  • List Options
  • Delete Options
  • PATCH API
  • /scale API
  • /status API
  • Resource watch support
  • Follow pod logs support
  • Discovery API
  • CRD support
  • Controller/Informer support
  • Swift Metrics
  • Complete documentation
  • End-to-end tests

Compatibility Matrix

1.28.01.28.31.29.61.32.01.32.21.33.31.34.6
0.16.x------
0.17.x------
0.18.x------
0.19.x-0.23.0------
0.24.0------
0.25.0------
0.26.0------
  • 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
letclient=tryKubernetesClient()

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.shutdown() 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
tryawait client.shutdown()
// DispatchQueue
letqueue:DispatchQueue=...
client.shutdown(queue: queue){(error:Error?)inprint(error)}

Configuring the client

The client tries to resolve a kube config automatically from different sources in the following order:

  • Kube config file at path of environment variable KUBECONFIG, if defined
  • 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.

However, KubeConfig can also be loaded manually:

letkubeConfig=tryKubeConfig.from(config:"<config as a YAML string>")letkubeConfig=tryKubeConfig.from(url:"<some URL>")letkubeConfig=tryKubeConfig.fromEnvironment(envVar:"KUBECONFIG")letkubeConfig=tryKubeConfig.fromDefaultLocalConfig()letkubeConfig=tryKubeConfig.fromServiceAccount()

and then used to initialize the KubernetesClientConfig like this:

letkubeConfig=KubeConfig.fromDefaultLocalConfig()letconfig=KubernetesClientConfig.from(
kubeConfig: kubeConfig,
contextName:"some-context" // if not provided, then "current-context" is used
)

Alternatively, the KubernetesClientConfig can be configured completely manually, for example:

letcaCert=tryNIOSSLCertificate.fromPEMFile(caFile)letauthentication=KubernetesClientAuthentication.basicAuth(
username:"admin", password:"admin")letconfig=KubernetesClientConfig(
masterURL:"https://kubernetesmaster",
namespace:"default",
authentication: authentication,
trustRoots:NIOSSLTrustRoots.certificates(caCert),
insecureSkipTLSVerify:false,
timeout:HTTPClient.Configuration.Timeout.init(connect:.seconds(1), read:.seconds(10)),
redirectConfiguration:HTTPClient.Configuration.RedirectConfiguration.follow(max:5, allowCycles:false))letclient=KubernetesClient(config: config)

Client authentication

The following authentication schemes are supported:

  • Basic Auth: .basicAuth(username: String, password: String)
  • Bearer Token: .bearer(token: String)
  • Client certificate: .x509(clientCertificate: NIOSSLCertificate, clientKey: NIOSSLPrivateKey)

Client DSL

SwiftkubeClient defines 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

letnamespaces=tryawait client.namespaces.list()letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces)letroles=tryawait client.rbacV1.roles.list(in:.namespace("ns"))

You can filter the listed resources or limit the returned list size via the ListOptions:

letdeployments=tryawait client.appsV1.deployments.list(in:.allNamespaces, options:[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.notIn(["env":["dev","staging"]])),.labelSelector(.exists(["app","env"])),.fieldSelector(.eq(["status.phase":"Running"])),.resourceVersion("9001"),.limit(20),.timeoutSeconds(10)])

Get a resource

letnamespace=tryawait client.namespaces.get(name:"ns")letdeployment=tryawait client.appsV1.deployments.get(in:.namespace("ns"), name:"nginx")letroles=tryawait client.rbacV1.roles.get(in:.namespace("ns"), name:"role")

You can also provide the following ReadOptions:

letdeployments=tryawait client.appsV1.deployments.get(in:.allNamespaces, options:[.pretty(true),.exact(false),.export(true)])

Delete a resource

tryawait client.namespaces.delete(name:"ns")tryawait client.appsV1.deployments.delete(in:.namespace("ns"), name:"nginx")tryawait client.rbacV1.roles.delete(in:.namespace("ns"), name:"role")

You can pass an instance of meta.v1.DeleteOptions to control the behaviour of the delete operation:

letdeletOptions= meta.v1.DeleteOptions(
gracePeriodSeconds:10,
propagationPolicy:"Foreground")tryawait client.pods.delete(in:.namespace("ns"), name:"nginx", options: deleteOptions)

Create and update a resource

Resources can be created/updated directly or via the convenience builders defined in SwiftkubeModel

// Create a resource instance and post it
letconfigMap= core.v1.ConfigMap(
metadata: meta.v1.ObjectMeta(name:"test"),
data:["foo":"bar"])try cm =tryawait client.configMaps.create(inNamespace:.default, configMap)
// Or inline via a builder
letpod=tryawait 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.

lettask:SwiftkubeClientTask=tryawait client.pods.watch(in:.allNamespaces)letstream=await task.start()fortryawaiteventin stream {print(event)}

You can also pass ListOptions to filter, i.e. select the required objects:

letoptions=[.labelSelector(.eq(["app":"nginx"])),.labelSelector(.exists(["env"]))]lettask=tryawait 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.

letstrategy=RetryStrategy(
policy:.maxAttemtps(20),
backoff:.exponentiaBackoff(maxDelay:60, multiplier:2.0),
initialDelay =5.0,
jitter =0.2)lettask=tryawait client.pods.watch(in:.default, retryStrategy: strategy)fortryawaiteventinawait 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.

lettask=tryawait client.pods.follow(in:.default, name:"nginx", container:"app")fortryawaitlineinawait 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.

letversion:Info=tryawait client.discovery.serverVersion()letgroups:meta.v1.APIGroupList=tryawait client.discovery.serverGroups()letresources:meta.v1.APIResourceList=tryawait 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
leturl=URL(fileURLWithPath:"/path/to/manifest.yaml")letdeployment=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:

guardlet gvr =try?GroupVersionResource(for:"deployment")else{
// handle this
}
// Get by name
letresource:UnstructuredResource=tryawait client.for(gvr: gvr).get(in:.default , name:"nginx")
// List all
letresources:UnstructuredResourceList=tryawait 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
letdeployment=..
let gvk =GroupVersionKind(of: deployment)letgvr=GroupVersionResource(of: deployment)letgvk=GroupVersionKind(of: apps.v1.Deployment.self)letgvr=GroupVersionResource(for:"configmaps")letgvk=GroupVersionKind(for:"cm")letgvr=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:

letcrd= apiextensions.v1.CustomResourceDefinition.load(contentsOf:URL(filePath:"/path/to/crd.yaml"))tryawait 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 implements 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:

apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:
name: crontabs.example.comspec:
group: example.comnames:
plural: crontabssingular: crontabkind: CronTabshortNames:
- ctscope: Namespacedversions:
- name: v1served: truestorage: trueschema:
openAPIV3Schema:
type: objectproperties:
spec:
type: objectproperties:
cronSpec:
type: stringimage:
type: stringreplicas:
type: integer

The marker protocols are:

  • 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 state field (w/o assuming its type)

The following custom structs can be defined:

structCronTab:KubernetesAPIResource,NamespacedResource,MetadataHavingResource,ReadableResource,CreatableResource,ListableResource{typealiasList=CronTabListvarapiVersion="example.com/v1"varkind="CronTab"varmetadata:meta.v1.ObjectMeta?varspec:CronTabSpec}structCronTabSpec:Codable,Hashable,Sendable{varcronSpec:Stringvarimage:Stringvarreplicas:Int}structCronTabList:KubernetesResourceList{varapiVersion="example.com/v1"varkind="crontabs"varitems:[CronTab]}

Now, the new Custom Resource can be used like any other Kubernetes resource:

letgvr=GroupVersionResource(
group:"example.com",
version:"v1",
resource:"crontabs")letcronTabClient= client.for(CronTab.self, gvr: gvr)letcronTab=CronTab(
metadata: meta.v1.ObjectMeta(name:"new-cron"),
spec:CronTabSpec(
cronSpec :"* * * * */5",
image:"some-cron-image",
replicas:2))letnew=tryawait cronTabClient.create(in:.default, cronTab)letcronTabs:CronTabList=tryawait 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
letprom=PrometheusClient()MetricsSystem.bootstrap(prom)

and expose a /metrics endpoint for scraping:

// if using vapor
app.get("metrics"){ request ->EventLoopFuture<String>inletpromise= request.eventLoop.makePromise(of:String.self)tryMetricsSystem.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:

.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")

then include it as a dependency in your target:

import PackageDescription
letpackage=Package(
// ...
dependencies:[.package(name:"SwiftkubeClient", url:"https://github.com/swiftkube/client.git", from:"0.23.0")],
targets:[.target(name:"<your-target>", dependencies:[.product(name:"SwiftkubeClient",package:"SwiftkubeClient"),])])

Then run swift build.

License

Swiftkube project is licensed under version 2.0 of the Apache License. See LICENSE for more details.

Releases

Packages

Contributors

Languages