Skip to content

bartvanbenthem/koprs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

169 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kubernetes Operators in Rust

koprs is a high-level Kubernetes operator library for Rust. koprs-external bridges HTTP endpoints and object stores (S3) into the same channel-based event model. 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 and 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 Core Kubernetes operator framework docs.rs crates.io
koprs-external HTTP and object-store polling watchers docs.rs crates.io
koprs-admission Validating admission webhook server docs.rs crates.io

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. Start there.

For working end-to-end examples, see:

  • configmapsync — a single CRD reconciled by one controller; the best starting point.
  • multicontroller — 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, and a ControllerBuilder that wires it all together.

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<Greeting> for GreetingReconciler {
    type Error = KubeGenericError;

    async fn reconcile(&self, cr: Arc<Greeting>, ctx: Arc<Context>) -> Result<Action, Self::Error> {
        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::<Greeting, GreetingStatus>(
            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<Greeting> = 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, 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.

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 recommended)

Build

# build all crates
cargo build

# build a specific crate
cargo build -p koprs

Test

# 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

CI

cargo-ci.sh runs all quality checks in sequence — format, type-check, unit tests, integration tests, coverage, release build, docs, and audit.

./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.

./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 for the full list of flags.

License

MIT

About

A collection of reusable Rust libraries for Kubernetes operator development, enabling operators built with significantly less code.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors