Skip to content

preserve unknown kubeconfig fields via serde(flatten)#1964

Merged
clux merged 3 commits into
kube-rs:mainfrom
alex-lapuka:feature/non-standard-fields-support
Mar 27, 2026
Merged

preserve unknown kubeconfig fields via serde(flatten)#1964
clux merged 3 commits into
kube-rs:mainfrom
alex-lapuka:feature/non-standard-fields-support

Conversation

@alex-lapuka
Copy link
Copy Markdown
Contributor

@alex-lapuka alex-lapuka commented Mar 25, 2026

Preserve unknown kubeconfig fields via serde(flatten)

Motivation

When tools read a kubeconfig, modify it (e.g. add a context), and write it back, any
fields not explicitly modeled in the Kubeconfig structs are silently dropped. This
causes data loss for users with:

  • Exec plugin fields like installHint (present in upstream Go client-go but missing here)
  • Cloud-provider-specific or vendor-specific extensions
  • Future Kubernetes fields not yet modeled in kube-rs

The existing extensions: Option<Vec<NamedExtension>> fields only capture the structured
extensions key — they don't preserve arbitrary unknown fields on each struct.

Changes

#[serde(flatten)] catch-all on all kubeconfig structs

Added pub other: BTreeMap<String, serde_json::Value> with #[serde(flatten)] to:
Kubeconfig, Preferences, NamedCluster, Cluster, NamedAuthInfo, AuthInfo,
AuthProviderConfig, ExecConfig, NamedContext, Context.

This follows the existing pattern in kube-core::schema::SchemaObject which already uses
#[serde(flatten)] extensions: BTreeMap<String, Value> for the same purpose.

Fields like installHint on ExecConfig (present in upstream Go client-go but not modeled
in kube-rs) are now automatically captured in the other map without needing dedicated
typed fields for each one.

Default derive for additional structs

Added Default derive to ExecConfig, Preferences, and AuthProviderConfig. These
structs previously lacked Default, making it harder to construct them with
..Default::default().

Merge logic

Updated Kubeconfig::merge() to merge the other maps using first-wins-per-key
semantics, consistent with the Kubernetes kubeconfig merge specification.

Round-trip test

Added kubeconfig_round_trip_preserves_unknown_fields test that verifies unknown fields
at every level (top-level, cluster, context, exec) survive a deserialize→serialize cycle.

Breaking changes

Adding a public field to these structs is technically a semver-breaking change for code
that constructs them with struct literal syntax without ..Default::default(). However:

  • ExecConfig, Preferences, AuthProviderConfig are never constructed with struct
    literals (only deserialized) — zero downstream impact
  • Kubeconfig, AuthInfo, Cluster, Context all derive Default and downstream code
    overwhelmingly uses ..Default::default()
  • NamedCluster, NamedAuthInfo, NamedContext are simple wrappers — the fix is adding
    ..Default::default() to any explicit construction site

The fix for any affected downstream code is mechanical: add ..Default::default() to
struct literal constructions.

Testing

  • All existing unit tests pass (10 file_config + 45 kube-client lib + 52 doc tests)
  • New round-trip preservation test validates the core behavior
  • Verified against a downstream consumer that reads/modifies/writes kubeconfigs

Add `#[serde(flatten)] pub other: BTreeMap<String, serde_json::Value>`
to all kubeconfig structs (Kubeconfig, Cluster, AuthInfo, ExecConfig,
Context, Preferences, AuthProviderConfig, and Named* wrappers) so that
unmodeled fields survive deserialization and can be serialized back
without data loss.

Also derive Default for ExecConfig, Preferences, and AuthProviderConfig.

Update Kubeconfig::merge() to merge extra fields with first-wins-per-key
semantics. Add a round-trip test verifying unknown fields are preserved
across deserialize/serialize cycles.

Signed-off-by: Alexey Lapuka <alexey@twingate.com>
@alex-lapuka alex-lapuka force-pushed the feature/non-standard-fields-support branch from 534af76 to 5f7da0d Compare March 25, 2026 22:47
@clux clux added this to the 4.0.0 milestone Mar 26, 2026
@clux clux added the changelog-change changelog change category for prs label Mar 26, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.5%. Comparing base (288053e) to head (a850811).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##            main   #1964     +/-   ##
=======================================
+ Coverage   76.4%   76.5%   +0.2%     
=======================================
  Files         89      89             
  Lines       8540    8566     +26     
=======================================
+ Hits        6520    6550     +30     
+ Misses      2020    2016      -4     
Files with missing lines Coverage Δ
kube-client/src/client/mod.rs 77.6% <ø> (ø)
kube-client/src/config/file_config.rs 80.6% <100.0%> (+3.0%) ⬆️

... 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.

Copy link
Copy Markdown
Member

@clux clux left a comment

Choose a reason for hiding this comment

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

Thanks for this. I think this is a good solution that makes us immediately compatible with new fields.

One small downside though, is that this may cause people to not bother contributing new standard fields when they appear in client-go, and ideally this should be a fallback method to access such fields until a release can be made.

I wonder if it makes sense to word something like a "if you are relying on this for standard fields present in upstream client-go, feel free to submit a PR". wdyt?

…consumers relying on standard client-go fields should submit PRs to add them as typed fields rather than using the generic fallback.

Also update the round-trip test to use only non-standard field names to avoid collision when standard fields like installHint are later added as typed fields.

Signed-off-by: Alexey Lapuka <alexey@twingate.com>
@clux clux changed the title feat: preserve unknown kubeconfig fields via serde(flatten) preserve unknown kubeconfig fields via serde(flatten) Mar 27, 2026
Copy link
Copy Markdown
Member

@clux clux left a comment

Choose a reason for hiding this comment

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

Thank you. Looks great.

@clux clux enabled auto-merge (squash) March 27, 2026 11:49
@clux clux disabled auto-merge March 27, 2026 11:51
@clux clux merged commit cfa38f2 into kube-rs:main Mar 27, 2026
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog-change changelog change category for prs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants