75 releases (breaking)
new 0.98.0 | Dec 23, 2024 |
---|---|
0.97.0 | Nov 20, 2024 |
0.96.0 | Oct 9, 2024 |
0.93.1 | Jul 23, 2024 |
0.38.0 | Jul 23, 2020 |
#968 in Network programming
750,475 downloads per month
Used in 119 crates
(18 directly)
715KB
12K
SLoC
kube-rs
A Rust client for Kubernetes in the style of a more generic client-go, a runtime abstraction inspired by controller-runtime, and a derive macro for CRDs inspired by kubebuilder. Hosted by CNCF as a Sandbox Project.
These crates build upon Kubernetes apimachinery api concepts to enable generic abstractions. These abstractions allow Rust reinterpretations of reflectors, controllers, and custom resource interfaces, so that you can write applications easily.
Installation
Select a version of kube
along with the generated k8s-openapi structs at your chosen Kubernetes version:
[dependencies]
kube = { version = "0.98.0", features = ["runtime", "derive"] }
k8s-openapi = { version = "0.24.0", features = ["latest"] }
See features for a quick overview of default-enabled / opt-in functionality.
Upgrading
See kube.rs/upgrading. Noteworthy changes are highlighted in releases, and archived in the changelog.
Usage
See the examples directory for how to use any of these crates.
Official examples:
- version-rs: lightweight deployment
reflector
using axum - controller-rs:
Controller
of a crd inside actix
For real world projects see ADOPTERS.
Api
The Api
is what interacts with Kubernetes resources, and is generic over Resource
:
use k8s_openapi::api::core::v1::Pod;
let pods: Api<Pod> = Api::default_namespaced(client);
let pod = pods.get("blog").await?;
println!("Got pod: {pod:?}");
let patch = json!({"spec": {
"activeDeadlineSeconds": 5
}});
let pp = PatchParams::apply("kube");
let patched = pods.patch("blog", &pp, &Patch::Apply(patch)).await?;
assert_eq!(patched.spec.active_deadline_seconds, Some(5));
pods.delete("blog", &DeleteParams::default()).await?;
See the examples ending in _api
examples for more detail.
Custom Resource Definitions
Working with custom resources uses automatic code-generation via proc_macros in kube-derive.
You need to add #[derive(CustomResource, JsonSchema)]
and some #[kube(attrs..)]
on a spec struct:
#[derive(CustomResource, Debug, Serialize, Deserialize, Default, Clone, JsonSchema)]
#[kube(group = "kube.rs", version = "v1", kind = "Document", namespaced)]
pub struct DocumentSpec {
title: String,
content: String,
}
Then you can use the generated wrapper struct Document
as a kube::Resource
:
let docs: Api<Document> = Api::default_namespaced(client);
let d = Document::new("guide", DocumentSpec::default());
println!("doc: {:?}", d);
println!("crd: {:?}", serde_yaml::to_string(&Document::crd()));
There are a ton of kubebuilder-like instructions that you can annotate with here. See the documentation or the crd_
prefixed examples for more.
NB: #[derive(CustomResource)]
requires the derive
feature enabled on kube
.
Runtime
The runtime
module exports the kube_runtime
crate and contains higher level abstractions on top of the Api
and Resource
types so that you don't have to do all the watch
/resourceVersion
/storage book-keeping yourself.
Watchers
A streaming interface (similar to informers) that presents watcher::Event
s and does automatic relists under the hood.
let api = Api::<Pod>::default_namespaced(client);
let stream = watcher(api, Config::default()).default_backoff().applied_objects();
This now gives a continual stream of events and you do not need to care about the watch having to restart, or connections dropping.
while let Some(event) = stream.try_next().await? {
println!("Applied: {}", event.name_any());
}
Note the base items from a watcher
stream are an abstraction above the native WatchEvent
to allow for store buffering. If you are following along to "see what changed", you can use utilities from WatchStreamExt
, such as applied_objects
to get a more conventional stream.
Reflectors
A reflector
is a watcher
with Store
on K
. It acts on all the Event<K>
exposed by watcher
to ensure that the state in the Store
is as accurate as possible.
let nodes: Api<Node> = Api::all(client);
let lp = Config::default().labels("kubernetes.io/arch=amd64");
let (reader, writer) = reflector::store();
let rf = reflector(writer, watcher(nodes, lp));
At this point you can listen to the reflector
as if it was a watcher
, but you can also query the reader
at any point.
Controllers
A Controller
is a reflector
along with an arbitrary number of watchers that schedule events internally to send events through a reconciler:
Controller::new(root_kind_api, Config::default())
.owns(child_kind_api, Config::default())
.run(reconcile, error_policy, context)
.for_each(|res| async move {
match res {
Ok(o) => info!("reconciled {:?}", o),
Err(e) => warn!("reconcile failed: {}", Report::from(e)),
}
})
.await;
Here reconcile
and error_policy
refer to functions you define. The first will be called when the root or child elements change, and the second when the reconciler
returns an Err
.
See the controller guide for how to write these.
TLS
By default rustls is used for TLS, but openssl
is supported. To switch, turn off default-features
, and enable the openssl-tls
feature:
[dependencies]
kube = { version = "0.98.0", default-features = false, features = ["client", "openssl-tls"] }
k8s-openapi = { version = "0.24.0", features = ["latest"] }
This will pull in openssl
and hyper-openssl
. If default-features
is left enabled, you will pull in two TLS stacks, and the default will remain as rustls
.
musl-libc
Kube will work with distroless, scratch, and alpine
(it's also possible to use alpine as a builder with some caveats).
License
Apache 2.0 licensed. See LICENSE for details.
Dependencies
~48–77MB
~1M SLoC