Skip to content

🐛 fix(crd): +kubebuilder:default={} now includes nested field defaults in generated CRDs#1392

Open
camilamacedo86 wants to merge 1 commit into
kubernetes-sigs:mainfrom
camilamacedo86:fix-issue-nested-values
Open

🐛 fix(crd): +kubebuilder:default={} now includes nested field defaults in generated CRDs#1392
camilamacedo86 wants to merge 1 commit into
kubernetes-sigs:mainfrom
camilamacedo86:fix-issue-nested-values

Conversation

@camilamacedo86
Copy link
Copy Markdown
Member

@camilamacedo86 camilamacedo86 commented Apr 27, 2026

Problem

Using +kubebuilder:default={} on an object field does not include nested defaults in the generated CRD schema. Then, CRD shows default: {} instead of a populated object with merged nested defaults.

Therefore, users looking at the CRD schema cannot see what default values will actually be applied, leading to confusion and bug reports (see #622).


Example

Go

type Spec struct {
    // +kubebuilder:default={}
    Config *PolicyConfig `json:"config,omitempty"`
}

type PolicyConfig struct {
    // +kubebuilder:default=20
    RateLimit int32 `json:"rateLimit,omitempty"`

    // +kubebuilder:default=50
    MaxFileSize int32 `json:"maxFileSize,omitempty"`
}

Before (broken)

config:
  type: object
  default: {}   # empty
  properties:
    rateLimit:
      type: integer
      default: 20
    maxFileSize:
      type: integer
      default: 50

After (fixed)

config:
  type: object
  default:
    rateLimit: 20
    maxFileSize: 50
  properties:
    rateLimit:
      type: integer
      default: 20
    maxFileSize:
      type: integer
      default: 50

Fix

When a schema has default: {} (empty object):

  1. Walk the schema tree using the visitor pattern
  2. Collect nested property defaults
  3. Merge them into the parent default object
  4. Preserve both parent and nested defaults (required for K8s recursive defaulting)

Result: users get a fully populated default object.

For users regenerating CRDs:

When you regenerate CRDs with this version, you'll see a one-time diff showing merged defaults:

    config:                                                 
  -   default: {}                                                                               
  +   default:
  +     rateLimit: 20                                                                           
  +     maxFileSize: 50             

This is cosmetic only - runtime behaviour is unchanged. Review and commit the updated CRD.

No action required for existing resources - they continue to work unchange

Fixes: #622

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Apr 27, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: camilamacedo86
Once this PR has been reviewed and has the lgtm label, please assign alvaroaleman for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Apr 27, 2026
@sbueringer
Copy link
Copy Markdown
Member

This seems like a breaking change to me.

Additional I think the CRDs we generate already behave this way as the apiserver recursively defaults top down

@camilamacedo86 camilamacedo86 force-pushed the fix-issue-nested-values branch from a1617c1 to 61c75f1 Compare April 29, 2026 10:54
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Apr 29, 2026
@camilamacedo86 camilamacedo86 force-pushed the fix-issue-nested-values branch 2 times, most recently from a6d092b to a393310 Compare April 29, 2026 11:19
@camilamacedo86 camilamacedo86 force-pushed the fix-issue-nested-values branch from a393310 to 92bcaf9 Compare April 29, 2026 11:21
@camilamacedo86
Copy link
Copy Markdown
Member Author

camilamacedo86 commented Apr 29, 2026

Hi @sbueringer,

This seems like a breaking change to me.

This is not a breaking change.

The actual runtime behavior stays the same. The Kubernetes API server already applies defaults recursively from top to bottom.

  • Before this PR:
    If a user omits the field, the API server defaults it to {} and then fills in the nested defaults.
    Final result: {rateLimit: 20, maxFileSize: 50}

  • After this PR:
    If a user omits the field, the API server defaults it directly to {rateLimit: 20, maxFileSize: 50}, and still applies nested defaults.
    Final result: {rateLimit: 20, maxFileSize: 50}

So the end result is identical.

What actually changes is only the generated CRD YAML:

  • Before: default: {}
  • After: shows the full default values explicitly

This just makes the defaults easier to understand.

Additionally I think the CRDs we generate already behave this way as the apiserver recursively defaults top down

Yes, exactly — this PR doesn’t change that behavior. It just makes it visible in the schema and avoids confusion (as described in #622).

Before, users would see default: {} and think nested defaults weren’t working.
Now they can clearly see what values will be applied.

I also added tests to confirm this behaviour, and that is not a breaking change.

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

+kubebuilder:default={} does not use nested defaults

3 participants