🐛 pkg/crd: reject pointer-receiver-only TextMarshaler types as map keys - #1441
🐛 pkg/crd: reject pointer-receiver-only TextMarshaler types as map keys#1441camilamacedo86 wants to merge 1 commit into
Conversation
|
Hi @HadrienPatte, I was reviewing your PR and found a scenario that I think is worth addressing before this reaches users. The check in Map keys are a bit different, though. Go's The test data in the PR currently demonstrates exactly this case. To make the behavior align with what
Thanks for adding this feature — the use case itself makes a lot of sense. I think it just needed this additional guard to ensure we only accept types that will actually work at runtime. Let me know what you think or if you'd like to discuss any of the details. @alvaroaleman please, feel free to check as well and let me know wdyt. |
HadrienPatte
left a comment
There was a problem hiding this comment.
Thanks for catching that nuance 🙏
In my PR I used url.URL3 in the tests which indeed only implements encoding.TextMarshaler as a pointer receiver, but in the associated issue #1420, I described the real-life use case that prompted me to come up with that PR which was using a netip.Addr as a map key, and that type does implement encoding.TextMarshaler as a value receiver.
|
@HadrienPatte: changing LGTM is restricted to collaborators DetailsIn response to this:
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. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: camilamacedo86, HadrienPatte The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
encoding/json marshals map keys by value, not by pointer. Map keys are not addressable in Go's reflect model, so if a type implements MarshalText only via a pointer receiver, encoding/json returns an error at runtime for any map that uses it as a key. PR kubernetes-sigs#1419 introduced the TextMarshaler map key check using the implements() helper, which accepts both value and pointer receivers. This caused controller-gen to accept types that would fail at runtime. Fix both occurrences in mapToSchema to use types.Implements directly, which checks value receivers only. The testdata example had the same problem: URL3 implements MarshalText only via a pointer receiver, so map[URL3]string would have failed at runtime. Replace it with a new TextMarshalerKey type that uses a value receiver and actually works. Add a Ginkgo test suite covering all three cases: accepted (value receiver), rejected (pointer receiver only), and rejected (no TextMarshaler at all).
1a0716f to
a611713
Compare
|
That is great news. IMO the real use case from issue #1420, using netip.Addr as a map key, is totally fine. netip.Addr implements TextMarshaler via a value receiver, so it works correctly with json.Marshal and your fix still accepts it. The problem is the testdata. URL3 only implements TextMarshaler via *URL3, so map[URL3]string would have failed at runtime. I am proposing to change that in this follow-up. Are you ok with it? I also moved the tests to Ginkgo since it is the project standard. Thank you so much for the nice contribution 🥇 |
|
Hi @sbueringer @alvaroaleman Could we get this one merged to fix the issue ? Thx |
|
@JoelSpeed this one has a LGTM/approval from the author already. |
|
I think its basically irrelevant if we error here or not, people will notice this doesn't work the first time they try to json marshal their api type |
|
Hi @alvaroaleman and @sbueringer, Thank you for looking on this one. I agree that the impact is probably low, since users will eventually get an error when the object is marshaled. That said, I still don’t see a strong reason to reject this PR. This change:
Moreover:
I see it is as reasonable follow up of PR #1419 to address what we did not catcher in time and to shape it better. Then, given all of the above, I'm trying to understand what is still preventing this PR from moving forward. Could you please clarify what the remaining concerns are? Is there something specific in the implementation that should be changed, or is there another reason you don't think this fix should be merged? I'd really appreciate understanding what is missing for this PR to receive an approval/LGTM. |
|
Hi @alvaroaleman and @sbueringer and @JoelSpeed I think this one should get merged before the next release as well. Could we get this one merged? WDYT? |
|
I don't think this PR should block or defer the next CR minor release |
| It("rejects a pointer-receiver-only TextMarshaler type as a map key", func() { | ||
| _, pkg := schemaForType(src, "PtrKeyMap") | ||
| Expect(pkg.Errors).NotTo(BeEmpty()) | ||
| Expect(pkg.Errors[0].Msg).To(ContainSubstring("map keys must be strings")) |
There was a problem hiding this comment.
What is the full error message here?
encoding/json marshals map keys by value, not by pointer. Map keys are not addressable in Go's reflect model, so if a type implements MarshalText only via a pointer receiver, encoding/json returns an error at runtime for any map that uses it as a key.
PR #1419 introduced the TextMarshaler map key check using the implements() helper, which accepts both value and pointer receivers. This caused controller-gen to accept types that would fail at runtime. Fix both occurrences in mapToSchema to use types.Implements directly, which checks value receivers only.
The testdata example had the same problem: URL3 implements MarshalText only via a pointer receiver, so map[URL3]string would have failed at runtime. Replace it with a new TextMarshalerKey type that uses a value receiver and actually works. Add a Ginkgo test suite covering all three cases: accepted (value receiver), rejected (pointer receiver only), and rejected (no TextMarshaler at all).