Skip to content

fix(derive): support container-level serde(default) in KubeSchema#1999

Open
mrthinger wants to merge 1 commit into
kube-rs:mainfrom
mrthinger:fix-kubeschema-container-serde-default
Open

fix(derive): support container-level serde(default) in KubeSchema#1999
mrthinger wants to merge 1 commit into
kube-rs:mainfrom
mrthinger:fix-kubeschema-container-serde-default

Conversation

@mrthinger

@mrthinger mrthinger commented Jun 10, 2026

Copy link
Copy Markdown

Motivation

fix #1805

Combining KubeSchema with a bare container-level #[serde(default)] fails to compile. The derive generates mirror structs ({Name}Validated, plus a per-field Validated wrapper) to compute the schema, and copies the container serde attributes onto them. schemars turns the container default into <Self as Default>::default() calls on those mirrors, which don't implement Default, so the build fails with no function or associated item named `default` found for struct `MyCrdSpecValidated` .

Solution

Detect a bare container-level serde(default) and emit delegating Default impls for the generated mirrors. Each impl constructs the original type's default and moves the values into the mirror.

Since the per-field wrappers each construct the full original default and take one field from it, the original Default runs once per x_kube field (plus once for the full mirror) while generating the schema. This matches how serde sources missing fields from a container default, and only happens when the CRD is generated.

The default = "path" form already works, since schemars resolves the given function directly. A new test pins that behavior.

@mrthinger mrthinger force-pushed the fix-kubeschema-container-serde-default branch from af73f3b to a9e3f18 Compare June 10, 2026 19:03
A bare container-level #[serde(default)] makes the schemars derive call
<Self as Default>::default() on the mirror structs that KubeSchema
generates to compute its schema (StructNameValidated and the per-field
Validated wrappers). Those mirrors have no Default impls, so combining
x_kube validations with a container serde default failed to compile
with "no function or associated item named `default`".

Detect the bare container default and emit delegating Default impls for
the generated mirrors: each calls the original type's Default once and
moves the resulting values into the mirror. The default = "path" form
is unaffected (schemars resolves the function directly); a test pins
that it keeps working.

Fixes kube-rs#1805

Signed-off-by: Evan Pierce <evan@mrthinger.com>
@mrthinger mrthinger force-pushed the fix-kubeschema-container-serde-default branch from 71d078e to 64c0332 Compare June 10, 2026 19:12
@mrthinger mrthinger marked this pull request as ready for review June 10, 2026 19:14
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.33333% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.9%. Comparing base (9981f58) to head (64c0332).
⚠️ Report is 24 commits behind head on main.

Files with missing lines Patch % Lines
kube-derive/src/cel_schema.rs 56.3% 7 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##            main   #1999     +/-   ##
=======================================
+ Coverage   76.8%   76.9%   +0.1%     
=======================================
  Files         89      89             
  Lines       8813    8855     +42     
=======================================
+ Hits        6764    6801     +37     
- Misses      2049    2054      +5     
Files with missing lines Coverage Δ
kube-derive/tests/crd_schema_test.rs 100.0% <100.0%> (ø)
kube-derive/src/cel_schema.rs 86.5% <56.3%> (-7.4%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@clux clux requested a review from Danil-Grigorev June 10, 2026 22:37

@Danil-Grigorev Danil-Grigorev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! Looks good overall, nice change!

Just one request for test coverage, and naming for these macros. It would help longer term to orient in these changes.

#[serde(rename_all = "camelCase", default)]
struct ContainerDefaultSpec {
#[x_kube(validation = "self % 2 == 1")]
replica_count: i32,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add another field to the structure, so that test validates multiple defaulted values?

#[automatically_derived]
impl #std::default::Default for #generated {
fn default() -> Self {
let original = #original;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is a bit confusing, since this is actually unwrapped original_default, I think it is better to shadow original_default with unwrapped value in this scope, or give it different name, like #default. Macros are harder to read overall, but this should improve it a bit.

Suggested change
let original = #original;
let original_default = #default;

#[automatically_derived]
impl #std::default::Default for Validated {
fn default() -> Self {
let original = #original;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as in https://github.com/kube-rs/kube/pull/1999/changes#r3407680513,

Suggested change
let original = #original;
let original_default = #default;

@clux clux added this to the 4.1.0 milestone Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

serde(default) and KubeSchema - no function or associated item named default

3 participants