# nocontrol A Rust library for building Kubernetes-style reconciliation controllers. Define your desired state with manifests and let nocontrol continuously reconcile them. ## Features - **Declarative manifests** - Define your desired state as typed specifications - **Automatic reconciliation** - Changes trigger reconciliation loops - **Configurable resync** - Periodic full reconciliation (default: 5 minutes) - **Lease-based coordination** - Safe for distributed deployments - **Requeue support** - Schedule future reconciliations with delays ## Quick Start Add to your `Cargo.toml`: ```toml [package] ... [dependencies] nocontrol = "0.0.1" ... ``` > **Note:** Requires Rust nightly (edition 2024). ```rust use nocontrol::{ ControlPlane, Operator, OperatorState, Specification, manifests::{Action, Manifest, ManifestMetadata, ManifestState}, }; use serde::{Deserialize, Serialize}; // 1. Define your specification #[derive(Clone, Serialize, Deserialize)] struct MySpec { replicas: u32, } impl Specification for MySpec { fn kind(&self) -> &'static str { "MyResource" } } // 2. Implement the Operator trait #[derive(Clone)] struct MyOperator; impl Operator for MyOperator { type Specifications = MySpec; async fn reconcile( &self, manifest: &mut ManifestState, ) -> anyhow::Result { println!("Reconciling: {}", manifest.manifest.name); // Your reconciliation logic here // ... // Requeue after 30 seconds Ok(Action::Requeue(std::time::Duration::from_secs(30))) } } // 3. Run the control plane #[tokio::main] async fn main() -> anyhow::Result<()> { let operator = OperatorState::new(MyOperator); let control_plane = ControlPlane::new(operator); // Add a manifest control_plane.add_manifest(Manifest { name: "my-resource".into(), metadata: ManifestMetadata {}, spec: MySpec { replicas: 3 }, }).await?; // Run the reconciliation loop control_plane.execute().await } ``` ## Configuration Configure the operator with `OperatorConfig`: ```rust use nocontrol::{OperatorConfig, OperatorState}; use std::time::Duration; let config = OperatorConfig { resync_interval: Duration::from_secs(10 * 60), // 10 minutes ..Default::default() }; let operator = OperatorState::new_with_config(MyOperator, config); ``` ## Actions Return from `reconcile()` to control scheduling: - `Action::None` - No follow-up reconciliation - `Action::Requeue(duration)` - Reconcile again after the specified delay ## License MIT