diff --git a/Cargo.toml b/Cargo.toml index 368de83c5..244a52345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,8 @@ members = [ # internal "tests", "examples", -] \ No newline at end of file +] + +[patch.crates-io.k8s-openapi] +git = "https://github.com/mikailbag/k8s-openapi" +branch = "expose-resource-name" \ No newline at end of file diff --git a/kube-derive/src/custom_resource.rs b/kube-derive/src/custom_resource.rs index 62dbe0e50..acd5fd41d 100644 --- a/kube-derive/src/custom_resource.rs +++ b/kube-derive/src/custom_resource.rs @@ -207,6 +207,14 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea #plural.into() } + fn scope(_: &()) -> kube::api::Scope { + if #namespaced { + kube::api::Scope::Namespaced + } else { + kube::api::Scope::Cluster + } + } + fn meta(&self) -> &kube::api::ObjectMeta { &self.metadata } diff --git a/kube/src/api/dynamic.rs b/kube/src/api/dynamic.rs index 2d7522347..2746b74c3 100644 --- a/kube/src/api/dynamic.rs +++ b/kube/src/api/dynamic.rs @@ -1,5 +1,5 @@ use crate::{ - api::{metadata::TypeMeta, Resource}, + api::{metadata::TypeMeta, Resource, Scope}, Error, Result, }; use k8s_openapi::apimachinery::pkg::apis::meta::v1::{APIResource, ObjectMeta}; @@ -20,6 +20,8 @@ pub struct GroupVersionKind { api_version: String, /// Optional plural/resource plural: Option, + /// Scope (Unknown by default) + scope: Scope, } impl GroupVersionKind { @@ -57,12 +59,18 @@ impl GroupVersionKind { format!("{}/{}", group, version) }; let plural = Some(ar.name.clone()); + let scope = if ar.namespaced { + Scope::Namespaced + } else { + Scope::Cluster + }; Self { group, version, kind, api_version, plural, + scope } } @@ -94,6 +102,7 @@ impl GroupVersionKind { kind, api_version, plural: None, + scope: Scope::Unknown, }) } @@ -102,6 +111,12 @@ impl GroupVersionKind { self.plural = Some(plural.to_string()); self } + + /// Set explicit scope (instead of default Unknown) + pub fn scope(mut self, scope: Scope) -> Self { + self.scope = scope; + self + } } /// Represents a type-erased object resource. @@ -247,6 +262,10 @@ impl Resource for DynamicObject { } } + fn scope(_dt: &GroupVersionKind) -> Scope { + Scope::Unknown + } + fn meta(&self) -> &ObjectMeta { &self.metadata } diff --git a/kube/src/api/metadata.rs b/kube/src/api/metadata.rs index 2289a3bcd..dd93bda63 100644 --- a/kube/src/api/metadata.rs +++ b/kube/src/api/metadata.rs @@ -4,6 +4,20 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::BTreeMap}; +/// Scope of the resource +#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] +pub enum Scope { + /// Resource is cluster-scoped, i.e. not bound + /// to any particular namespace + Cluster, + /// Resource is namespaced: it is owned by the namespace + /// specified in the `.metadata.namespace` field. + Namespaced, + /// Unknown scope + // Future work: delete this variant + Unknown, +} + /// An accessor trait for a kubernetes Resource. /// /// This is for a subset of Kubernetes type that do not end in `List`. @@ -32,6 +46,8 @@ pub trait Resource { fn group(dt: &Self::DynamicType) -> Cow<'_, str>; /// Returns version of this object fn version(dt: &Self::DynamicType) -> Cow<'_, str>; + /// Returns resource scope + fn scope(dt: &Self::DynamicType) -> Scope; /// Returns apiVersion of this object fn api_version(dt: &Self::DynamicType) -> Cow<'_, str> { let group = Self::group(dt); @@ -194,6 +210,18 @@ where K::API_VERSION.into() } + fn plural(_: &()) -> Cow<'_, str> { + K::PLURAL_NAME.into() + } + + fn scope(_: &()) -> Scope { + if K::NAMESPACED { + Scope::Namespaced + } else { + Scope::Cluster + } + } + fn meta(&self) -> &ObjectMeta { self.metadata() } diff --git a/kube/src/api/mod.rs b/kube/src/api/mod.rs index 63b6c5051..65aee2d49 100644 --- a/kube/src/api/mod.rs +++ b/kube/src/api/mod.rs @@ -34,6 +34,6 @@ pub(crate) mod object; pub use self::object::{Object, ObjectList, WatchEvent}; mod metadata; -pub use self::metadata::{ListMeta, ObjectMeta, Resource, ResourceExt, TypeMeta}; +pub use self::metadata::{ListMeta, ObjectMeta, Resource, ResourceExt, Scope, TypeMeta}; #[cfg(feature = "admission")] pub mod admission; diff --git a/kube/src/api/typed.rs b/kube/src/api/typed.rs index 584d68d2a..d6fd9e03d 100644 --- a/kube/src/api/typed.rs +++ b/kube/src/api/typed.rs @@ -6,7 +6,7 @@ use tracing::instrument; use crate::{ api::{ - DeleteParams, ListParams, ObjectList, Patch, PatchParams, PostParams, Request, Resource, WatchEvent, + DeleteParams, ListParams, ObjectList, Patch, PatchParams, PostParams, Request, Resource, WatchEvent, Scope }, client::{Client, Status}, Result, @@ -38,22 +38,15 @@ where { /// Cluster level resources, or resources viewed across all namespaces pub fn all(client: Client) -> Self { - let url = K::url_path(&Default::default(), None); - Self { - client, - request: Request::new(url), - phantom: iter::empty(), - } + Self::all_with(client, &Default::default()) } /// Namespaced resource within a given namespace + /// + /// # Panics + /// This function panics if the resource is clister-scoped. pub fn namespaced(client: Client, ns: &str) -> Self { - let url = K::url_path(&Default::default(), Some(ns)); - Self { - client, - request: Request::new(url), - phantom: iter::empty(), - } + Self::namespaced_with(client, ns, &Default::default()) } } @@ -76,7 +69,13 @@ impl Api { /// Namespaced resource within a given namespace /// /// This function accepts `K::DynamicType` so it can be used with dynamic resources. + /// + /// # Panics + /// This function panics if the resource is cluster-scoped. pub fn namespaced_with(client: Client, ns: &str, dyntype: &K::DynamicType) -> Self { + if let Scope::Cluster = K::scope(dyntype) { + panic!("Namespaced Api created for the cluster-scoped resource"); + } let url = K::url_path(dyntype, Some(ns)); Self { client, @@ -382,3 +381,4 @@ impl From> for Client { api.client } } +