mod group; mod service_user; mod user_attribute; use core::fmt; use std::sync::Arc; use k8s_openapi::{ClusterResourceScope, NamespaceResourceScope}; use kube::runtime::controller::Action; use kube::runtime::finalizer; use kube::{Api, Resource, ResourceExt}; use serde::Serialize; use serde::de::DeserializeOwned; use tracing::{debug, instrument}; pub use self::group::Group; pub use self::service_user::ServiceUser; pub use self::user_attribute::{Type as AttributeType, UserAttribute}; use crate::context::Context; use crate::lldap; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Failed to commit: {0}")] Commit(#[from] kube::api::entry::CommitError), #[error("Kube api error: {0}")] Kube(#[from] kube::Error), #[error("LLDAP error: {0}")] Lldap(#[from] lldap::Error), #[error("Finalizer error: {0}")] Finalizer(#[source] Box>), #[error("MissingObjectKey: {0}")] MissingObjectKey(&'static str), #[error("UserAttributeDesync: {0:?}")] UserAttributeDesync(Vec), } impl From> for Error { fn from(error: finalizer::Error) -> Self { Self::Finalizer(Box::new(error)) } } type Result = std::result::Result; trait Reconcile { async fn reconcile(self: Arc, ctx: Arc) -> Result; async fn cleanup(self: Arc, ctx: Arc) -> Result; } #[instrument(skip(obj, ctx))] pub async fn reconcile_namespaced(obj: Arc, ctx: Arc) -> Result where T: Resource + ResourceExt + Clone + Serialize + DeserializeOwned + fmt::Debug + Reconcile, ::DynamicType: Default, { debug!(name = obj.name_any(), "Reconcile"); let namespace = obj.namespace().expect("resource should be namespaced"); let api = Api::::namespaced(ctx.client.clone(), &namespace); Ok(finalizer(&api, &ctx.controller_name, obj, |event| async { match event { finalizer::Event::Apply(obj) => obj.reconcile(ctx.clone()).await, finalizer::Event::Cleanup(obj) => obj.cleanup(ctx.clone()).await, } }) .await?) } #[instrument(skip(obj, ctx))] pub async fn reconcile(obj: Arc, ctx: Arc) -> Result where T: Resource + ResourceExt + Clone + Serialize + DeserializeOwned + fmt::Debug + Reconcile, ::DynamicType: Default, { debug!(name = obj.name_any(), "Reconcile"); let api = Api::::all(ctx.client.clone()); Ok(finalizer(&api, &ctx.controller_name, obj, |event| async { match event { finalizer::Event::Apply(obj) => obj.reconcile(ctx.clone()).await, finalizer::Event::Cleanup(obj) => obj.cleanup(ctx.clone()).await, } }) .await?) }