Skip to content

Commit

Permalink
add example using validator - closes #129
Browse files Browse the repository at this point in the history
Signed-off-by: clux <[email protected]>
  • Loading branch information
clux committed Oct 9, 2021
1 parent 6aa0fd3 commit 7ed2fcf
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 9 deletions.
5 changes: 2 additions & 3 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ ws = ["kube/ws"]
latest = ["k8s-openapi/v1_22"]
deprecated = ["kube/deprecated-crd-v1beta1", "k8s-openapi/v1_21"]

[dependencies]
[dev-dependencies]
tokio-util = "0.6.8"
assert-json-diff = "2.0.1"

[dev-dependencies]
validator = { version = "0.14.0", features = ["derive"] }
anyhow = "1.0.44"
env_logger = "0.9.0"
futures = "0.3.17"
Expand Down
24 changes: 22 additions & 2 deletions examples/crd_api.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#[macro_use] extern crate log;
use either::Either::{Left, Right};
use anyhow::{Result, bail};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::time::Duration;
use validator::Validate;
use tokio::time::sleep;

// Using the old v1beta1 extension requires the deprecated-crd-v1beta1 feature on kube
Expand All @@ -23,13 +25,14 @@ use kube::{
};

// Own custom resource
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug, JsonSchema)]
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug, Validate, JsonSchema)]
#[kube(group = "clux.dev", version = "v1", kind = "Foo", namespaced)]
#[cfg_attr(feature = "deprecated", kube(apiextensions = "v1beta1"))]
#[kube(status = "FooStatus")]
#[kube(scale = r#"{"specReplicasPath":".spec.replicas", "statusReplicasPath":".status.replicas"}"#)]
#[kube(printcolumn = r#"{"name":"Team", "jsonPath": ".spec.metadata.team", "type": "string"}"#)]
pub struct FooSpec {
#[validate(length(min = 3))]
name: String,
info: String,
replicas: i32,
Expand All @@ -42,7 +45,7 @@ pub struct FooStatus {
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
async fn main() -> Result<()> {
std::env::set_var("RUST_LOG", "info,kube=debug");
env_logger::init();
let client = Client::try_default().await?;
Expand Down Expand Up @@ -203,6 +206,23 @@ async fn main() -> anyhow::Result<()> {
// Delete the last - expect a status back (instant delete)
assert!(foos.delete("qux", &dp).await?.is_right());

// Check that validation is being obeyed
info!("Verifying validation rules");
let fx = Foo::new("x", FooSpec {
name: "x".into(),
info: "failing validation obj".into(),
replicas: 1,
});
match foos.create(&pp, &fx).await {
Err(kube::Error::Api(ae)) => {
assert_eq!(ae.code, 422);
assert!(ae.message.contains("spec.name in body should be at least 3 chars long"));
},
Err(e) => bail!("somehow got unexpected error from validation: {:?}", e),
Ok(o) => bail!("somehow created {:?} despite validation", o),
}
info!("Rejected fx for invalid name {}", fx.name());

// Cleanup the full collection - expect a wait
match foos.delete_collection(&dp, &lp).await? {
Left(list) => {
Expand Down
12 changes: 8 additions & 4 deletions kube-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ mod custom_resource;
/// use serde::{Serialize, Deserialize};
/// use kube_derive::CustomResource;
/// use schemars::JsonSchema;
/// use validator::Validate;
///
/// #[derive(CustomResource, Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
/// #[derive(CustomResource, Serialize, Deserialize, Debug, PartialEq, Clone, Validate, JsonSchema)]
/// #[kube(
/// group = "clux.dev",
/// version = "v1",
Expand All @@ -126,9 +127,11 @@ mod custom_resource;
/// scale = r#"{"specReplicasPath":".spec.replicas", "statusReplicasPath":".status.replicas"}"#,
/// printcolumn = r#"{"name":"Spec", "type":"string", "description":"name of foo", "jsonPath":".spec.name"}"#
/// )]
/// #[serde(rename_all = "camelCase")]
/// struct FooSpec {
/// #[validate(length(min = 3))]
/// data: String,
/// replicas: i32
/// replicas_count: i32
/// }
///
/// #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
Expand Down Expand Up @@ -163,18 +166,19 @@ mod custom_resource;
/// - [Serde/Schemars Attributes](https://graham.cool/schemars/examples/3-schemars_attrs/) (no need to duplicate serde renames)
/// - [`#[schemars(schema_with = "func")]`](https://graham.cool/schemars/examples/7-custom_serialization/) (e.g. like in the [`crd_derive` example](https://github.com/kube-rs/kube-rs/blob/master/examples/crd_derive.rs))
/// - `impl JsonSchema` on a type / newtype around external type. See [#129](https://github.com/kube-rs/kube-rs/issues/129#issuecomment-750852916)
/// - [`#[validate(...)]` field attributes with validator](https://github.com/Keats/validator) for kubebuilder style validation rules (see [`crd_api` example](https://github.com/kube-rs/kube-rs/blob/master/examples/crd_api.rs)))
///
/// In general, you will need to override parts of the schemas (for fields in question) when you are:
/// You might need to override parts of the schemas (for fields in question) when you are:
/// - **using complex enums**: enums do not currently generate [structural schemas](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema), so kubernetes won't support them by default
/// - **customizing [merge-strategies](https://kubernetes.io/docs/reference/using-api/server-side-apply/#merge-strategy)** (e.g. like in the [`crd_derive_schema` example](https://github.com/kube-rs/kube-rs/blob/master/examples/crd_derive_schema.rs))
/// - **customizing [certain kubebuilder like validation rules](https://github.com/kube-rs/kube-rs/issues/129#issuecomment-749463718)** (tail the issue for state of affairs)
///
/// See [kubernetes openapi validation](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation) for the format of the OpenAPI v3 schemas.
///
/// If you have to override a lot, [you can opt-out of schema-generation entirely](https://github.com/kube-rs/kube-rs/issues/355#issuecomment-751253657)
///
/// ## Advanced Features
/// - **embedding k8s-openapi types** can be done by enabling the `schemars` feature of `k8s-openapi` from [`0.13.0`](https://github.com/Arnavion/k8s-openapi/blob/master/CHANGELOG.md#v0130-2021-08-09)
/// - **adding validation** via [validator crate](https://github.com/Keats/validator) is supported from `schemars` >= [`0.8.5`](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md#085---2021-09-20) - you must derive `Validate`
///
/// ## Debugging
/// Try `cargo-expand` to see your own macro expansion.
Expand Down

0 comments on commit 7ed2fcf

Please sign in to comment.