-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Don't call Marshal in unmarshalerEmbeddedStructsHookFunc #14213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Don't call Marshal in unmarshalerEmbeddedStructsHookFunc #14213
Conversation
CodSpeed Performance ReportMerging #14213 will not alter performanceComparing
|
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (89.47%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #14213 +/- ##
==========================================
- Coverage 92.14% 92.13% -0.01%
==========================================
Files 666 666
Lines 41441 41463 +22
==========================================
+ Hits 38186 38203 +17
- Misses 2216 2219 +3
- Partials 1039 1041 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
mx-psi
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to fix the issue that motivated this over at #14203
I would be okay with
breaking unmarshaling of structs with multiple identically-named fields
I am a bit worried about how to test this. Are there any tests we could add here for this?
|
There are already tests for the current embedded unmarshaler behavior, but I think it would be worth adding one using |
Context
confmapuses amapstructurehook to call theUnmarshalmethod on types which define it as part of the unmarshaling process, butmapstructurehooks are not called for squashed fields, so we have a separate hook which manually inspects all structs for squashed fields that implementUnmarshaler.This second hook currently returns a modified version of the raw YAML config, to be used by
mapstructureto finish unmarshaling the full struct. This is a problem, because anUnmarshalmethod can set the state of the struct in arbitrary ways, which we want to preserve withoutmapstructureoverwriting fields. (SeeTestEmbeddedUnmarshaler)The way this is currently worked around is by marshaling the embedded struct into a map, and then copying that map back into the raw config, in the hope that nothing will change when the raw config is mapped back into the struct.
However, this assumption breaks when dealing with a
configopaque.Stringinside an embeddedUnmarshaler. That type has the very deliberate behavior of returning[REDACTED]when marshaled instead of its true contents, meaning marshaling then unmarshaling does not round trip.Description
This PR attempts to fix this issue and make embedded
Unmarshalers more reliable by modifying the hook so that it returns a fully unmarshaled struct, leadingmapstructureto do nothing. (This is what the basicUnmarshalerhook does to avoid "interference".)This means the hook needs to somehow unmarshal the non-
Unmarshalersubfields manually, without affecting the input or output ofUnmarshal. I did this by constructing a custom "partial" struct usingreflect.StructOfwhich only contains the non-Unmarshalerfields, copying the initial values into it, callingDecodeon it, then copying the output values back into the original struct.In a previous version of this PR, I kept most of the hook intact, but changed the logic so that we clear fields from the raw config when they might conflict with the embedded struct (instead of setting those fields to the embedded struct's current value). However, this has the side effect of breaking unmarshaling of structs with multiple identically-named fields, of which there is at least one in contrib (both
docker.Configandscraperhelper.ControllerConfighave aTimeoutfield, and one of them implementsUnmarshaler).