Skip to content

Commit

Permalink
add a versioned CustomResourceExt trait - fixes #497 (#545)
Browse files Browse the repository at this point in the history
* add a versioned CustomResourceExt trait - fixes #497

* less import lines

* simplify cargo release flags via crate-ci/cargo-release#253

* new clippy lint
  • Loading branch information
clux authored Jun 8, 2021
1 parent 2ef5dc2 commit 1f31d15
Show file tree
Hide file tree
Showing 24 changed files with 100 additions and 45 deletions.
3 changes: 3 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ authors = [
publish = false
edition = "2018"

[package.metadata.release]
disable-release = true

[features]
default = ["native-tls", "schema", "kubederive", "ws"]
kubederive = ["kube/derive"] # by default import kube-derive with its default features
Expand Down
1 change: 1 addition & 0 deletions examples/crd_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1beta1 as a
use kube::{
api::{Api, DeleteParams, ListParams, Patch, PatchParams, PostParams, ResourceExt},
Client, CustomResource,
core::crd::v1beta1::CustomResourceExt
};

// Own custom resource
Expand Down
2 changes: 1 addition & 1 deletion examples/crd_apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1 as apiext

use kube::{
api::{Api, ListParams, Patch, PatchParams, ResourceExt, WatchEvent},
Client, CustomResource,
Client, CustomResource, CustomResourceExt
};

// NB: This example uses server side apply and beta1 customresources
Expand Down
2 changes: 1 addition & 1 deletion examples/crd_derive.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition;
use kube::{CustomResource, Resource};
use kube::{CustomResource, CustomResourceExt, Resource};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

Expand Down
3 changes: 2 additions & 1 deletion examples/crd_derive_no_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ properties:
#[cfg(not(feature = "schema"))]
impl Bar {
fn crd_with_manual_schema() -> CustomResourceDefinition {
use kube::CustomResourceExt;
let schema: JSONSchemaProps = serde_yaml::from_str(MANUAL_SCHEMA).expect("invalid schema");

let mut crd = Self::crd();
let mut crd = <Self as CustomResourceExt>::crd();
crd.spec.versions.iter_mut().for_each(|v| {
v.schema = Some(CustomResourceValidation {
open_api_v3_schema: Some(schema.clone()),
Expand Down
2 changes: 1 addition & 1 deletion examples/crd_derive_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use kube::{
Api, ApiResource, DeleteParams, DynamicObject, GroupVersionKind, ListParams, Patch, PatchParams,
PostParams, WatchEvent,
},
Client, CustomResource,
Client, CustomResource, CustomResourceExt
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand Down
36 changes: 36 additions & 0 deletions kube-core/src/crd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Traits and tyes for CustomResources
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions as apiexts;

/// Types for v1 CustomResourceDefinitions
pub mod v1 {
/// Extension trait that will be implemented by kube-derive
///
/// This trait variant is implemented by default (or when `#[kube(apiextensions = "v1")]`)
pub trait CustomResourceExt {
/// Helper to generate the CRD including the JsonSchema
///
/// This is using the stable v1::CustomResourceDefinitions (present in kubernetes >= 1.16)
fn crd() -> super::apiexts::v1::CustomResourceDefinition;
/// Helper to generate the api information type for use with the dynamic `Api`
fn api_resource() -> crate::discovery::ApiResource;
}
}

/// Types for legacy v1beta1 CustomResourceDefinitions
pub mod v1beta1 {
/// Extension trait that will be implemented by kube-derive for legacy v1beta1::CustomResourceDefinitions
///
/// This trait variant is only implemented with `#[kube(apiextensions = "v1beta1")]`
pub trait CustomResourceExt {
/// Helper to generate the legacy CRD without a JsonSchema
///
/// This is using v1beta1::CustomResourceDefinitions (which will be removed in kubernetes 1.22)
fn crd() -> super::apiexts::v1beta1::CustomResourceDefinition;
/// Helper to generate the api information type for use with the dynamic `Api`
fn api_resource() -> crate::discovery::ApiResource;
}
}

/// re-export the current latest version until a newer one is available in cloud providers
pub use v1::CustomResourceExt;
3 changes: 3 additions & 0 deletions kube-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub mod discovery;
pub mod dynamic;
pub use dynamic::DynamicObject;

pub mod crd;
pub use crd::CustomResourceExt;

pub mod gvk;
pub use gvk::{GroupVersion, GroupVersionKind, GroupVersionResource};

Expand Down
4 changes: 2 additions & 2 deletions kube-core/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ impl PatchParams {
if self.force {
qp.append_pair("force", "true");
}
if let Some(ref field_manager) = self.field_manager {
qp.append_pair("fieldManager", &field_manager);
if let Some(ref fm) = self.field_manager {
qp.append_pair("fieldManager", fm);
}
}

Expand Down
12 changes: 6 additions & 6 deletions kube-core/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ impl Request {
let mut qp = form_urlencoded::Serializer::new(target);

if let Some(fields) = &lp.field_selector {
qp.append_pair("fieldSelector", &fields);
qp.append_pair("fieldSelector", fields);
}
if let Some(labels) = &lp.label_selector {
qp.append_pair("labelSelector", &labels);
qp.append_pair("labelSelector", labels);
}
if let Some(limit) = &lp.limit {
qp.append_pair("limit", &limit.to_string());
Expand Down Expand Up @@ -69,10 +69,10 @@ impl Request {
// https://github.com/kubernetes/kubernetes/issues/6513
qp.append_pair("timeoutSeconds", &lp.timeout.unwrap_or(290).to_string());
if let Some(fields) = &lp.field_selector {
qp.append_pair("fieldSelector", &fields);
qp.append_pair("fieldSelector", fields);
}
if let Some(labels) = &lp.label_selector {
qp.append_pair("labelSelector", &labels);
qp.append_pair("labelSelector", labels);
}
if lp.bookmarks {
qp.append_pair("allowWatchBookmarks", "true");
Expand Down Expand Up @@ -120,10 +120,10 @@ impl Request {
let target = format!("{}?", self.url_path);
let mut qp = form_urlencoded::Serializer::new(target);
if let Some(fields) = &lp.field_selector {
qp.append_pair("fieldSelector", &fields);
qp.append_pair("fieldSelector", fields);
}
if let Some(labels) = &lp.label_selector {
qp.append_pair("labelSelector", &labels);
qp.append_pair("labelSelector", labels);
}
let urlstr = qp.finish();
let body = serde_json::to_vec(&dp)?;
Expand Down
2 changes: 1 addition & 1 deletion kube-core/src/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl Request {
let mut qp = form_urlencoded::Serializer::new(target);

if let Some(container) = &lp.container {
qp.append_pair("container", &container);
qp.append_pair("container", container);
}

if lp.follow {
Expand Down
11 changes: 7 additions & 4 deletions kube-derive/src/custom_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
let apiext = quote! {
k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::#v1ident
};
let extver = quote! {
kube::core::crd::#v1ident
};

let short_json = serde_json::to_string(&shortnames).unwrap();
let crd_meta_name = format!("{}.{}", plural, group);
Expand Down Expand Up @@ -329,10 +332,10 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
}
};

// Implement the ::crd and ::api_resource methods (fine to not have in a trait as its a generated type)
// Implement the CustomResourcExt trait to allow users writing generic logic on top of them
let impl_crd = quote! {
impl #rootident {
pub fn crd() -> #apiext::CustomResourceDefinition {
impl #extver::CustomResourceExt for #rootident {
fn crd() -> #apiext::CustomResourceDefinition {
let columns : Vec<#apiext::CustomResourceColumnDefinition> = serde_json::from_str(#printers).expect("valid printer column json");
let scale: Option<#apiext::CustomResourceSubresourceScale> = if #scale_code.is_empty() {
None
Expand All @@ -358,7 +361,7 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
.expect("valid custom resource from #[kube(attrs..)]")
}

pub fn api_resource() -> kube::core::ApiResource {
fn api_resource() -> kube::core::ApiResource {
kube::core::ApiResource::erase::<Self>(&())
}
}
Expand Down
11 changes: 7 additions & 4 deletions kube-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ mod custom_resource;
/// This root object will implement the [`kube::Resource`] trait
/// so it can be used with [`kube::Api`].
///
/// The generated type will also implement a `::crd` method to generate the crd
/// at the specified api version (or `v1` if unspecified).
/// The generated type will also implement kube's [`kube::CustomResourceExt`] trait to generate the crd
/// and generate [`kube::core::ApiResource`] information for use with the dynamic api.
///
/// # Example
///
/// ```rust
/// use serde::{Serialize, Deserialize};
/// use kube::Resource;
/// use kube::{Resource, CustomResourceExt};
/// use kube_derive::CustomResource;
/// use schemars::JsonSchema;
///
Expand Down Expand Up @@ -72,7 +72,8 @@ mod custom_resource;
/// The version for `CustomResourceDefinition` desired in the `apiextensions.k8s.io` group.
/// Default is `v1` (for clusters >= 1.17). If using kubernetes <= 1.16 please use `v1beta1`.
///
/// **NOTE**: Support for `v1` requires deriving the openapi v3 `JsonSchema` via the `schemars` dependency.
/// - **NOTE**: Support for `v1` requires deriving the openapi v3 `JsonSchema` via the `schemars` dependency.
/// - **NOTE**: When using `v1beta` the associated `CustomResourceExt` trait lives in `kube::core::crd::v1beta`
///
/// ### `#[kube(singular = "nonstandard-singular")]`
/// To specify the singular name. Defaults to lowercased `kind`.
Expand Down Expand Up @@ -196,6 +197,8 @@ mod custom_resource;
/// [`kube`]: https://docs.rs/kube
/// [`kube::Api`]: https://docs.rs/kube/*/kube/struct.Api.html
/// [`kube::Resource`]: https://docs.rs/kube/*/kube/trait.Resource.html
/// [`kube::core::ApiResource`]: https://docs.rs/kube/*/kube/core/struct.ApiResource.html
/// [`kube::CustomResourceExt`]: https://docs.rs/kube/*/kube/trait.CustomResourceExt.html
#[proc_macro_derive(CustomResource, attributes(kube))]
pub fn derive_custom_resource(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
custom_resource::derive(proc_macro2::TokenStream::from(input)).into()
Expand Down
1 change: 1 addition & 0 deletions kube-derive/tests/crd_schema_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fn default_nullable() -> Option<String> {

#[test]
fn test_crd_schema_matches_expected() {
use kube::core::CustomResourceExt;
assert_eq!(
Foo::crd(),
serde_json::from_value(serde_json::json!({
Expand Down
14 changes: 7 additions & 7 deletions kube/src/api/core_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ where
/// }
/// ```
pub async fn list(&self, lp: &ListParams) -> Result<ObjectList<K>> {
let mut req = self.request.list(&lp)?;
let mut req = self.request.list(lp)?;
req.extensions_mut().insert("list");
self.client.request::<ObjectList<K>>(req).await
}
Expand All @@ -75,7 +75,7 @@ where
K: Serialize,
{
let bytes = serde_json::to_vec(&data)?;
let mut req = self.request.create(&pp, bytes)?;
let mut req = self.request.create(pp, bytes)?;
req.extensions_mut().insert("create");
self.client.request::<K>(req).await
}
Expand Down Expand Up @@ -103,7 +103,7 @@ where
/// }
/// ```
pub async fn delete(&self, name: &str, dp: &DeleteParams) -> Result<Either<K, Status>> {
let mut req = self.request.delete(name, &dp)?;
let mut req = self.request.delete(name, dp)?;
req.extensions_mut().insert("delete");
self.client.request_status::<K>(req).await
}
Expand Down Expand Up @@ -140,7 +140,7 @@ where
dp: &DeleteParams,
lp: &ListParams,
) -> Result<Either<ObjectList<K>, Status>> {
let mut req = self.request.delete_collection(&dp, &lp)?;
let mut req = self.request.delete_collection(dp, lp)?;
req.extensions_mut().insert("delete_collection");
self.client.request_status::<ObjectList<K>>(req).await
}
Expand Down Expand Up @@ -180,7 +180,7 @@ where
pp: &PatchParams,
patch: &Patch<P>,
) -> Result<K> {
let mut req = self.request.patch(name, &pp, patch)?;
let mut req = self.request.patch(name, pp, patch)?;
req.extensions_mut().insert("patch");
self.client.request::<K>(req).await
}
Expand Down Expand Up @@ -234,7 +234,7 @@ where
K: Serialize,
{
let bytes = serde_json::to_vec(&data)?;
let mut req = self.request.replace(name, &pp, bytes)?;
let mut req = self.request.replace(name, pp, bytes)?;
req.extensions_mut().insert("replace");
self.client.request::<K>(req).await
}
Expand Down Expand Up @@ -281,7 +281,7 @@ where
lp: &ListParams,
version: &str,
) -> Result<impl Stream<Item = Result<WatchEvent<K>>>> {
let mut req = self.request.watch(&lp, &version)?;
let mut req = self.request.watch(lp, version)?;
req.extensions_mut().insert("watch");
self.client.request_events::<K>(req).await
}
Expand Down
8 changes: 4 additions & 4 deletions kube/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ where
pp: &PatchParams,
patch: &Patch<P>,
) -> Result<Scale> {
let mut req = self.request.patch_subresource("scale", name, &pp, patch)?;
let mut req = self.request.patch_subresource("scale", name, pp, patch)?;
req.extensions_mut().insert("patch_scale");
self.client.request::<Scale>(req).await
}

/// Replace the scale subresource
pub async fn replace_scale(&self, name: &str, pp: &PostParams, data: Vec<u8>) -> Result<Scale> {
let mut req = self.request.replace_subresource("scale", name, &pp, data)?;
let mut req = self.request.replace_subresource("scale", name, pp, data)?;
req.extensions_mut().insert("replace_scale");
self.client.request::<Scale>(req).await
}
Expand Down Expand Up @@ -98,7 +98,7 @@ where
pp: &PatchParams,
patch: &Patch<P>,
) -> Result<K> {
let mut req = self.request.patch_subresource("status", name, &pp, patch)?;
let mut req = self.request.patch_subresource("status", name, pp, patch)?;
req.extensions_mut().insert("patch_status");
self.client.request::<K>(req).await
}
Expand All @@ -123,7 +123,7 @@ where
/// }
/// ```
pub async fn replace_status(&self, name: &str, pp: &PostParams, data: Vec<u8>) -> Result<K> {
let mut req = self.request.replace_subresource("status", name, &pp, data)?;
let mut req = self.request.replace_subresource("status", name, pp, data)?;
req.extensions_mut().insert("replace_status");
self.client.request::<K>(req).await
}
Expand Down
4 changes: 2 additions & 2 deletions kube/src/client/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,9 @@ fn token_from_gcp_provider(provider: &AuthProviderConfig) -> Result<ProviderToke

if let Some(field) = provider.config.get("token-key") {
let json_output: serde_json::Value = serde_json::from_slice(&output.stdout)?;
let token = extract_value(&json_output, &field)?;
let token = extract_value(&json_output, field)?;
if let Some(field) = provider.config.get("expiry-key") {
let expiry = extract_value(&json_output, &field)?;
let expiry = extract_value(&json_output, field)?;
let expiry = expiry
.parse::<DateTime<Utc>>()
.map_err(ConfigError::MalformedTokenExpirationDate)?;
Expand Down
6 changes: 3 additions & 3 deletions kube/src/client/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub mod native_tls {
if let Some(ders) = root_cert {
for der in ders {
builder.add_root_certificate(
Certificate::from_der(&der).map_err(|e| Error::SslError(format!("{}", e)))?,
Certificate::from_der(der).map_err(|e| Error::SslError(format!("{}", e)))?,
);
}
}
Expand All @@ -40,8 +40,8 @@ pub mod native_tls {
// TODO Replace this with pure Rust implementation to avoid depending on openssl on macOS and Win
fn pkcs12_from_pem(pem: &[u8], password: &str) -> Result<Vec<u8>> {
use openssl::{pkcs12::Pkcs12, pkey::PKey, x509::X509};
let x509 = X509::from_pem(&pem)?;
let pkey = PKey::private_key_from_pem(&pem)?;
let x509 = X509::from_pem(pem)?;
let pkey = PKey::private_key_from_pem(pem)?;
let p12 = Pkcs12::builder().build(password, "kubeconfig", &pkey, &x509)?;
let der = p12.to_der()?;
Ok(der)
Expand Down
2 changes: 1 addition & 1 deletion kube/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl Config {

if let Some(ca_bundle) = loader.ca_bundle()? {
for ca in &ca_bundle {
accept_invalid_certs = hacky_cert_lifetime_for_macos(&ca);
accept_invalid_certs = hacky_cert_lifetime_for_macos(ca);
}
root_cert = Some(ca_bundle);
}
Expand Down
Loading

0 comments on commit 1f31d15

Please sign in to comment.