diff --git a/examples/crd_derive_schema.rs b/examples/crd_derive_schema.rs index 24013f4d6..a95a42262 100644 --- a/examples/crd_derive_schema.rs +++ b/examples/crd_derive_schema.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use futures::{StreamExt, TryStreamExt}; use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; use kube::{ - api::{Api, DeleteParams, ListParams, PostParams, Resource, WatchEvent}, + api::{Api, DeleteParams, ListParams, Patch, PatchParams, PostParams, Resource, WatchEvent}, Client, CustomResource, }; use schemars::JsonSchema; @@ -73,6 +73,36 @@ pub struct FooSpec { // If the resource is created with `kubectl` and if this field was missing, defaulting will happen. #[serde(default = "default_nullable")] nullable_with_default: Option, + + /// Default listable field + #[serde(default)] + default_listable: Vec, + + // Listable field with specified set merge strategy + #[serde(default)] + set_listable: SetListable, +} +#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)] +struct SetListable(Vec); +// https://kubernetes.io/docs/reference/using-api/server-side-apply/#merge-strategy +use schemars::{gen::SchemaGenerator, schema::Schema}; +impl JsonSchema for SetListable { + fn schema_name() -> String { + "SetListable".into() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + serde_json::from_value(serde_json::json!({ + "type": "array", + "items": { + "format": "u32", + "minium": 0, + "type": "integer" + }, + "x-kubernetes-list-type": "set" + })) + .unwrap() + } } fn default_value() -> String { @@ -116,6 +146,9 @@ async fn main() -> Result<()> { // Defaulting did not happen because `null` was sent. // Deserialization does not apply the default either. nullable_with_default: None, + // Listables all default + default_listable: Default::default(), + set_listable: Default::default(), }); // Set up dynamic resource to test using raw values. @@ -145,6 +178,7 @@ async fn main() -> Result<()> { "spec": { "non_nullable": "a required field", // `non_nullable_with_default` field is missing + "set_listable": SetListable(vec![2]), // a value patch later } }))?; let val = client @@ -153,6 +187,7 @@ async fn main() -> Result<()> { println!("{:?}", val["spec"]); // Defaulting happened for non-nullable field assert_eq!(val["spec"]["non_nullable_with_default"], default_value()); + //assert_eq!(val["spec"]["atomic_listable"], "[2]"); // Missing required field (non-nullable without default) is an error let data = serde_json::to_vec(&serde_json::json!({ @@ -180,6 +215,19 @@ async fn main() -> Result<()> { _ => assert!(false), } + // Test the manually specified merge strategy + let ssapply = PatchParams::apply("crd_derive_schema_example").force(); + let patch = serde_json::json!({ + "apiVersion": "clux.dev/v1", + "kind": "Foo", + "spec": { + "set_listable": SetListable(vec![3]) + } + }); + let pres = foos.patch("baz", &ssapply, &Patch::Apply(patch)).await?; + assert_eq!(pres.spec.set_listable, SetListable(vec![2, 3])); + println!("{:?}", serde_json::to_value(pres.spec)); + delete_crd(client.clone()).await?; Ok(())