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

Separate GVK from ApiResource #495

Merged
merged 8 commits into from
May 15, 2021
Merged
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
13 changes: 7 additions & 6 deletions examples/crd_derive_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use futures::{StreamExt, TryStreamExt};
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition;
use kube::{
api::{
Api, DeleteParams, DynamicObject, GroupVersionKind, ListParams, Patch, PatchParams, PostParams,
WatchEvent,
Api, ApiResource, DeleteParams, DynamicObject, GroupVersionKind, ListParams, Patch, PatchParams,
PostParams, WatchEvent,
},
Client, CustomResource,
};
Expand Down Expand Up @@ -148,8 +148,9 @@ async fn main() -> Result<()> {
});

// Set up dynamic resource to test using raw values.
let gvk = GroupVersionKind::gvk("clux.dev", "v1", "Foo")?;
let dynapi: Api<DynamicObject> = Api::namespaced_with(client.clone(), &namespace, &gvk);
let gvk = GroupVersionKind::gvk("clux.dev", "v1", "Foo");
let api_resource = ApiResource::from_gvk(&gvk);
let dynapi: Api<DynamicObject> = Api::namespaced_with(client.clone(), &namespace, &api_resource);

// Test that skipped nullable field without default is not defined.
let val = dynapi.get("bar").await?.data;
Expand All @@ -160,7 +161,7 @@ async fn main() -> Result<()> {
assert!(spec.contains_key("nullable"));

// Test defaulting of `non_nullable_with_default` field
let data = DynamicObject::new("baz", &gvk).data(serde_json::json!({
let data = DynamicObject::new("baz", &api_resource).data(serde_json::json!({
"spec": {
"non_nullable": "a required field",
// `non_nullable_with_default` field is missing
Expand All @@ -180,7 +181,7 @@ async fn main() -> Result<()> {
assert_eq!(serde_json::to_string(&val["spec"]["set_listable"])?, "[2]");

// Missing required field (non-nullable without default) is an error
let data = DynamicObject::new("qux", &gvk).data(serde_json::json!({
let data = DynamicObject::new("qux", &api_resource).data(serde_json::json!({
"spec": {}
}));
let res = dynapi.create(&PostParams::default(), &data).await;
Expand Down
37 changes: 16 additions & 21 deletions examples/dynamic_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
//! to `kubectl get all --all-namespaces`.

use kube::{
api::{Api, DynamicObject, Resource, ResourceExt},
client::Discovery,
Client,
api::{Api, DynamicObject, ResourceExt},
client::{
discovery::{verbs, Discovery, Scope},
Client,
},
};
use log::{info, warn};
use log::info;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
Expand All @@ -23,30 +25,23 @@ async fn main() -> anyhow::Result<()> {

for group in discovery.groups() {
let ver = group.preferred_version_or_guess();
for gvk in group.resources_by_version(ver) {
let kind = DynamicObject::kind(&gvk);
let (_, raw_resource) = discovery
.resolve_group_version_kind(group.name(), ver, &kind)
.unwrap();
let api: Api<DynamicObject> = if raw_resource.namespaced {
for (api_res, extras) in group.resources_by_version(ver) {
if !extras.supports_operation(verbs::LIST) {
continue;
}
let api: Api<DynamicObject> = if extras.scope == Scope::Namespaced {
if let Some(ns) = &ns_filter {
Api::namespaced_with(client.clone(), ns, &gvk)
Api::namespaced_with(client.clone(), ns, &api_res)
} else {
Api::all_with(client.clone(), &gvk)
Api::all_with(client.clone(), &api_res)
}
} else {
Api::all_with(client.clone(), &gvk)
Api::all_with(client.clone(), &api_res)
};

info!("{}/{} : {}", group.name(), ver, kind);
info!("{}/{} : {}", group.name(), ver, api_res.kind);

let list = match api.list(&Default::default()).await {
Ok(l) => l,
Err(e) => {
warn!("Failed to list: {:#}", e);
continue;
}
};
let list = api.list(&Default::default()).await?;
for item in list.items {
let name = item.name();
let ns = item.metadata.namespace.map(|s| s + "/").unwrap_or_default();
Expand Down
17 changes: 14 additions & 3 deletions examples/dynamic_watcher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use futures::prelude::*;
use kube::{
api::{DynamicObject, GroupVersionKind, ListParams, ResourceExt},
api::{ApiResource, DynamicObject, GroupVersionKind, ListParams, ResourceExt},
Api, Client,
};
use kube_runtime::{utils::try_flatten_applied, watcher};
Expand All @@ -18,9 +18,20 @@ async fn main() -> anyhow::Result<()> {
let kind = env::var("KIND").unwrap_or_else(|_| "Foo".into());

// Turn them into a GVK
let gvk = GroupVersionKind::gvk(&group, &version, &kind)?;
let gvk = GroupVersionKind::gvk(&group, &version, &kind);
let mut api_resource = ApiResource::from_gvk(&gvk);

if let Some(resource) = env::var("RESOURCE").ok() {
api_resource.plural = resource;
} else {
println!(
"Using inferred plural name (use RESOURCE to override): {}",
api_resource.plural
);
}

// Use them in an Api with the GVK as its DynamicType
let api = Api::<DynamicObject>::all_with(client, &gvk);
let api = Api::<DynamicObject>::all_with(client, &api_resource);

// Fully compatible with kube-runtime
let watcher = watcher(api, ListParams::default());
Expand Down
6 changes: 5 additions & 1 deletion kube-derive/src/custom_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
}
};

// Implement the ::crd method (fine to not have in a trait as its a generated type)
// Implement the ::crd and ::api_resource methods (fine to not have in a trait as its a generated type)
let impl_crd = quote! {
impl #rootident {
pub fn crd() -> #apiext::CustomResourceDefinition {
Expand Down Expand Up @@ -357,6 +357,10 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
serde_json::from_value(jsondata)
.expect("valid custom resource from #[kube(attrs..)]")
}

pub fn api_resource() -> kube::api::ApiResource {
kube::api::ApiResource::erase::<Self>(&())
}
}
};

Expand Down
7 changes: 1 addition & 6 deletions kube-runtime/src/reflector/object_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,7 @@ impl<K: Resource> ObjectRef<K> {

pub fn erase(self) -> ObjectRef<DynamicObject> {
ObjectRef {
dyntype: kube::api::GroupVersionKind::gvk(
K::group(&self.dyntype).as_ref(),
K::version(&self.dyntype).as_ref(),
K::kind(&self.dyntype).as_ref(),
)
.expect("valid gvk"),
dyntype: kube::api::ApiResource::erase::<K>(&self.dyntype),
name: self.name,
namespace: self.namespace,
}
Expand Down
Loading