Skip to content

Commit

Permalink
generalize path resolution, unfinished
Browse files Browse the repository at this point in the history
  • Loading branch information
Gottox committed Oct 26, 2024
1 parent e852870 commit 50b153c
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 56 deletions.
48 changes: 24 additions & 24 deletions src/bin/rustcloak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ async fn main() -> Result<()> {
);
}
if opts.controllers.contains(&ControllerOpt::Realm) {
controllers.push(
ControllerRunner::new(
MorphController::<KeycloakRealm>::default(),
&client,
)
.run()
.boxed(),
);
//controllers.push(
// ControllerRunner::new(
// MorphController::<KeycloakRealm>::default(),
// &client,
// )
// .run()
// .boxed(),
//);
}
if opts.controllers.contains(&ControllerOpt::Client) {
controllers.push(
Expand Down Expand Up @@ -102,14 +102,14 @@ async fn main() -> Result<()> {
);
}
if opts.controllers.contains(&ControllerOpt::ClientScope) {
controllers.push(
ControllerRunner::new(
MorphController::<KeycloakClientScope>::default(),
&client,
)
.run()
.boxed(),
);
//controllers.push(
// ControllerRunner::new(
// MorphController::<KeycloakClientScope>::default(),
// &client,
// )
// .run()
// .boxed(),
//);
}
if opts.controllers.contains(&ControllerOpt::Component) {
controllers.push(
Expand Down Expand Up @@ -165,14 +165,14 @@ async fn main() -> Result<()> {
);
}
if opts.controllers.contains(&ControllerOpt::ProtocolMapper) {
controllers.push(
ControllerRunner::new(
MorphController::<KeycloakProtocolMapper>::default(),
&client,
)
.run()
.boxed(),
);
//controllers.push(
// ControllerRunner::new(
// MorphController::<KeycloakProtocolMapper>::default(),
// &client,
// )
// .run()
// .boxed(),
//);
}
if opts
.controllers
Expand Down
27 changes: 9 additions & 18 deletions src/controller/client_secret_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ use std::{sync::Arc, time::Duration};

use crate::{
app_id,
crd::{
HasEndpoint, KeycloakApiStatus, KeycloakClient, KeycloakInstance,
KeycloakRealm,
},
crd::{KeycloakApiStatus, KeycloakClient, KeycloakInstance},
endpoint::{Hierarchy, HierarchyQuery},
error::Result,
util::K8sKeycloakBuilder,
};
Expand Down Expand Up @@ -89,21 +87,17 @@ impl KeycloakClientSecretController {
resource: Arc<KeycloakClient>,
ctx: Arc<Self>,
) -> Result<Action> {
let client = &ctx.client;
let ns = resource.namespace().ok_or(Error::NoNamespace)?;
let secret_ref =
resource.spec.client_secret.clone().unwrap_or_default();

let ns = resource.namespace().ok_or(Error::NoNamespace)?;

let client = &ctx.client;
let hierarchy =
Hierarchy::<KeycloakClient>::query(&resource, client.clone())
.await?;
let secret_api: Api<Secret> = Api::namespaced(client.clone(), &ns);
let realm_api: Api<KeycloakRealm> =
Api::namespaced(client.clone(), &ns);
let instance_api: Api<KeycloakInstance> =
Api::namespaced(client.clone(), &ns);

let realm = realm_api.get(&resource.spec.realm_ref).await?;
let instance = instance_api.get(&realm.spec.instance_ref).await?;

let client_id_key = secret_ref
.client_id_key
.clone()
Expand All @@ -112,12 +106,9 @@ impl KeycloakClientSecretController {
.client_secret_key
.clone()
.unwrap_or("client_secret".to_string());
let instance = instance_api.get(hierarchy.instance_ref()).await?;

let path = format!(
"admin/realms/{}/clients/{}/client-secret",
realm.primary_key_value(),
resource.primary_key_value()
);
let path = format!("{}/client-secret", hierarchy.path());

let keycloak = K8sKeycloakBuilder::new(&instance, client)
.with_token()
Expand Down
37 changes: 30 additions & 7 deletions src/controller/morph_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ use std::sync::Arc;
use super::controller_runner::LifecycleController;
use crate::{
app_id,
crd::{HasEndpoint, KeycloakApiObject, KeycloakApiObjectSpec},
endpoint::Resolver,
crd::{
ChildOf, HasEndpoint, HasParentType, KeycloakApiEndpoint,
KeycloakApiObject, KeycloakApiObjectSpec,
},
endpoint::{HasHierarchy, Hierarchy, HierarchyQuery},
error::{Error, Result},
morph::Morph,
};
use async_trait::async_trait;
use k8s_openapi::NamespaceResourceScope;
use kube::{
api::{ObjectMeta, Patch, PatchParams},
runtime::{controller::Action, watcher, Controller},
Expand All @@ -19,11 +23,11 @@ use serde::{de::DeserializeOwned, Serialize};
use serde_json::json;

#[derive(Debug)]
pub struct MorphController<T: Resolver> {
pub struct MorphController<T: HasParentType> {
phantom: std::marker::PhantomData<T>,
}

impl<T: Resolver> Default for MorphController<T> {
impl<T: HasParentType> Default for MorphController<T> {
fn default() -> Self {
Self {
phantom: std::marker::PhantomData,
Expand All @@ -34,17 +38,29 @@ impl<T: Resolver> Default for MorphController<T> {
#[async_trait]
impl<R> LifecycleController for MorphController<R>
where
R: Resolver
R: HasParentType
+ Morph
+ HasHierarchy
+ HasEndpoint
+ Resource<DynamicType = ()>
+ HasParentType
+ ChildOf
+ Resource<DynamicType = (), Scope = NamespaceResourceScope>
+ Send
+ Sync
+ 'static
+ Clone
+ std::fmt::Debug
+ Serialize
+ DeserializeOwned,
R::Parent: Resource<DynamicType = (), Scope = NamespaceResourceScope>
+ Clone
+ std::fmt::Debug
+ DeserializeOwned
+ Send
+ Sync
+ 'static,
R::HierarchyParent: HierarchyQuery<Object = R::Parent> + Sync + Send,
R::ParentRefType: AsRef<str> + Sync + Send,
{
type Resource = R;

Expand Down Expand Up @@ -85,7 +101,14 @@ where
.into();
let payload = payload.into();

let endpoint = resource.resolve(client.clone()).await?;
let hierarchy =
Hierarchy::<Self::Resource>::query(&resource, client.clone())
.await?;
let endpoint = KeycloakApiEndpoint {
instance_ref: hierarchy.instance_ref().to_string().into(),
path: hierarchy.path().into(),
};
//let endpoint = resource.resolve(client.clone()).await?;
debug!("Resolved endpoint: {:?}", endpoint);

let api_object = KeycloakApiObject {
Expand Down
2 changes: 1 addition & 1 deletion src/crd/endpoint/client_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ endpoint_impl!(KeycloakClientScope, ClientScopeRepresentation, id, cs);

impl ChildOf for KeycloakClientScope {
type ParentRefType = String;
type Parent = KeycloakRealm;
type ParentType = KeycloakRealm;
fn sub_path(&self) -> &'static str {
if self.spec.is_template.unwrap_or(false) {
"client-scopes"
Expand Down
2 changes: 1 addition & 1 deletion src/crd/endpoint/protocol_mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct KeycloakProtocolMapperSpec {
endpoint_impl!(KeycloakProtocolMapper, ProtocolMapperRepresentation, id, pm);

impl ChildOf for KeycloakProtocolMapper {
type Parent = Either<KeycloakClient, KeycloakClientScope>;
type ParentType = Either<KeycloakClient, KeycloakClientScope>;
type ParentRefType = Either<String, String>;
fn sub_path(&self) -> &'static str {
"protocol-mappers/models"
Expand Down
11 changes: 9 additions & 2 deletions src/crd/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,22 @@ macro_rules! endpoint_impl {
}

pub trait ChildOf {
type Parent;
type ParentType;
type ParentRefType;
fn sub_path(&self) -> &'static str;
fn parent_ref(&self) -> Self::ParentRefType;
}

pub trait HasParentType {
type Parent;
}

#[macro_export]
macro_rules! child_of {
($name:ty, $parent:ty, $ref:ident, $sub_path:expr) => {
use kube::core::object::HasSpec;
impl $crate::crd::ChildOf for $name {
type Parent = $parent;
type ParentType = $parent;
type ParentRefType = String;
fn sub_path(&self) -> &'static str {
$sub_path
Expand All @@ -77,6 +81,9 @@ macro_rules! child_of {
self.spec().$ref.to_string()
}
}
impl $crate::crd::HasParentType for $name {
type Parent = $parent;
}
};
}

Expand Down
120 changes: 117 additions & 3 deletions src/endpoint/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{
crd::{
ChildOf, HasEndpoint, KeycloakApiEndpoint, KeycloakClient,
KeycloakClientScope, KeycloakProtocolMapper, KeycloakRealm,
ChildOf, HasEndpoint, HasParentType, KeycloakApiEndpoint,
KeycloakClient, KeycloakClientScope, KeycloakProtocolMapper,
KeycloakRealm,
},
error::{Error, Result},
};
Expand All @@ -10,6 +11,119 @@ use either::Either;
use k8s_openapi::NamespaceResourceScope;
use kube::{Api, Resource, ResourceExt};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;

pub trait HasHierarchy {
type HierarchyParent;
}

impl HasHierarchy for KeycloakRealm {
type HierarchyParent = ();
}

impl<P: HasHierarchy + Resource, T: HasParentType<Parent = P>> HasHierarchy
for T
{
type HierarchyParent = Hierarchy<P>;
}

pub struct Hierarchy<O>
where
O: HasHierarchy + Resource,
{
pub object: Arc<O>,
pub parent: O::HierarchyParent,
}

#[async_trait]
pub trait HierarchyQuery
where
Self: Sized + Send + Sync,
{
type Object;
fn path(&self) -> String;

fn instance_ref(&self) -> &str;

async fn query(
object: &Arc<Self::Object>,
client: kube::Client,
) -> Result<Self>;
}

#[async_trait]
impl HierarchyQuery for Hierarchy<KeycloakRealm> {
type Object = KeycloakRealm;

fn instance_ref(&self) -> &str {
&self.object.spec.instance_ref
}

fn path(&self) -> String {
format!("admin/realms/{}", self.object.primary_key_value())
}

async fn query(
object: &Arc<Self::Object>,
_client: kube::Client,
) -> Result<Self> {
Ok(Self {
object: object.clone(),
parent: (),
})
}
}

#[async_trait]
impl<O> HierarchyQuery for Hierarchy<O>
where
O: Resource<DynamicType = (), Scope = NamespaceResourceScope>
+ HasHierarchy
+ HasParentType
+ ChildOf
+ Clone
+ HasEndpoint
+ std::fmt::Debug
+ DeserializeOwned
+ Send
+ Sync,
O::ParentRefType: AsRef<str> + Send + Sync,
O::HierarchyParent: HierarchyQuery<Object = O::Parent> + Send + Sync,
O::Parent: Resource<DynamicType = (), Scope = NamespaceResourceScope>
+ Clone
+ std::fmt::Debug
+ DeserializeOwned
+ Send
+ Sync,
{
type Object = O;

fn path(&self) -> String {
format!(
"{}/{}/{}",
self.parent.path(),
self.object.sub_path(),
self.object.primary_key_value()
)
}

fn instance_ref(&self) -> &str {
self.parent.instance_ref()
}

async fn query(object: &Arc<O>, client: kube::Client) -> Result<Self> {
let client = client.clone();
let object = object.clone();
let ns = object.namespace().ok_or(Error::NoNamespace)?;
let parent_ref = object.parent_ref();
let parent = Api::<O::Parent>::namespaced(client.clone(), &ns)
.get(parent_ref.as_ref())
.await?;
let parent = Arc::new(parent);
let parent = O::HierarchyParent::query(&parent, client).await?;
Ok(Self { object, parent })
}
}

#[async_trait]
pub trait Resolver
Expand Down Expand Up @@ -44,7 +158,7 @@ where
+ Serialize,
T: Resource<DynamicType = (), Scope = NamespaceResourceScope>
+ HasEndpoint
+ ChildOf<Parent = P, ParentRefType = String>
+ ChildOf<ParentType = P, ParentRefType = String>
+ Send,
Self: Send + Sync,
{
Expand Down

0 comments on commit 50b153c

Please sign in to comment.