# 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<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
