Skip to content

Commit

Permalink
try the two suggested traits for resource scope - for #194
Browse files Browse the repository at this point in the history
  • Loading branch information
clux committed Apr 1, 2020
1 parent cde680d commit 505ceb9
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 26 deletions.
1 change: 1 addition & 0 deletions kube/src/api/crds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ mod test {
};
let client = Client::try_default().await.unwrap();
let r1: Api<Foo> = Api::namespaced(client.clone(), "myns");
impl crate::api::resource::NamespaceScopedResource for Foo {} // TODO: This in kube-derive

let r2: Api<Foo> = CustomResource::kind("Foo")
.group("clux.dev")
Expand Down
93 changes: 70 additions & 23 deletions kube/src/api/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,51 @@ pub struct Resource {
pub namespace: Option<String>,
}


// Try Arnavion's first suggestion
pub trait ClusterScopedResource: k8s_openapi::Resource { }
pub trait NamespaceScopedResource: k8s_openapi::Resource { }
use k8s::{
admissionregistration::v1beta1 as adregv1beta1,
apps::v1 as appsv1,
authorization::v1 as authv1,
autoscaling::v1 as autoscalingv1,
batch::v1beta1 as batchv1beta1,
core::v1 as corev1,
extensions::v1beta1 as extsv1beta1,
networking::{v1 as networkingv1, v1beta1 as networkingv1beta1},
rbac::v1 as rbacv1,
storage::v1 as storagev1,
};
use k8s_openapi::api as k8s;
impl NamespaceScopedResource for corev1::Secret {}
impl NamespaceScopedResource for rbacv1::Role {}
impl NamespaceScopedResource for batchv1beta1::CronJob {}
impl NamespaceScopedResource for autoscalingv1::HorizontalPodAutoscaler {}
impl NamespaceScopedResource for networkingv1::NetworkPolicy {}
impl NamespaceScopedResource for extsv1beta1::Ingress {}
impl NamespaceScopedResource for appsv1::Deployment {}
impl NamespaceScopedResource for corev1::Pod {}
impl NamespaceScopedResource for appsv1::ReplicaSet {}
impl NamespaceScopedResource for networkingv1beta1::Ingress {}
impl NamespaceScopedResource for appsv1::DaemonSet {}

use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1 as apiextsv1;
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1beta1 as apiextsv1beta1;

impl ClusterScopedResource for storagev1::VolumeAttachment {}
impl ClusterScopedResource for adregv1beta1::ValidatingWebhookConfiguration {}
impl ClusterScopedResource for authv1::SelfSubjectRulesReview {}
impl ClusterScopedResource for apiextsv1::CustomResourceDefinition {}
impl ClusterScopedResource for corev1::Namespace {}
impl ClusterScopedResource for apiextsv1beta1::CustomResourceDefinition {}
impl ClusterScopedResource for corev1::Node {}



impl Resource {
/// Cluster level resources, or resources viewed across all namespaces
pub fn all<K: k8s_openapi::Resource>() -> Self {
/// Cluster level resources,
pub fn cluster<K: ClusterScopedResource>() -> Self {
Self {
api_version: K::API_VERSION.to_string(),
kind: K::KIND.to_string(),
Expand All @@ -41,14 +83,19 @@ impl Resource {
}
}

/// Namespaced resource within a given namespace
pub fn namespaced<K: k8s_openapi::Resource>(ns: &str) -> Self {
match K::KIND {
"Node" | "Namespace" | "ClusterRole" | "CustomResourceDefinition" => {
panic!("{} is not a namespace scoped resource", K::KIND)
}
_ => {}
/// Namespaced resources viewed across all namespaces
pub fn all<K: NamespaceScopedResource>() -> Self {
Self {
api_version: K::API_VERSION.to_string(),
kind: K::KIND.to_string(),
group: K::GROUP.to_string(),
version: K::VERSION.to_string(),
namespace: None,
}
}

/// Namespaced resource within a given namespace
pub fn namespaced<K: NamespaceScopedResource>(ns: &str) -> Self {
Self {
api_version: K::API_VERSION.to_string(),
kind: K::KIND.to_string(),
Expand Down Expand Up @@ -588,14 +635,14 @@ mod test {

#[test]
fn api_url_vattach() {
let r = Resource::all::<storagev1::VolumeAttachment>();
let r = Resource::cluster::<storagev1::VolumeAttachment>();
let req = r.create(&PostParams::default(), vec![]).unwrap();
assert_eq!(req.uri(), "/apis/storage.k8s.io/v1/volumeattachments?");
}

#[test]
fn api_url_admission() {
let r = Resource::all::<adregv1beta1::ValidatingWebhookConfiguration>();
let r = Resource::cluster::<adregv1beta1::ValidatingWebhookConfiguration>();
let req = r.create(&PostParams::default(), vec![]).unwrap();
assert_eq!(
req.uri(),
Expand All @@ -605,7 +652,7 @@ mod test {

#[test]
fn api_auth_selfreview() {
let r = Resource::all::<authv1::SelfSubjectRulesReview>();
let r = Resource::cluster::<authv1::SelfSubjectRulesReview>();
assert_eq!(r.group, "authorization.k8s.io");
assert_eq!(r.kind, "SelfSubjectRulesReview");

Expand All @@ -618,7 +665,7 @@ mod test {

#[test]
fn api_apiextsv1_crd() {
let r = Resource::all::<apiextsv1::CustomResourceDefinition>();
let r = Resource::cluster::<apiextsv1::CustomResourceDefinition>();
let req = r.create(&PostParams::default(), vec![]).unwrap();
assert_eq!(
req.uri(),
Expand Down Expand Up @@ -679,7 +726,7 @@ mod test {

#[test]
fn namespace_path() {
let r = Resource::all::<corev1::Namespace>();
let r = Resource::cluster::<corev1::Namespace>();
let gp = ListParams::default();
let req = r.list(&gp).unwrap();
assert_eq!(req.uri(), "/api/v1/namespaces")
Expand All @@ -704,7 +751,7 @@ mod test {
// subresources with weird version accuracy
#[test]
fn patch_status_path() {
let r = Resource::all::<corev1::Node>();
let r = Resource::cluster::<corev1::Node>();
let pp = PatchParams::default();
let req = r.patch_status("mynode", &pp, vec![]).unwrap();
assert_eq!(req.uri(), "/api/v1/nodes/mynode/status?");
Expand All @@ -716,7 +763,7 @@ mod test {
}
#[test]
fn replace_status_path() {
let r = Resource::all::<corev1::Node>();
let r = Resource::cluster::<corev1::Node>();
let pp = PostParams::default();
let req = r.replace_status("mynode", &pp, vec![]).unwrap();
assert_eq!(req.uri(), "/api/v1/nodes/mynode/status?");
Expand Down Expand Up @@ -745,7 +792,7 @@ mod test {

#[test]
fn replace_status() {
let r = Resource::all::<apiextsv1beta1::CustomResourceDefinition>();
let r = Resource::cluster::<apiextsv1beta1::CustomResourceDefinition>();
let pp = PostParams::default();
let req = r.replace_status("mycrd.domain.io", &pp, vec![]).unwrap();
assert_eq!(
Expand All @@ -755,31 +802,31 @@ mod test {
}
#[test]
fn get_scale_path() {
let r = Resource::all::<corev1::Node>();
let r = Resource::cluster::<corev1::Node>();
let req = r.get_scale("mynode").unwrap();
assert_eq!(req.uri(), "/api/v1/nodes/mynode/scale");
assert_eq!(req.method(), "GET");
}
#[test]
fn patch_scale_path() {
let r = Resource::all::<corev1::Node>();
let r = Resource::cluster::<corev1::Node>();
let pp = PatchParams::default();
let req = r.patch_scale("mynode", &pp, vec![]).unwrap();
assert_eq!(req.uri(), "/api/v1/nodes/mynode/scale?");
assert_eq!(req.method(), "PATCH");
}
#[test]
fn replace_scale_path() {
let r = Resource::all::<corev1::Node>();
let r = Resource::cluster::<corev1::Node>();
let pp = PostParams::default();
let req = r.replace_scale("mynode", &pp, vec![]).unwrap();
assert_eq!(req.uri(), "/api/v1/nodes/mynode/scale?");
assert_eq!(req.method(), "PUT");
}

#[test]
#[should_panic]
/* #[test]
#[should_panic] - compile fails now!
fn all_resources_not_namespaceable() {
Resource::namespaced::<corev1::Node>("ns");
}
}*/
}
25 changes: 22 additions & 3 deletions kube/src/api/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,29 @@ pub struct Api<K> {
pub(crate) phantom: PhantomData<K>,
}

use crate::api::resource::{NamespaceScopedResource, ClusterScopedResource};

/// Expose same interface as Api for controlling scope/group/versions/ns
impl<K> Api<K>
where
K: k8s_openapi::Resource,
{
/// Cluster level resources, or resources viewed across all namespaces
pub fn all(client: Client) -> Self {
/// Cluster level resources
pub fn cluster(client: Client) -> Self
where K: ClusterScopedResource
{
let api = Resource::cluster::<K>();
Self {
api,
client,
phantom: PhantomData,
}
}

/// Namespaced resources viewed across all namespaces
pub fn all(client: Client) -> Self
where K: NamespaceScopedResource
{
let api = Resource::all::<K>();
Self {
api,
Expand All @@ -45,7 +61,9 @@ where
}

/// Namespaced resource within a given namespace
pub fn namespaced(client: Client, ns: &str) -> Self {
pub fn namespaced(client: Client, ns: &str) -> Self
where K: NamespaceScopedResource
{
let api = Resource::namespaced::<K>(ns);
Self {
api,
Expand Down Expand Up @@ -121,6 +139,7 @@ where
where
K: Serialize,
{
// TODO: assert self.resource is not a Resource::all
let bytes = serde_json::to_vec(&data)?;
let req = self.api.create(&pp, bytes)?;
self.client.request::<K>(req).await
Expand Down

0 comments on commit 505ceb9

Please sign in to comment.