Skip to content

Commit

Permalink
feat(kube): begin implementation of api discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
HoKim98 committed Jul 22, 2024
1 parent aa9c52d commit 68ac378
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 73 deletions.
67 changes: 64 additions & 3 deletions crates/cassette-plugin-kubernetes-core/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::marker::PhantomData;
use std::{marker::PhantomData, rc::Rc};

use anyhow::Result;
use kube_core::{params::ListParams, ObjectList, Request};
Expand All @@ -16,7 +16,68 @@ pub struct Api<K> {
}

impl<K> Api<K> {
pub(crate) async fn list(self, lp: ListParams) -> Result<ObjectList<K>>
pub(crate) async fn find(api_version: String, kind: String) -> Result<Self> {
let (api_group, version) = match api_version.split_once('/') {
Some(("", "")) => (None, None),
Some((api_group, "")) => (Some(api_group), None),
Some(("", version)) => (None, Some(version)),
Some((api_group, version)) => (Some(api_group), Some(version)),
None => {
if api_version.is_empty() {
(None, None)
} else {
(None, Some(api_version.as_str()))
}
}
};

// TODO: to be implemented (server-side)
let _ = kind;

Ok(Self {
api_group: api_group.map(|s| s.into()),
namespace: Some("default".into()),
plural: "deployments".into(),
version: version.unwrap_or("v1").into(),
_type: PhantomData,
})

// let url_path = match api_group {
// Some(api_group) => format!("/apis/"),
// };

// // Discover most stable version variant of document
// let apigroup = discovery::group(&kube, api_group).await?;
// let (ar, caps) = match match version {
// Some(version) => apigroup.versioned_resources(version),
// None => apigroup.recommended_resources(),
// }
// .into_iter()
// .find(|(ar, _)| ar.kind == kind)
// {
// Some((ar, caps)) => (ar, caps),
// None => bail!(
// "Cannot find resource: {kind}.{api_group}/{version}",
// api_group = if api_group.is_empty() {
// "core"
// } else {
// api_group
// },
// version = version.unwrap_or("auto"),
// ),
// };

// // Use the discovered kind in an Api, and Controller with the ApiResource as its DynamicType
// Ok(match caps.scope {
// Scope::Cluster => Api::all_with(kube, &ar),
// Scope::Namespaced => match namespace {
// Some(namespace) => Api::namespaced_with(kube, namespace, &ar),
// None => Api::default_namespaced_with(kube, &ar),
// },
// });
}

pub(crate) async fn list(self: Rc<Self>, lp: ListParams) -> Result<ObjectList<K>>
where
K: Clone + DeserializeOwned,
{
Expand All @@ -26,7 +87,7 @@ impl<K> Api<K> {
plural,
version,
_type: PhantomData,
} = self;
} = &*self;

let url_path = match (api_group, namespace) {
(None, None) => format!("/api/{version}/{plural}"),
Expand Down
21 changes: 20 additions & 1 deletion crates/cassette-plugin-kubernetes-core/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,28 @@ use yew::platform::spawn_local;

use crate::api::Api;

pub fn use_kubernetes_api<K>(
ctx: &mut CassetteContext,
api_version: String,
kind: String,
) -> CassetteTaskHandle<FetchState<Api<K>>>
where
K: 'static + Clone + DeserializeOwned,
{
let handler_name = "kubernetes api";
let force_init = false;
let state = ctx.use_state(handler_name, force_init, || FetchState::Pending);
{
let state = state.clone();
let f = move || Api::find(api_version, kind);
try_fetch(state, f);
}
state
}

pub fn use_kubernetes_list<K>(
ctx: &mut CassetteContext,
api: Api<K>,
api: Rc<Api<K>>,
lp: ListParams,
) -> CassetteTaskHandle<FetchState<ObjectList<K>>>
where
Expand Down
57 changes: 0 additions & 57 deletions crates/cassette-plugin-kubernetes-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,3 @@
pub mod api;
pub mod client;
pub mod hooks;

// use anyhow::{anyhow, bail, Result};
// use inflector::Inflector;
// use kube::{
// api::DynamicObject,
// discovery::{self, ApiGroup, Scope},
// Api, Client,
// };
// use tracing::{instrument, Level};

// pub async fn init_client() -> Result<Client> {
// Client::try_default()
// .await
// .map_err(|error| anyhow!("failed to init kubernetes client: {error}"))
// }

// #[instrument(level = Level::INFO, skip(kube))]
// pub async fn get_api(
// kube: Client,
// namespace: Option<&str>,
// api_group: Option<&str>,
// version: Option<&str>,
// kind: &str,
// ) -> Result<Api<DynamicObject>> {
// let api_group = api_group.unwrap_or(ApiGroup::CORE_GROUP);
// let kind_pascal = kind.to_pascal_case();

// // Discover most stable version variant of document
// let apigroup = discovery::group(&kube, api_group).await?;
// let (ar, caps) = match match version {
// Some(version) => apigroup.versioned_resources(version),
// None => apigroup.recommended_resources(),
// }
// .into_iter()
// .find(|(ar, _)| ar.kind == kind_pascal)
// {
// Some((ar, caps)) => (ar, caps),
// None => bail!(
// "Cannot find resource: {kind}.{api_group}/{version}",
// api_group = if api_group.is_empty() {
// "core"
// } else {
// api_group
// },
// version = version.unwrap_or("auto"),
// ),
// };

// // Use the discovered kind in an Api, and Controller with the ApiResource as its DynamicType
// Ok(match caps.scope {
// Scope::Cluster => Api::all_with(kube, &ar),
// Scope::Namespaced => match namespace {
// Some(namespace) => Api::namespaced_with(kube, namespace, &ar),
// None => Api::default_namespaced_with(kube, &ar),
// },
// })
// }
27 changes: 15 additions & 12 deletions crates/cassette-plugin-kubernetes-list/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::marker::PhantomData;

use cassette_core::{
cassette::{CassetteContext, GenericCassetteTaskHandle},
components::ComponentRenderer,
net::fetch::FetchState,
prelude::*,
task::{TaskResult, TaskState},
};
use cassette_plugin_kubernetes_core::{api::Api, hooks::use_kubernetes_list};
use cassette_plugin_kubernetes_core::hooks::{use_kubernetes_api, use_kubernetes_list};
use kube_core::{params::ListParams, DynamicObject};
use serde::{Deserialize, Serialize};
use yew::prelude::*;
Expand Down Expand Up @@ -42,15 +40,20 @@ impl ComponentRenderer<Spec> for State {
fn render(self, ctx: &mut CassetteContext, spec: Spec) -> TaskResult<Option<Self>> {
let Spec { api_version, kind } = spec;

// TODO: to be implemented
let _ = (api_version, kind);

let api: Api<DynamicObject> = Api {
api_group: Some("apps".into()),
namespace: Some("default".into()),
plural: "deployments".into(),
version: "v1".into(),
_type: PhantomData,
let api = match use_kubernetes_api(ctx, api_version, kind).get() {
FetchState::Pending | FetchState::Fetching => {
return Ok(TaskState::Break {
body: html! { <Loading /> },
state: None,
})
}
FetchState::Collecting(api) | FetchState::Completed(api) => api.clone(),
FetchState::Error(msg) => {
return Ok(TaskState::Break {
body: html! { <Error msg={ msg.clone() } /> },
state: None,
})
}
};
let lp = ListParams::default();

Expand Down

0 comments on commit 68ac378

Please sign in to comment.