https://github.com/bartvanbenthem/koprs
A collection of reusable Rust libraries for Kubernetes operator development, enabling operators built with significantly less code.
https://github.com/bartvanbenthem/koprs
controllers kube-rs kubernetes operator-sdk operators rust rust-lang
Last synced: 3 days ago
JSON representation
A collection of reusable Rust libraries for Kubernetes operator development, enabling operators built with significantly less code.
- Host: GitHub
- URL: https://github.com/bartvanbenthem/koprs
- Owner: bartvanbenthem
- Created: 2026-05-17T11:10:29.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-12T10:51:54.000Z (17 days ago)
- Last Synced: 2026-06-12T11:11:25.124Z (17 days ago)
- Topics: controllers, kube-rs, kubernetes, operator-sdk, operators, rust, rust-lang
- Language: Rust
- Homepage: https://docs.rs/crate/koprs/latest
- Size: 322 KB
- Stars: 16
- Watchers: 0
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Kubernetes Operators in Rust
[`koprs`](./crates/koprs) is a high-level Kubernetes operator library for Rust. [`koprs-external`](./crates/koprs-external) bridges HTTP endpoints and object stores (S3) into the same channel-based event model. [`koprs-admission`](./crates/koprs-admission) provides a typed validating admission webhook server.
Operators simplify managing complex stateful applications on Kubernetes, but writing them remains difficult: low-level APIs, boilerplate, and poor modularity all add friction. Koprs is a Rust framework built on [`kube`](https://github.com/kube-rs/kube) and [`kube-runtime`](https://crates.io/crates/kube-runtime) that addresses this with high-level abstractions and extensions for common Operator use cases.
This repository contains the core framework, external polling helpers, admission webhook support, and example operators.
## Crates
| Crate | Description | Docs |
|-------|-------------|------|
| [`koprs`](./crates/koprs) | Core Kubernetes operator framework | [](https://docs.rs/koprs) [](https://crates.io/crates/koprs) |
| [`koprs-external`](./crates/koprs-external) | HTTP and object-store polling watchers | [](https://docs.rs/koprs-external) [](https://crates.io/crates/koprs-external) |
| [`koprs-admission`](./crates/koprs-admission) | Validating admission webhook server | [](https://docs.rs/koprs-admission) [](https://crates.io/crates/koprs-admission) |
## Workspace layout
```
koprs/
├── Cargo.toml # workspace manifest
├── Cargo.lock
├── crates/
│ ├── koprs/ # core Kubernetes operator framework
│ ├── koprs-external/ # HTTP and object-store polling watchers
│ └── koprs-admission/ # validating admission webhook server
└── examples/
├── configmapsync/ # single CRD, single controller
└── multicontroller/ # multiple CRDs, multiple controllers in one operator
```
## Getting started
If you are here to build a Kubernetes operator, you want [`koprs`](./crates/koprs). Start there.
For working end-to-end examples, see:
* [configmapsync](./examples/configmapsync/README.md) — a single CRD reconciled by one controller; the best starting point.
* [multicontroller](./examples/multicontroller/README.md) — multiple CRDs (`SecretSync`, `ServiceAccountSync`) each reconciled by its own controller, run side by side in one operator binary.
### Minimal example
A `koprs` operator boils down to three pieces: a CRD type, a [`Reconciler`](./crates/koprs/src/controller.rs),
and a [`ControllerBuilder`](./crates/koprs/src/controller.rs) that wires it all together.
```rust,no_run
use std::sync::Arc;
use std::time::Duration;
use kube::{Api, Client, CustomResource, ResourceExt};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use koprs::controller::{Action, Context, ControllerBuilder, Reconciler};
use koprs::error::KubeGenericError;
use koprs::status::patch_status_namespaced;
/// The `Greeting` CRD — `kube::CustomResource` derives the type, its CRD spec,
/// and the generated `Greeting` struct (spec + status + metadata) in one go.
#[derive(CustomResource, Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
#[kube(
group = "example.io",
version = "v1alpha1",
kind = "Greeting",
namespaced,
status = "GreetingStatus"
)]
pub struct GreetingSpec {
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct GreetingStatus {
pub ready: bool,
}
struct GreetingReconciler;
impl Reconciler for GreetingReconciler {
type Error = KubeGenericError;
async fn reconcile(&self, cr: Arc, ctx: Arc) -> Result {
let name = cr.name_any();
let namespace = cr
.namespace()
.ok_or(KubeGenericError::MissingMetadata("namespace".into()))?;
// Mark the resource ready — replace with your own reconciliation logic.
patch_status_namespaced::(
ctx.client.clone(),
&namespace,
&name,
GreetingStatus { ready: true },
"greeting-operator",
)
.await?;
Ok(Action::requeue(Duration::from_secs(300)))
}
// error_policy defaults to requeue(30s) — override it for custom backoff
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::try_default().await?;
let api: Api = Api::all(client.clone());
let ctx = Context::new(client);
ControllerBuilder::new(api)
.health_port(8080)
.graceful_shutdown()
.run(GreetingReconciler, ctx)
.await?;
Ok(())
}
```
For finalizers, owned-resource reconciliation, garbage collection, events, and leader
election, see the [configmapsync operator](./examples/configmapsync/README.md), it
walks through the same building blocks in a complete, runnable operator. To see how to
run several CRDs and controllers from a single operator binary, see
[multicontroller](./examples/multicontroller/README.md).
## Contributing
Contributions are welcome. Please open an issue before submitting a pull request for
anything beyond small fixes, so the approach can be agreed on first.
### Prerequisites
- Rust stable toolchain
- A local Kubernetes cluster for integration tests ([kind](https://kind.sigs.k8s.io/) recommended)
### Build
```bash
# build all crates
cargo build
# build a specific crate
cargo build -p koprs
```
### Test
```bash
# unit tests (no cluster required)
cargo test
# integration tests
kind create cluster --name koprs-test
cargo test --features integration --test integration
kind delete cluster --name koprs-test
```
### CI
[](https://github.com/bartvanbenthem/koprs/actions/workflows/ci.yml)
`cargo-ci.sh` runs all quality checks in sequence — format, type-check,
unit tests, integration tests, coverage, release build, docs, and audit.
```bash
./scripts/cargo-ci.sh # run all steps
./scripts/cargo-ci.sh --fast # fmt + check + unit tests only (no coverage)
./scripts/cargo-ci.sh --no-audit # skip cargo-audit
./scripts/cargo-ci.sh --no-integration # skip integration tests
./scripts/cargo-ci.sh --no-doc # skip cargo doc
./scripts/cargo-ci.sh --no-coverage # skip llvm-cov coverage report
./scripts/cargo-ci.sh --bench # also compile benchmarks (slow, opt-in)
./scripts/cargo-ci.sh --coverage-fail-under=80 # fail if line coverage drops below N%
```
### Publishing
`publish.sh` handles the full pre-flight and publishes the crate to crates.io.
```bash
./scripts/publish.sh # full pre-flight + publish
./scripts/publish.sh --dry-run # stop before cargo publish
./scripts/publish.sh --skip-ci # skip CI checks, publish only
./scripts/publish.sh --crate koprs # publish a single crate
```
See the [CI script docs](./scripts/cargo-ci.sh) for the full list of flags.
## License
MIT