preserve unknown kubeconfig fields via serde(flatten)#1964
Merged
clux merged 3 commits intoMar 27, 2026
Conversation
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>
534af76 to
5f7da0d
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 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
🚀 New features to boost your workflow:
|
clux
approved these changes
Mar 26, 2026
clux
left a comment
Member
There was a problem hiding this comment.
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>
eleboucher
pushed a commit
to eleboucher/towonel
that referenced
this pull request
Jun 18, 2026
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [kube](https://github.com/kube-rs/kube) | dependencies | major | `3.1` → `4.0` | --- ### Release Notes <details> <summary>kube-rs/kube (kube)</summary> ### [`v4.0.0`](https://github.com/kube-rs/kube/blob/HEAD/CHANGELOG.md#400--2026-06-16) [Compare Source](kube-rs/kube@3.1.0...4.0.0) \=================== <!-- Release notes generated using configuration in .github/release.yml at 4.0.0 --> #### New Major As per the release schedule to match up with the [latest Kubernetes ハル release](https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/). Lots of fixes and improvements. Thanks to everyone who contributed! #### Kubernetes `v1_36` support via k8s-openapi [0.28](https://github.com/Arnavion/k8s-openapi/releases/tag/v0.28.0) Please [upgrade k8s-openapi along with kube](https://kube.rs/upgrading/) to avoid conflicts. #### CEL Validation A new optional crate [kube-cel](https://docs.rs/kube-cel/latest/kube_cel/) is being re-exported through `kube::core::cel` via [#​1954](kube-rs/kube#1954) Kubernetes CRDs support [CEL validation rules](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules) via `x-kubernetes-validations`, and were supported from 3.0 via [`KubeSchema`](https://docs.rs/kube/latest/kube/derive.KubeSchema.html), but these rules could only be evaluated server-side by the API server. The new crate allows evaluating these rules locally using rules matching the [upstream Kubernetes CEL libraries](https://docs.rs/kube/latest/kube/core/cel/trait.KubeCelExt.html#upstream-sources). While low-level, a higher-level CEL validator integrates with [`CustomResource`](https://docs.rs/kube/latest/kube/derive.CustomResource.html) via [`#[kube(cel)]`](https://docs.rs/kube/latest/kube/derive.CustomResource.html#cel-validation-client-side) from [#​2011](kube-rs/kube#2011) and can be used as; ```rust #[derive(CustomResource, Serialize, Deserialize, Clone, KubeSchema)] #[kube(group = "example.com", version = "v1", kind = "Foo", namespaced)] #[kube(cel, validation = "self.spec.replicas >= 0")] // cel trigger + validation rule struct FooSpec { replicas: i32 } let foo = Foo::new("test", FooSpec { replicas: -1 }); foo.validate_cel()?; // new impl; checks creation rules new_foo.validate_cel_update(&old_foo)?; // new impl; checks transition rules ``` See [examples/crd\_derive\_cel.rs](https://github.com/kube-rs/kube/blob/main/examples/crd_derive_cel.rs) for more details. This is available under the `kube/cel` feature, courtesy of [@​doxxx93](https://github.com/doxxx93). #### Config A lot of improvements to config handling; - better error handling of malformed client certs in [#​1966](kube-rs/kube#1966) - add missing `Kubeconfig` fields in [#​1965](kube-rs/kube#1965) - `Kubeconfig` future key compatibility for new fields by adding catch-all `other` key via [#​1964](kube-rs/kube#1964) - deserialization changed from `serde-yaml` to [`serde-saphyr`](https://github.com/bourumir-wyngs/serde-saphyr) to get rid of the long-deprecated dependency. [#​1975](kube-rs/kube#1975) ##### Retry and Timeouts Better timeout and retry handling to better deal with flaky network conditions, and busy or initializing apiservers. - default global read timeouts has been unset in favor of `watcher` level timeouts in [#​1945](kube-rs/kube#1945) (see [#​1798](kube-rs/kube#1798) for context) - regular (non-watch) queries now respect the [`RetryPolicy`](https://docs.rs/kube/latest/kube/client/retry/struct.RetryPolicy.html) - now enabled by default in [#​2007](kube-rs/kube#2007). ##### Client - properly handling rotating ca certs in cluster via [#​1962](kube-rs/kube#1962) - handle `tls-server-name` with `openssl-tls` via [#​1993](kube-rs/kube#1993) - auth exec: accept `yaml` output from `exec` plugins via [#​2003](kube-rs/kube#2003) - fix `ws` task leak and `drop`, and a deadlock on `join()` via [#​1978](kube-rs/kube#1978) - **change**: client tracing now opt-in due to issues. see [#​1972](kube-rs/kube#1972) ##### Runtime - [`watcher`](https://docs.rs/kube/latest/kube/runtime/watcher/index.html) automatically uses the `metadata_` api methods when called with [`PartialObjectMeta<K>`](https://docs.rs/kube/latest/kube/core/metadata/struct.PartialObjectMeta.html) via [#​1952](kube-rs/kube#1952) - (this deprecates [`metadata_watcher`](https://docs.rs/kube/latest/kube/runtime/watcher/fn.metadata_watcher.html) in favor of an explicit change from `Api::<K>` to `Api::<PartialObjectMeta<K>>`) - added [`wait::conditions::is_created`](https://docs.rs/kube/latest/kube/runtime/wait/conditions/fn.is_deleted.html) as a counter to `is_deleted` [#​2000](kube-rs/kube#2000) - added [`Store::state_filtered`](https://docs.rs/kube/latest/kube/runtime/reflector/struct.Store.html#method.state_filter) and [`Store::state_filter_selector`](https://docs.rs/kube/latest/kube/runtime/reflector/struct.Store.html#method.state_filter_selector) to allow more efficient slicing of the locked cache via [#​2002](kube-rs/kube#2002) + [#​1998](kube-rs/kube#1998) #### What's Changed ##### Added - feat: add typed kubeconfig fields for client-go parity by [@​alex-lapuka](https://github.com/alex-lapuka) in [#​1965](kube-rs/kube#1965) - Add CEL validation via kube-cel re-export by [@​doxxx93](https://github.com/doxxx93) in [#​1954](kube-rs/kube#1954) - Add `AdmissionRequest::to_cel_request()` for VAP CEL bridging by [@​doxxx93](https://github.com/doxxx93) in [#​1991](kube-rs/kube#1991) - runtime: implement `Store::state_with` and `Store::state_filtered` by [@​Alvov1](https://github.com/Alvov1) in [#​1998](kube-rs/kube#1998) - runtime: add `wait::conditions::is_created` helper by [@​orangecms](https://github.com/orangecms) in [#​2000](kube-rs/kube#2000) - refactor(runtime): rename Store::state\_with/state\_filtered per review feedback by [@​Alvov1](https://github.com/Alvov1) in [#​2002](kube-rs/kube#2002) - deps: bump kube-cel to 0.6.1 (validation surface flattened) by [@​doxxx93](https://github.com/doxxx93) in [#​2005](kube-rs/kube#2005) - Enable `RetryPolicy::server_retry` by default for `Client` by [@​Danil-Grigorev](https://github.com/Danil-Grigorev) in [#​2007](kube-rs/kube#2007) - feat(derive): client-side CEL validation via #\[kube(cel)] / #\[x\_kube(cel)] by [@​doxxx93](https://github.com/doxxx93) in [#​2011](kube-rs/kube#2011) ##### Changed - preserve unknown kubeconfig fields via serde(flatten) by [@​alex-lapuka](https://github.com/alex-lapuka) in [#​1964](kube-rs/kube#1964) - Remove global read\_timeout default, add watcher-level idle timeout by [@​doxxx93](https://github.com/doxxx93) in [#​1945](kube-rs/kube#1945) - Update tokio-tungstenite requirement from 0.28.0 to 0.29.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​1963](kube-rs/kube#1963) - convert from serde-yaml to serde-saphyr by [@​clux](https://github.com/clux) in [#​1975](kube-rs/kube#1975) - features: making client tracing opt-in by [@​mattklein123](https://github.com/mattklein123) in [#​1972](kube-rs/kube#1972) - client: reload in-cluster CA bundle on rotation (rustls-tls) by [@​chrnorm](https://github.com/chrnorm) in [#​1962](kube-rs/kube#1962) - Api\<PartialObjectMeta<K>> should opportunistically degrade to metadata requests by [@​doxxx93](https://github.com/doxxx93) in [#​1952](kube-rs/kube#1952) - Chore(deps): Update garde requirement from 0.22.0 to 0.23.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​1989](kube-rs/kube#1989) - bump k8s-openapi to 0.28 by [@​clux](https://github.com/clux) in [#​2009](kube-rs/kube#2009) - Box a large runtime error in ReconcilerErr by [@​clux](https://github.com/clux) in [#​1880](kube-rs/kube#1880) ##### Fixed - fix: feature-flag CREATE\_NO\_WINDOW to not break stderr inheritance by [@​cristeigabriela](https://github.com/cristeigabriela) in [#​1971](kube-rs/kube#1971) - Remove silent error when client-key/client-certificate is malformed by [@​goenning](https://github.com/goenning) in [#​1966](kube-rs/kube#1966) - Fix AttachedProcess task leak on drop and join() deadlock by [@​SebTardif](https://github.com/SebTardif) in [#​1978](kube-rs/kube#1978) - support auth exec yaml output by [@​aviramha](https://github.com/aviramha) in [#​2003](kube-rs/kube#2003) - fix(client): apply tls-server-name on the openssl-tls path by [@​dgunzy](https://github.com/dgunzy) in [#​1993](kube-rs/kube#1993) - Use the resource's own name for the schema title by [@​cehoffman](https://github.com/cehoffman) in [#​1985](kube-rs/kube#1985) - [@​alex-lapuka](https://github.com/alex-lapuka) made their first contribution in [#​1965](kube-rs/kube#1965) - [@​cristeigabriela](https://github.com/cristeigabriela) made their first contribution in [#​1971](kube-rs/kube#1971) - [@​mattklein123](https://github.com/mattklein123) made their first contribution in [#​1972](kube-rs/kube#1972) - [@​chrnorm](https://github.com/chrnorm) made their first contribution in [#​1962](kube-rs/kube#1962) - [@​SebTardif](https://github.com/SebTardif) made their first contribution in [#​1978](kube-rs/kube#1978) - [@​Alvov1](https://github.com/Alvov1) made their first contribution in [#​1998](kube-rs/kube#1998) - [@​orangecms](https://github.com/orangecms) made their first contribution in [#​2000](kube-rs/kube#2000) - [@​dgunzy](https://github.com/dgunzy) made their first contribution in [#​1993](kube-rs/kube#1993) - [@​cehoffman](https://github.com/cehoffman) made their first contribution in [#​1985](kube-rs/kube#1985) **Full Changelog**: <kube-rs/kube@3.1.0...4.0.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL21ham9yIl19--> Reviewed-on: https://codeberg.org/towonel/towonel/pulls/29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Kubeconfigstructs are silently dropped. Thiscauses data loss for users with:
installHint(present in upstream Go client-go but missing here)The existing
extensions: Option<Vec<NamedExtension>>fields only capture the structuredextensionskey — they don't preserve arbitrary unknown fields on each struct.Changes
#[serde(flatten)]catch-all on all kubeconfig structsAdded
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::SchemaObjectwhich already uses#[serde(flatten)] extensions: BTreeMap<String, Value>for the same purpose.Fields like
installHintonExecConfig(present in upstream Go client-go but not modeledin kube-rs) are now automatically captured in the
othermap without needing dedicatedtyped fields for each one.
Defaultderive for additional structsAdded
Defaultderive toExecConfig,Preferences, andAuthProviderConfig. Thesestructs previously lacked
Default, making it harder to construct them with..Default::default().Merge logic
Updated
Kubeconfig::merge()to merge theothermaps using first-wins-per-keysemantics, consistent with the Kubernetes kubeconfig merge specification.
Round-trip test
Added
kubeconfig_round_trip_preserves_unknown_fieldstest that verifies unknown fieldsat 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,AuthProviderConfigare never constructed with structliterals (only deserialized) — zero downstream impact
Kubeconfig,AuthInfo,Cluster,Contextall deriveDefaultand downstream codeoverwhelmingly uses
..Default::default()NamedCluster,NamedAuthInfo,NamedContextare simple wrappers — the fix is adding..Default::default()to any explicit construction siteThe fix for any affected downstream code is mechanical: add
..Default::default()tostruct literal constructions.
Testing