Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dyntype from ObjectRef #1053

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions examples/crd_reflector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,25 @@ async fn main() -> anyhow::Result<()> {
let lp = ListParams::default().timeout(20); // low timeout in this example
let rf = reflector(writer, watcher(foos, lp));

let mut rfa = rf.applied_objects().boxed();
// keep polling the reflector stream in a background task
tokio::spawn(async move {
loop {
// Periodically read our state
// while this runs you can kubectl apply -f crd-baz.yaml or crd-qux.yaml and see it works
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
let crds = reader.state().iter().map(|r| r.name_any()).collect::<Vec<_>>();
info!("Current crds: {:?}", crds);
match rfa.try_next().await {
Ok(Some(event)) => info!("saw {}", event.name_any()),
Ok(_) => {}
Err(e) => {
error!("Kubernetes stream failure: {}", e);
panic!("Kubernetes stream exited");
}
}
}
});
let mut rfa = rf.applied_objects().boxed();
while let Some(event) = rfa.try_next().await? {
info!("saw {}", event.name_any());

// Periodically dump state
loop {
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
let foos = reader.state().iter().map(|r| r.name_any()).collect::<Vec<_>>();
info!("Current {} foos: {:?}", foos.len(), foos);
}
Ok(())
}
4 changes: 4 additions & 0 deletions kube-core/src/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ impl Resource for DynamicObject {
fn meta_mut(&mut self) -> &mut ObjectMeta {
&mut self.metadata
}

fn typemeta() -> Option<TypeMeta> {
None
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion kube-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub mod gvk;
pub use gvk::{GroupVersion, GroupVersionKind, GroupVersionResource};

pub mod metadata;
pub use metadata::{ListMeta, ObjectMeta, TypeMeta};
pub use metadata::{Inspect, ListMeta, ObjectMeta, TypeMeta};

pub mod object;
pub use object::{NotUsed, Object, ObjectList};
Expand Down
129 changes: 125 additions & 4 deletions kube-core/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! Metadata structs used in traits, lists, and dynamic objects.
use std::borrow::Cow;

use crate::{ApiResource, DynamicObject, DynamicResourceScope, Object, Resource};
pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::{ListMeta, ObjectMeta};
use serde::{Deserialize, Serialize};

use crate::{ApiResource, DynamicResourceScope, Resource};
use std::borrow::Cow;

/// Type information that is flattened into every kubernetes object
#[derive(Deserialize, Serialize, Clone, Default, Debug, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -65,4 +63,127 @@ impl Resource for PartialObjectMeta {
fn meta_mut(&mut self) -> &mut ObjectMeta {
&mut self.metadata
}

fn typemeta() -> Option<TypeMeta> {
None
}
}


/// A runtime accessor trait for `TypeMeta`
///
/// This trait is a runtime subset of the `Resource` trait that can read the object directly.
/// It **cannot** retrieve the plural, **nor** the scope of a resource (which requires an `ApiResource`).
pub trait Inspect {
/// Get the `TypeMeta` of an object
///
/// This is a safe `TypeMeta` getter for all object types
/// While it is generally safe to unwrap this option, do note that a few endpoints can elide it.
fn types(&self) -> Option<TypeMeta>;

/// Get the `TypeMeta` of an object that is guaranteed to have it
///
/// Returns `TypeMeta` when exists, panics otherwise
fn types_unchecked(&self) -> TypeMeta;

/// Get the `kind` of an object
fn kind(&self) -> Option<Cow<'_, str>>;
/// Get the `apiVersion` of any object
fn api_version(&self) -> Option<Cow<'_, str>>;
/// Get a reference to the `ObjectMeta` of an object
fn meta(&self) -> &ObjectMeta;
/// Get a mutable reference to the `ObjectMeta` of an Object
fn meta_mut(&mut self) -> &mut ObjectMeta;
}

// lean on static info on k8s_openapi generated types (safer than runtime lookups)
impl<K> Inspect for K
where
K: k8s_openapi::Resource,
K: k8s_openapi::Metadata<Ty = ObjectMeta>,
{
fn types(&self) -> Option<TypeMeta> {
Some(self.types_unchecked())
}

fn types_unchecked(&self) -> TypeMeta {
TypeMeta {
api_version: K::API_VERSION.into(),
kind: K::KIND.into(),
}
}

fn kind(&self) -> Option<Cow<'_, str>> {
Some(K::KIND.into())
}

fn api_version(&self) -> Option<Cow<'_, str>> {
Some(K::API_VERSION.into())
}

fn meta(&self) -> &ObjectMeta {
self.metadata()
}

fn meta_mut(&mut self) -> &mut ObjectMeta {
self.metadata_mut()
}
}

// always lookup from object in dynamic cases
impl<P, U> Inspect for Object<P, U>
where
P: Clone,
U: Clone,
{
fn types(&self) -> Option<TypeMeta> {
self.types.clone()
}

fn types_unchecked(&self) -> TypeMeta {
self.types.clone().unwrap()
}

fn kind(&self) -> Option<Cow<'_, str>> {
self.types.as_ref().map(|t| Cow::Borrowed(t.kind.as_ref()))
}

fn api_version(&self) -> Option<Cow<'_, str>> {
self.types.as_ref().map(|t| Cow::Borrowed(t.api_version.as_ref()))
}

fn meta(&self) -> &ObjectMeta {
&self.metadata
}

fn meta_mut(&mut self) -> &mut ObjectMeta {
&mut self.metadata
}
}

// always lookup from object in dynamic cases
impl Inspect for DynamicObject {
fn types(&self) -> Option<TypeMeta> {
self.types.clone()
}

fn types_unchecked(&self) -> TypeMeta {
self.types.clone().unwrap()
}

fn kind(&self) -> Option<Cow<'_, str>> {
self.types.as_ref().map(|t| Cow::Borrowed(t.kind.as_ref()))
}

fn api_version(&self) -> Option<Cow<'_, str>> {
self.types.as_ref().map(|t| Cow::Borrowed(t.api_version.as_ref()))
}

fn meta(&self) -> &ObjectMeta {
&self.metadata
}

fn meta_mut(&mut self) -> &mut ObjectMeta {
&mut self.metadata
}
}
4 changes: 4 additions & 0 deletions kube-core/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ where
fn meta_mut(&mut self) -> &mut ObjectMeta {
&mut self.metadata
}

fn typemeta() -> Option<TypeMeta> {
None
}
}

impl<P, U> HasSpec for Object<P, U>
Expand Down
24 changes: 19 additions & 5 deletions kube-core/src/resource.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::TypeMeta;
pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
use k8s_openapi::{
api::core::v1::ObjectReference,
Expand Down Expand Up @@ -39,13 +40,13 @@ pub trait Resource {
/// Dynamic types should select `Scope = DynamicResourceScope`
type Scope;

/// Returns kind of this object
/// Returns the static kind of this Resource
fn kind(dt: &Self::DynamicType) -> Cow<'_, str>;
/// Returns group of this object
/// Returns the static group of this Resource
fn group(dt: &Self::DynamicType) -> Cow<'_, str>;
/// Returns version of this object
/// Returns the static version of this Resource
fn version(dt: &Self::DynamicType) -> Cow<'_, str>;
/// Returns apiVersion of this object
/// Returns the static apiVersion of this Resource
fn api_version(dt: &Self::DynamicType) -> Cow<'_, str> {
let group = Self::group(dt);
if group.is_empty() {
Expand All @@ -56,7 +57,7 @@ pub trait Resource {
group.push_str(&Self::version(dt));
group.into()
}
/// Returns the plural name of the kind
/// Returns the static plural name of this Resource
///
/// This is known as the resource in apimachinery, we rename it for disambiguation.
fn plural(dt: &Self::DynamicType) -> Cow<'_, str>;
Expand Down Expand Up @@ -113,6 +114,12 @@ pub trait Resource {
..OwnerReference::default()
})
}

/// Return `TypeMeta` of a Resource where it can be statically determined
///
/// This is only possible on static types.
/// Dynamic types need to find these via discovery or through the `Inspect` trait.
fn typemeta() -> Option<TypeMeta>;
}

/// Implement accessor trait for any ObjectMeta-using Kubernetes Resource
Expand Down Expand Up @@ -151,6 +158,13 @@ where
fn meta_mut(&mut self) -> &mut ObjectMeta {
self.metadata_mut()
}

fn typemeta() -> Option<TypeMeta> {
Some(TypeMeta {
api_version: K::API_VERSION.into(),
kind: K::KIND.into(),
})
}
}

/// Helper methods for resources.
Expand Down
45 changes: 43 additions & 2 deletions kube-derive/src/custom_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,50 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
fn meta_mut(&mut self) -> &mut #k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
&mut self.metadata
}

fn typemeta() -> Option<#kube_core::TypeMeta> {
Some(#kube_core::TypeMeta {
api_version: #api_ver.into(),
kind: #kind.into(),
})
}
}
};

// 3. implement typeinfo trait
let impl_typeinfo = quote! {
impl #kube_core::Inspect for #rootident {

fn types(&self) -> Option<#kube_core::TypeMeta> {
Some(#kube_core::TypeMeta {
api_version: #api_ver.into(),
kind: #kind.into(),
})
}

fn types_unchecked(&self) -> #kube_core::TypeMeta {
self.types().unwrap()
}

fn kind(&self) -> Option<std::borrow::Cow<'_, str>> {
Some(#kind.into())
}

fn api_version(&self) -> Option<std::borrow::Cow<'_, str>> {
Some(#api_ver.into())
}

fn meta(&self) -> &#k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
&self.metadata
}

fn meta_mut(&mut self) -> &mut #k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
&mut self.metadata
}
}
};

// 3. Implement Default if requested
// 4. Implement Default if requested
let impl_default = if has_default {
quote! {
impl Default for #rootident {
Expand All @@ -340,7 +380,7 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
quote! {}
};

// 4. Implement CustomResource
// 5. Implement CustomResource

// Compute a bunch of crd props
let printers = format!("[ {} ]", printcolums.join(",")); // hacksss
Expand Down Expand Up @@ -468,6 +508,7 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
quote! {
#root_obj
#impl_resource
#impl_typeinfo
#impl_default
#impl_crd
#impl_hasspec
Expand Down
Loading