98
README.md
Normal file
98
README.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
```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<MySpec>,
|
||||||
|
) -> anyhow::Result<Action> {
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user