chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,155 @@
use kube_derive::CustomResource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(group = "clux.dev", version = "v1", kind = "FooEnum")]
#[serde(rename_all = "camelCase")]
#[allow(clippy::enum_variant_names)]
enum FooEnumSpec {
/// First variant with an int
VariantOne { int: i32 },
/// Second variant with an String
VariantTwo { str: String },
/// Third variant which doesn't has an attribute
VariantThree {},
}
#[test]
fn test_crd_name() {
use kube::core::CustomResourceExt;
assert_eq!("fooenums.clux.dev", FooEnum::crd_name());
}
#[test]
fn test_serialized_matches_expected() {
assert_eq!(
serde_json::to_value(FooEnum::new("bar", FooEnumSpec::VariantOne { int: 42 })).unwrap(),
serde_json::json!({
"apiVersion": "clux.dev/v1",
"kind": "FooEnum",
"metadata": {
"name": "bar",
},
"spec": {
"variantOne": {
"int": 42
}
}
})
);
assert_eq!(
serde_json::to_value(FooEnum::new("bar", FooEnumSpec::VariantThree {})).unwrap(),
serde_json::json!({
"apiVersion": "clux.dev/v1",
"kind": "FooEnum",
"metadata": {
"name": "bar",
},
"spec": {
"variantThree": {}
}
})
);
}
#[test]
fn test_crd_schema_matches_expected() {
use kube::core::CustomResourceExt;
assert_eq!(
FooEnum::crd(),
serde_json::from_value(serde_json::json!({
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"name": "fooenums.clux.dev"
},
"spec": {
"group": "clux.dev",
"names": {
"categories": [],
"kind": "FooEnum",
"plural": "fooenums",
"shortNames": [],
"singular": "fooenum"
},
"scope": "Cluster",
"versions": [
{
"additionalPrinterColumns": [],
"name": "v1",
"schema": {
"openAPIV3Schema": {
"description": "Auto-generated derived type for FooEnumSpec via `CustomResource`",
"properties": {
"spec": {
"oneOf": [
{
"required": [
"variantOne"
]
},
{
"required": [
"variantTwo"
]
},
{
"required": [
"variantThree"
]
}
],
"properties": {
"variantOne": {
"description": "First variant with an int",
"properties": {
"int": {
"format": "int32",
"type": "integer"
}
},
"required": [
"int"
],
"type": "object"
},
"variantThree": {
"description": "Third variant which doesn't has an attribute",
"type": "object"
},
"variantTwo": {
"description": "Second variant with an String",
"properties": {
"str": {
"type": "string"
}
},
"required": [
"str"
],
"type": "object"
}
},
"type": "object"
}
},
"required": [
"spec"
],
"title": "FooEnum",
"type": "object"
}
},
"served": true,
"storage": true,
"subresources": {}
}
]
}
}
))
.unwrap()
);
}

View File

@@ -0,0 +1,436 @@
#![allow(missing_docs)]
#![recursion_limit = "256"]
use assert_json_diff::assert_json_eq;
use chrono::{DateTime, Utc};
use kube::CELSchema;
use kube_derive::CustomResource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
// See `crd_derive_schema` example for how the schema generated from this struct affects defaulting and validation.
#[derive(CustomResource, Serialize, Deserialize, Debug, PartialEq, Clone, CELSchema)]
#[kube(
group = "clux.dev",
version = "v1",
kind = "Foo",
category = "clux",
namespaced,
doc = "Custom resource representing a Foo",
derive = "PartialEq",
shortname = "fo",
shortname = "f",
served = false,
storage = false,
deprecated = "my warning",
selectable = ".spec.nonNullable",
selectable = ".spec.nullable",
annotation("clux.dev", "cluxingv1"),
annotation("clux.dev/firewall", "enabled"),
label("clux.dev", "cluxingv1"),
label("clux.dev/persistence", "disabled"),
rule = Rule::new("self.metadata.name == 'singleton'"),
status = "Status",
scale(
spec_replicas_path = ".spec.replicas",
status_replicas_path = ".status.replicas",
label_selector_path = ".status.labelSelector"
),
)]
#[cel_validate(rule = Rule::new("has(self.nonNullable)"))]
#[serde(rename_all = "camelCase")]
struct FooSpec {
non_nullable: String,
#[serde(default = "default_value")]
non_nullable_with_default: String,
#[serde(skip_serializing_if = "Option::is_none")]
nullable_skipped: Option<String>,
nullable: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default = "default_nullable")]
nullable_skipped_with_default: Option<String>,
#[serde(default = "default_nullable")]
nullable_with_default: Option<String>,
// Using feature `chrono`
timestamp: DateTime<Utc>,
/// This is a complex enum with a description
#[cel_validate(rule = Rule::new("!has(self.variantOne) || self.variantOne.int > 22"))]
complex_enum: ComplexEnum,
/// This is a untagged enum with a description
untagged_enum_person: UntaggedEnumPerson,
set: HashSet<String>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Status {
replicas: usize,
label_selector: String,
}
fn default_value() -> String {
"default_value".into()
}
fn default_nullable() -> Option<String> {
Some("default_nullable".into())
}
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug, JsonSchema)]
#[kube(group = "clux.dev", version = "v1", kind = "Flattening")]
pub struct FlatteningSpec {
foo: String,
#[serde(flatten)]
arbitrary: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[allow(clippy::enum_variant_names)]
enum ComplexEnum {
/// First variant with an int
VariantOne { int: i32 },
/// Second variant with an String
VariantTwo { str: String },
/// Third variant which doesn't has an attribute
VariantThree {},
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
enum UntaggedEnumPerson {
GenderAndAge(GenderAndAge),
GenderAndDateOfBirth(GenderAndDateOfBirth),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GenderAndAge {
/// Gender of the person
gender: Gender,
/// Age of the person in years
age: i32,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GenderAndDateOfBirth {
/// Gender of the person
gender: Gender,
/// Date of birth of the person as ISO 8601 date
date_of_birth: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
enum Gender {
Female,
Male,
/// This variant has a comment!
Other,
}
#[test]
fn test_crd_name() {
use kube::core::CustomResourceExt;
assert_eq!("foos.clux.dev", Foo::crd_name());
}
#[test]
fn test_shortnames() {
use kube::core::CustomResourceExt;
assert_eq!(&["fo", "f"], Foo::shortnames());
}
#[test]
fn test_serialized_matches_expected() {
assert_json_eq!(
serde_json::to_value(Foo::new("bar", FooSpec {
non_nullable: "asdf".to_string(),
non_nullable_with_default: "asdf".to_string(),
nullable_skipped: None,
nullable: None,
nullable_skipped_with_default: None,
nullable_with_default: None,
timestamp: DateTime::from_timestamp(0, 0).unwrap(),
complex_enum: ComplexEnum::VariantOne { int: 23 },
untagged_enum_person: UntaggedEnumPerson::GenderAndAge(GenderAndAge {
age: 42,
gender: Gender::Male,
}),
set: HashSet::from(["foo".to_owned()])
}))
.unwrap(),
serde_json::json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": {
"annotations": {
"clux.dev": "cluxingv1",
"clux.dev/firewall": "enabled",
},
"labels": {
"clux.dev": "cluxingv1",
"clux.dev/persistence": "disabled",
},
"name": "bar",
},
"spec": {
"nonNullable": "asdf",
"nonNullableWithDefault": "asdf",
"nullable": null,
"nullableWithDefault": null,
"timestamp": "1970-01-01T00:00:00Z",
"complexEnum": {
"variantOne": {
"int": 23
}
},
"untaggedEnumPerson": {
"age": 42,
"gender": "Male"
},
"set": ["foo"]
}
})
)
}
#[test]
fn test_crd_schema_matches_expected() {
use kube::core::CustomResourceExt;
assert_json_eq!(
Foo::crd(),
serde_json::json!({
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"annotations": {
"clux.dev": "cluxingv1",
"clux.dev/firewall": "enabled",
},
"labels": {
"clux.dev": "cluxingv1",
"clux.dev/persistence": "disabled",
},
"name": "foos.clux.dev"
},
"spec": {
"group": "clux.dev",
"names": {
"categories": ["clux"],
"kind": "Foo",
"plural": "foos",
"shortNames": ["fo", "f"],
"singular": "foo"
},
"scope": "Namespaced",
"versions": [
{
"name": "v1",
"served": false,
"storage": false,
"deprecated": true,
"deprecationWarning": "my warning",
"additionalPrinterColumns": [],
"selectableFields": [{
"jsonPath": ".spec.nonNullable"
}, {
"jsonPath": ".spec.nullable"
}],
"subresources": {
"status": {},
"scale": {
"specReplicasPath": ".spec.replicas",
"labelSelectorPath": ".status.labelSelector",
"statusReplicasPath": ".status.replicas"
}
},
"schema": {
"openAPIV3Schema": {
"description": "Custom resource representing a Foo",
"properties": {
"spec": {
"properties": {
"nonNullable": {
"type": "string"
},
"nonNullableWithDefault": {
"default": "default_value",
"type": "string"
},
"nullableSkipped": {
"nullable": true,
"type": "string"
},
"nullable": {
"nullable": true,
"type": "string"
},
"nullableSkippedWithDefault": {
"default": "default_nullable",
"nullable": true,
"type": "string"
},
"nullableWithDefault": {
"default": "default_nullable",
"nullable": true,
"type": "string"
},
"timestamp": {
"type": "string",
"format": "date-time"
},
"complexEnum": {
"type": "object",
"properties": {
"variantOne": {
"type": "object",
"properties": {
"int": {
"type": "integer",
"format": "int32"
}
},
"required": ["int"],
"description": "First variant with an int"
},
"variantTwo": {
"type": "object",
"properties": {
"str": {
"type": "string"
}
},
"required": ["str"],
"description": "Second variant with an String"
},
"variantThree": {
"type": "object",
"description": "Third variant which doesn't has an attribute"
}
},
"oneOf": [
{
"required": ["variantOne"]
},
{
"required": ["variantTwo"]
},
{
"required": ["variantThree"]
}
],
"x-kubernetes-validations": [{
"rule": "!has(self.variantOne) || self.variantOne.int > 22",
}],
"description": "This is a complex enum with a description"
},
"untaggedEnumPerson": {
"type": "object",
"properties": {
"age": {
"type": "integer",
"format": "int32",
"description": "Age of the person in years"
},
"dateOfBirth": {
"type": "string",
"description": "Date of birth of the person as ISO 8601 date"
},
"gender": {
"type": "string",
"enum": ["Female", "Male", "Other"],
"description": "Gender of the person"
}
},
"anyOf": [
{
"required": ["age", "gender"]
},
{
"required": ["dateOfBirth", "gender"]
}
],
"description": "This is a untagged enum with a description"
},
"set": {
"type": "array",
"items": {
"type": "string"
},
},
},
"required": [
"complexEnum",
"nonNullable",
"set",
"timestamp",
"untaggedEnumPerson"
],
"x-kubernetes-validations": [{
"rule": "has(self.nonNullable)",
}],
"type": "object"
},
"status": {
"properties": {
"replicas": {
"type": "integer",
"format": "uint",
"minimum": 0.0,
},
"labelSelector": {
"type": "string"
}
},
"required": [
"labelSelector",
"replicas"
],
"nullable": true,
"type": "object"
}
},
"required": [
"spec"
],
"x-kubernetes-validations": [{
"rule": "self.metadata.name == 'singleton'",
}],
"title": "Foo_kube_validation",
"type": "object"
}
},
}
]
}
})
);
}
#[test]
fn flattening() {
use kube::core::CustomResourceExt;
let spec = &Flattening::crd().spec.versions[0]
.schema
.clone()
.unwrap()
.open_api_v3_schema
.unwrap()
.properties
.unwrap()["spec"];
assert_eq!(spec.x_kubernetes_preserve_unknown_fields, Some(true));
assert_eq!(spec.additional_properties, None);
}

55
vendor/kube-derive/tests/resource.rs vendored Normal file
View File

@@ -0,0 +1,55 @@
use k8s_openapi::{
api::core::v1::{ConfigMap, Secret},
ByteString,
};
use kube::api::ObjectMeta;
use kube_derive::Resource;
#[derive(Resource, Default)]
#[resource(inherit = "ConfigMap")]
struct TypedMap {
metadata: ObjectMeta,
data: Option<TypedData>,
}
#[derive(Default)]
struct TypedData {
field: String,
}
#[derive(Resource, Default)]
#[resource(inherit = "Secret")]
struct TypedSecret {
metadata: ObjectMeta,
data: Option<TypedSecretData>,
}
#[derive(Default)]
struct TypedSecretData {
field: ByteString,
}
#[cfg(test)]
mod tests {
use kube::Resource;
use crate::{TypedMap, TypedSecret};
#[test]
fn test_parse_config_map_default() {
TypedMap::default();
assert_eq!(TypedMap::kind(&()), "ConfigMap");
assert_eq!(TypedMap::api_version(&()), "v1");
assert_eq!(TypedMap::group(&()), "");
assert_eq!(TypedMap::plural(&()), "configmaps");
}
#[test]
fn test_parse_secret_default() {
TypedSecret::default();
assert_eq!(TypedSecret::kind(&()), "Secret");
assert_eq!(TypedSecret::api_version(&()), "v1");
assert_eq!(TypedSecret::group(&()), "");
assert_eq!(TypedSecret::plural(&()), "secrets");
}
}

10
vendor/kube-derive/tests/test_ui.rs vendored Normal file
View File

@@ -0,0 +1,10 @@
// Test that `kube-derive` outputs helpful error messages.
// If you make a change, remove `tests/ui/*.stderr` and run `cargo test`.
// Then copy the files that appear under `wip/` if it's what you expected.
// Alternatively, run `TRYBUILD=overwrite cargo test`.
// See https://github.com/dtolnay/trybuild
#[test]
fn test_failures() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/*.rs");
}

View File

@@ -0,0 +1,11 @@
use kube_derive::CustomResource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(group = "clux.dev", version = "v1", kind = "Foo", shortnames = "foo")]
struct FooSpec {
foo: String,
}
fn main() {}

View File

@@ -0,0 +1,5 @@
error: Unknown field: `shortnames`. Did you mean `shortname`?
--> $DIR/fail_with_suggestion.rs:6:58
|
6 | #[kube(group = "clux.dev", version = "v1", kind = "Foo", shortnames = "foo")]
| ^^^^^^^^^^

View File

@@ -0,0 +1,10 @@
use kube_derive::CustomResource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
struct FooSpec {
foo: String,
}
fn main() {}

View File

@@ -0,0 +1,23 @@
error: Missing field `group`
--> $DIR/missing_required.rs:5:10
|
5 | #[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
| ^^^^^^^^^^^^^^
|
= note: this error originates in the derive macro `CustomResource` (in Nightly builds, run with -Z macro-backtrace for more info)
error: Missing field `version`
--> $DIR/missing_required.rs:5:10
|
5 | #[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
| ^^^^^^^^^^^^^^
|
= note: this error originates in the derive macro `CustomResource` (in Nightly builds, run with -Z macro-backtrace for more info)
error: Missing field `kind`
--> $DIR/missing_required.rs:5:10
|
5 | #[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
| ^^^^^^^^^^^^^^
|
= note: this error originates in the derive macro `CustomResource` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@@ -0,0 +1,10 @@
use kube_derive::CustomResource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(CustomResource, Serialize, Deserialize, JsonSchema)]
union FooSpec {
int: u32,
}
fn main() {}

View File

@@ -0,0 +1,13 @@
error: Unions can not #[derive(CustomResource)]
--> tests/ui/union_fails.rs:6:7
|
6 | union FooSpec {
| ^^^^^^^
error: Serde does not support derive for unions
--> tests/ui/union_fails.rs:6:1
|
6 | / union FooSpec {
7 | | int: u32,
8 | | }
| |_^