diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b384fa..ce4327e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## 0.12.0 + +### New + +- Update to Kubernetes model v1.24.8 +- Update dependencies + - Async HTTP Client 1.13.1 + - SwiftkubeModel 0.6.0 + - SwiftLog 1.4.4 + - SwiftMetrics 2.3.3 + - SwiftNIO 2.46.0 + - Yams 5.0.1 +- Update k3s docker image to k3s:v1.24.8-k3s1 +- Add configurable timeout and redirect config for the underlying HTTPClient by @octo47 +- Update documentation comments + +### Breaking Changes + +- Raise minimum supported Swift version to 5.5 +- Replace `EventLoops` with `async/await` style API + +## 0.11.0 + +### New + +- Update to Kubernetes model v1.22.7 +- Add option to retrieve logs once without watching / streaming (#14) by @thomashorrobin +- Add discovery API to load server resources +- Use SwiftkubeModel v0.5.0 +- Refactor client to use `GroupVersionResource` instead of `GroupVersionKind` for resource type resolution +- Support creating a parametrised generic client given a `GroupVersionResource` +- Make `GenericKubernetesClient` extensions public +- Update k3s docker image to k3s:v1.22.7-k3s1 + +### Fixes + +- Typo in property name for storage.v1 API Group (#11) by @portellaa +- Add explicit dependency on NIO (#12) by @t089 + ## 0.10.0 ### New diff --git a/README.md b/README.md index 793a05a..4bd7684 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -
+
-
-
-
-
+
+
+
+
@@ -33,9 +33,10 @@
## Overview
-Swift client for talking to a [Kubernetes](http://kubernetes.io/) cluster via a fluent DSL based on [SwiftNIO](https://github.com/apple/swift-nio) and the [AysncHTTPClient](https://github.com/swift-server/async-http-client).
+Swift client for talking to a [Kubernetes](http://kubernetes.io/) cluster via a fluent DSL based
+on [SwiftNIO](https://github.com/apple/swift-nio) and the [AysncHTTPClient](https://github.com/swift-server/async-http-client).
-- [x] Covers all Kubernetes API Groups in v1.22.7
+- [x] Covers all Kubernetes API Groups in v1.24.8
- [x] Automatic configuration discovery
- [x] DSL style API
- [x] For all API Groups/Versions
@@ -61,21 +62,24 @@ Swift client for talking to a [Kubernetes](http://kubernetes.io/) cluster via a
## Compatibility Matrix
-| | <1.18.9 | 1.18.9 - 1.18.13 | 1.19.8 | 1.20.9 | 1.22.7 |
-|------------------------|---------|------------------|--------|--------|--------|
-| SwiftkubeClient 0.6.x | - | ✓ | - | - | - |
-| SwiftkubeClient 0.7.x | - | - | ✓ | - | - |
-| SwiftkubeClient 0.8.x | - | - | ✓ | - | - |
-| SwiftkubeClient 0.9.x | - | - | ✓ | - | - |
-| SwiftkubeClient 0.10.x | - | - | - | ✓ | - |
-| SwiftkubeClient 0.11.x | - | - | - | - | ✓ |
+| | <1.18.9 | 1.18.9 - 1.18.13 | 1.19.8 | 1.20.9 | 1.22.7 | 1.24.8 |
+|------------------------|---------|------------------|--------|--------|--------|--------|
+| SwiftkubeClient 0.6.x | - | ✓ | - | - | - | - |
+| SwiftkubeClient 0.7.x | - | - | ✓ | - | - | - |
+| SwiftkubeClient 0.8.x | - | - | ✓ | - | - | - |
+| SwiftkubeClient 0.9.x | - | - | ✓ | - | - | - |
+| SwiftkubeClient 0.10.x | - | - | - | ✓ | - | - |
+| SwiftkubeClient 0.11.x | - | - | - | - | ✓ | - |
+| SwiftkubeClient 0.12.x | - | - | - | - | - | ✓ |
- `✓` Exact match of API objects in both client and the Kubernetes version.
-- `-` API objects mismatches either due to the removal of old API or the addition of new API. However, everything the client and Kubernetes have in common will work.
+- `-` 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](https://github.com/swiftkube/examples) repository.
+Concrete examples for using the `Swiftkube` tooling reside in the[Swiftkube:Examples](https://github.com/swiftkube/examples)
+repository.
## Usage
@@ -89,26 +93,31 @@ To create a client just import `SwiftkubeClient` and init an instance.
let client = try KubernetesClient()
```
-You should shut down the `KubernetesClient` instance, which in turn shuts down the underlying `HTTPClient`. Thus you shouldn't call `client.syncShutdown()` before all requests have finished. Alternatively, you can close the client asynchronously by providing a `DispatchQueue` for the completion callback.
+You should shut down the `KubernetesClient` instance when you're done using it, which in turn shuts down the underlying
+`HTTPClient`. Thus, you shouldn't call `client.syncShutdown()` before all requests have finished. You can also shut down
+the client asynchronously in an async/await context or by providing a `DispatchQueue` for the completion callback.
```swift
// when finished close the client
try client.syncShutdown()
-// or asynchronously
+// async/await
+try await client.shutdown()
+
+// DispatchQueue
let queue: DispatchQueue = ...
client.shutdown(queue: queue) { (error: Error?) in
print(error)
}
```
-
### Configuring the client
The client tries to resolve a `kube config` automatically from different sources in the following order:
- Kube config file in the user's `$HOME/.kube/config` directory
-- `ServiceAccount` token located at `/var/run/secrets/kubernetes.io/serviceaccount/token` and a mounted CA certificate, if it's running in Kubernetes.
+- `ServiceAccount` token located at `/var/run/secrets/kubernetes.io/serviceaccount/token` and a mounted CA certificate,
+- if it's running in Kubernetes.
Alternatively it can be configured manually, for example:
@@ -124,7 +133,9 @@ let config = KubernetesClientConfig(
namespace: "default",
authentication: authentication,
trustRoots: NIOSSLTrustRoots.certificates(caCert),
- insecureSkipTLSVerify: false
+ insecureSkipTLSVerify: false,
+ timeout: HTTPClient.Configuration.Timeout.init(connect: .seconds(1), read: .seconds(10)),
+ redirectConfiguration: HTTPClient.Configuration.RedirectConfiguration.follow(max: 5, allowCycles: false)
)
let client = KubernetesClient(config: config)
@@ -142,22 +153,20 @@ The following authentication schemes are supported:
`SwiftkubeClient` defines convenience API to work with Kubernetes resources. Using this DSL is the same for all resources.
-> The examples use the blocking `wait()` for brevity. API calls return `EventLoopFutures` that can be composed and acted upon in an asynchronous way.
+The client exposes asynchronous functions using the new Swift concurrency model.
-> Currently only a subset of all API groups are accessible via the DSL. See [Advanced usage](#advanced-usage) for mor details.
-
#### List resources
```swift
-let namespaces = try client.namespaces.list().wait()
-let deployments = try client.appsV1.deployments.list(in: .allNamespaces).wait()
-let roles = try client.rbacV1.roles.list(in: .namespace("ns")).wait()
+let namespaces = try await client.namespaces.list()
+let deployments = try await client.appsV1.deployments.list(in: .allNamespaces)
+let roles = try await client.rbacV1.roles.list(in: .namespace("ns"))
```
You can filter the listed resources or limit the returned list size via the `ListOptions`:
```swift
-let deployments = try client.appsV1.deployments.list(in: .allNamespaces, options: [
+let deployments = try await client.appsV1.deployments.list(in: .allNamespaces, options: [
.labelSelector(.eq(["app": "nginx"])),
.labelSelector(.notIn(["env": ["dev", "staging"]])),
.labelSelector(.exists(["app", "env"])),
@@ -165,33 +174,33 @@ let deployments = try client.appsV1.deployments.list(in: .allNamespaces, options
.resourceVersion("9001"),
.limit(20),
.timeoutSeconds(10)
-]).wait()
+])
```
#### Get a resource
```swift
-let namespace = try client.namespaces.get(name: "ns").wait()
-let deployment = try client.appsV1.deployments.get(in: .namespace("ns"), name: "nginx").wait()
-let roles = try client.rbacV1.roles.get(in: .namespace("ns"), name: "role").wait()
+let namespace = try await client.namespaces.get(name: "ns")
+let deployment = try await client.appsV1.deployments.get(in: .namespace("ns"), name: "nginx")
+let roles = try await client.rbacV1.roles.get(in: .namespace("ns"), name: "role")
```
You can also provide the following `ReadOptions`:
```swift
-let deployments = try client.appsV1.deployments.get(in: .allNamespaces, options: [
+let deployments = try await client.appsV1.deployments.get(in: .allNamespaces, options: [
.pretty(true),
.exact(false),
.export(true)
-]).wait()
+])
```
#### Delete a resource
```swift
-try.client.namespaces.delete(name: "ns").wait()
-try client.appsV1.deployments.delete(in: .namespace("ns"), name: "nginx").wait()
-try client.rbacV1.roles.delete(in: .namespace("ns"), name: "role").wait()
+try await client.namespaces.delete(name: "ns")
+try await client.appsV1.deployments.delete(in: .namespace("ns"), name: "nginx")
+try await 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:
@@ -201,7 +210,7 @@ let deletOptions = meta.v1.DeleteOptions(
gracePeriodSeconds: 10,
propagationPolicy: "Foreground"
)
-try client.pods.delete(in: .namespace("ns"), name: "nginx", options: deleteOptions).wait()
+try await client.pods.delete(in: .namespace("ns"), name: "nginx", options: deleteOptions)
```
#### Create and update a resource
@@ -213,12 +222,11 @@ Resources can be created/updated directly or via the convenience builders define
let configMap = core.v1.ConfigMap(
metadata: meta.v1.Metadata(name: "test"),
data: ["foo": "bar"]
-}
-try cm = try client.configMaps.create(inNamespace: .default, configMap).wait()
-
+)
+try cm = try await client.configMaps.create(inNamespace: .default, configMap)
// Or inline via a builder
-let pod = try client.pods.create(inNamespace: .default) {
+let pod = try await client.pods.create(inNamespace: .default) {
sk.pod {
$0.metadata = sk.metadata(name: "nginx")
$0.spec = sk.podSpec {
@@ -230,14 +238,14 @@ let pod = try client.pods.create(inNamespace: .default) {
}
}
}
-.wait()
```
#### 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.
+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 can be cancelled any time to stop the watch.
@@ -262,9 +270,11 @@ let task = client.pods.watch(in: .default, options: options) { (event, pod) in
}
```
-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 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.
+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.
@@ -280,10 +290,11 @@ let task = client.pods.watch(in: .default, retryStrategy: strategy) { (event, po
}
```
-To handle events you can pass a `ResourceWatcherCallback.EventHandler` closure, which is used as a callback for new events. The clients sends each event paired with the corresponding resource as a pair to this `eventHandler`.
-
-If you require more control or stateful logic, then you can implement the `ResourceWatcherDelegate` protocol and pass it to the `watch` call:
+To handle events you can pass a `ResourceWatcherCallback.EventHandler` closure, which is used as a callback for new events.
+The client sends each event paired with the corresponding resource as a pair to this `eventHandler`.
+If you require more control or stateful logic, then you can implement the `ResourceWatcherDelegate` protocol and pass
+it to the `watch` call:
```swift
class MyDelegate: ResourceWatcherDelegate {
@@ -301,7 +312,6 @@ class MyDelegate: ResourceWatcherDelegate {
let task = client.pods.watch(in: .default, delegate: MyDelegate())
```
-
#### Follow logs
The `follow` API resembles the `watch`. The difference being the closure/delegate signature:
@@ -319,12 +329,13 @@ 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
+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.
```swift
-let version: ResourceOrStatus