use std::{future::Future, pin::Pin, sync::Arc, time::Duration};
use futures_util::{FutureExt, StreamExt};
use kube::{
Api, CustomResource, CustomResourceExt,
runtime::{Controller, finalizer::Event, watcher::Config},
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use staircase::{
controller::{BoxPlan, Immutable, Plan, Reconciler, RunContext, Step, StepOutcome, StepResult},
util::{reconcile_with_finalizer, requeue_on_error},
};
use thiserror::Error;
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug, JsonSchema)]
#[kube(
group = "example.com",
version = "v1",
kind = "Foo",
status = "FooStatus",
namespaced
)]
pub struct FooSpec {
info: String,
name: String,
replicas: i32,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)]
pub struct FooStatus {
pub observed: u64,
}
pub struct StepA {}
impl Step for StepA {
type Error = DummyError;
type InData = ();
type Mode = Immutable;
type OutData = ();
type Resource = Foo;
async fn run(
&self,
_context: &RunContext<Self::Resource>,
data: Self::InData,
) -> StepResult<Self::OutData, Self::Error> {
Ok(StepOutcome::NoModification { data })
}
}
#[allow(dead_code)]
struct CustomReconciler {
client: kube::Client,
}
#[derive(Error, Debug)]
pub enum DummyError {}
impl Reconciler for CustomReconciler {
type Error = DummyError;
type EvaluationOutcome = ();
type EvaluationResource = kube::runtime::finalizer::Event<Foo>;
type InitialData = ();
type Plan = BoxPlan<Self::Resource, Self::Error, Self::InitialData, ()>;
type Resource = Foo;
async fn evaluate_plan(&self, resource: Self::EvaluationResource) -> ((), (), Arc<Self::Resource>, Self::Plan) {
(
(),
(),
match resource {
Event::Apply(r) => r,
Event::Cleanup(r) => r,
},
Plan::from(StepA {}).boxed(),
)
}
async fn client(&self) -> Result<kube::Client, DummyError> { Ok(self.client.clone()) }
}
impl CustomReconciler {
pub async fn start() -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
let client = kube::Client::try_default()
.await
.expect("couldn't create kubernetes client");
staircase::util::update_crd(Foo::crd(), &client)
.await
.expect("coundn't update crd");
let foos = Api::<Foo>::all(client.clone());
let reconciler = Arc::new(Self { client });
let controller_stream = Controller::new(foos, Config::default()).run::<_, Self>(
reconcile_with_finalizer(|o, ()| o.action(Duration::from_secs(30))),
requeue_on_error::<_, _, _>(Duration::from_secs(5)),
reconciler,
);
controller_stream
.filter_map(|x| async move { std::result::Result::ok(x) })
.for_each(|_| futures_util::future::ready(()))
.boxed()
}
}
fn main() {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
let _cr = rt.block_on(CustomReconciler::start());
}