# kube-rs [![Crates.io](https://img.shields.io/crates/v/kube.svg)](https://crates.io/crates/kube) [![Rust 1.77](https://img.shields.io/badge/MSRV-1.77-dea584.svg)](https://github.com/rust-lang/rust/releases/tag/1.77.2) [![Tested against Kubernetes v1_26 and above](https://img.shields.io/badge/MK8SV-v1_26-326ce5.svg)](https://kube.rs/kubernetes-version) [![Best Practices](https://bestpractices.coreinfrastructure.org/projects/5413/badge)](https://bestpractices.coreinfrastructure.org/projects/5413) [![Discord chat](https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=plastic)](https://discord.gg/tokio) A [Rust](https://rust-lang.org/) client for [Kubernetes](http://kubernetes.io) in the style of a more generic [client-go](https://github.com/kubernetes/client-go), a runtime abstraction inspired by [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime), and a derive macro for [CRDs](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/) inspired by [kubebuilder](https://book.kubebuilder.io/reference/generating-crd.html). Hosted by [CNCF](https://cncf.io/) as a [Sandbox Project](https://www.cncf.io/sandbox-projects/). These crates build upon Kubernetes [apimachinery](https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/v1/types.go) + [api concepts](https://kubernetes.io/docs/reference/using-api/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](https://github.com/Arnavion/k8s-openapi) structs at your chosen [Kubernetes version](https://kube.rs/kubernetes-version/): ```toml [dependencies] kube = { version = "0.97.0", features = ["runtime", "derive"] } k8s-openapi = { version = "0.23.0", features = ["latest"] } ``` See [features](https://kube.rs/features/) for a quick overview of default-enabled / opt-in functionality. ## Upgrading See [kube.rs/upgrading](https://kube.rs/upgrading/). Noteworthy changes are highlighted in [releases](https://github.com/kube-rs/kube/releases), and archived in the [changelog](https://kube.rs/changelog/). ## Usage See the **[examples directory](https://github.com/kube-rs/kube/blob/main/examples)** for how to use any of these crates. - **[kube API Docs](https://docs.rs/kube/)** - **[kube.rs](https://kube.rs)** Official examples: - [version-rs](https://github.com/kube-rs/version-rs): lightweight deployment `reflector` using axum - [controller-rs](https://github.com/kube-rs/controller-rs): `Controller` of a crd inside actix For real world projects see [ADOPTERS](https://kube.rs/adopters/). ## Api The [`Api`](https://docs.rs/kube/latest/kube/struct.Api.html) is what interacts with Kubernetes resources, and is generic over [`Resource`](https://docs.rs/kube/latest/kube/trait.Resource.html): ```rust use k8s_openapi::api::core::v1::Pod; let pods: Api = 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](https://docs.rs/kube/latest/kube/derive.CustomResource.html). You need to add `#[derive(CustomResource, JsonSchema)]` and some `#[kube(attrs..)]` on a __spec__ struct: ```rust #[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`](https://docs.rs/kube/*/kube/trait.Resource.html): ```rust let docs: Api = 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](https://docs.rs/kube/latest/kube/derive.CustomResource.html) or the `crd_` prefixed [examples](https://github.com/kube-rs/kube/blob/main/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`](https://docs.rs/kube/latest/kube/runtime/watcher/enum.Event.html)s and does automatic relists under the hood. ```rust let api = Api::::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. ```rust 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`](https://docs.rs/kube/latest/kube/runtime/trait.WatchStreamExt.html), 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` exposed by `watcher` to ensure that the state in the `Store` is as accurate as possible. ```rust let nodes: Api = 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: ```rust 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](https://kube.rs/controllers/intro/) for how to write these. ## TLS By default [rustls](https://github.com/rustls/rustls) is used for TLS, but `openssl` is supported. To switch, turn off `default-features`, and enable the `openssl-tls` feature: ```toml [dependencies] kube = { version = "0.97.0", default-features = false, features = ["client", "openssl-tls"] } k8s-openapi = { version = "0.23.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](https://github.com/kube-rs/controller-rs/blob/main/Dockerfile), [scratch](https://github.com/constellation-rs/constellation/blob/27dc89d0d0e34896fd37d638692e7dfe60a904fc/Dockerfile), and `alpine` (it's also possible to use alpine as a builder [with some caveats](https://github.com/kube-rs/kube/issues/331#issuecomment-715962188)). ## License Apache 2.0 licensed. See LICENSE for details.