Skip to content
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

openapiv2 experimental json unmarshaler #319

Merged
merged 5 commits into from
Oct 12, 2022

Conversation

alexzielenski
Copy link
Member

@alexzielenski alexzielenski commented Sep 27, 2022

Continuing work from #315

This PR adds integrates the work done by @dsnet in the above issue.

Adds fuzz tests and also tests for roundtripping.

Adds benchmark

jsonv2_via_jsonv1 uses the new experimental decoder but is triggered via json.Unmarshal rather than invoking jsonv2 directly. The legacy json wrapper does have a cost.

goos: darwin
goarch: arm64
pkg: k8s.io/kube-openapi/pkg/validation/spec
BenchmarkSwaggerSpec_ExperimentalUnmarshal/jsonv1-20         	           3	 447452972 ns/op	103245472 B/op	 1506417 allocs/op
BenchmarkSwaggerSpec_ExperimentalUnmarshal/jsonv2_via_jsonv1-20            24	  47524977 ns/op	26232751 B/op	  138894 allocs/op
BenchmarkSwaggerSpec_ExperimentalUnmarshal/jsonv2-20                       44	  25974345 ns/op	26232392 B/op	  138887 allocs/op
PASS
ok  	k8s.io/kube-openapi/pkg/validation/spec	5.320s

/cc @apelisse

@k8s-ci-robot k8s-ci-robot added 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/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Sep 27, 2022
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 27, 2022
@apelisse
Copy link
Member

apelisse commented Sep 27, 2022

Since the wrapper has quite an impact on the performance, can we just provide a FromJSON() method on the Swagger type?

@apelisse
Copy link
Member

That seems to be what keeps failing for some reason:

         											AdditionalProperties: &spec.SchemaOrBool{
        - 												Allows: true,
        + 												Allows: false,
          												Schema: &{},
          											},

@apelisse
Copy link
Member

Went through it, looks good to me. Except for the bug I don't understand.

@apelisse
Copy link
Member

OK feel free to rebase now that #320 has merged.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 28, 2022
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 28, 2022
Copy link
Member

@apelisse apelisse left a comment

Choose a reason for hiding this comment

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

/lgtm

I don't have approval on pkg/validation, but I would appreciate someone else's look at it. @dsnet?

limitations under the License.
*/

package internal
Copy link
Member

Choose a reason for hiding this comment

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

Note for other reviewers, this package is at the right level because we'll probably use the same variable for OpenAPI v3 that lives in pkg/spec3. Thanks!

Copy link

Choose a reason for hiding this comment

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

Should this be protected by a build tag rather than a variable?

I've generally recommended against open source projects using go-json-experiment due to the following effect:

  1. Let module M depend on modules A and B.
  2. Module A depends on go-json-experiment/[email protected].
  3. Module B depends on go-json-experiment/[email protected].
  4. Let’s suppose a breaking change occurs between v0.5.0 and v0.8.0.
  5. MVS dictates that v0.8.0 be selected to build module M.
  6. However, the use of v0.8.0 can break package A since it is using the API for v0.5.0, which is not compatible.

I don't know enough about the kubernetes/kube-openapi whether this is a module that is only used to build a binary, or whether it is a module intended to be depended upon by many other modules. If the former, then guarding it with a variable sounds fine. If the latter, then we probably want to guard it with a build tag.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the explanation. One quick (and ugly, but we're used to it here) solution would be to just fork the go-json-experiment into this repo so that we only depend internally on it, no more problem with MVS?

Copy link
Member

Choose a reason for hiding this comment

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

@liggitt Do you have an opinion about the suggestion above? Thanks.

Copy link

Choose a reason for hiding this comment

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

Forking SGTM.

The license is BSD and the listed authors are the Go authors since I had originally started the project when I was a Google employee. It is currently developed by all non-Googlers, but every contributor is also a regular contributor to the Go toolchain, so have each signed the Google CLA.

Copy link
Member

Choose a reason for hiding this comment

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

forking into an internal third_party package with correct attribution as an internal implementation detail until the go-json-experiment stabilizes sgtm

Copy link
Member Author

@alexzielenski alexzielenski Oct 5, 2022

Choose a reason for hiding this comment

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

I've forked it into pkg/internal/third_party/go-json-experiment/json. I added a readme with the commit hash and a note about when it should be removed.

To fork I executed git clone and removed .git, .github, go.mod, go.sum, and all .png files. I am unsure if there is a more standard way to "fork" the package into our tree. Please let me know if the current way I've done it is OK

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Sep 28, 2022
Copy link

@dsnet dsnet left a comment

Choose a reason for hiding this comment

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

Implementation generally LGTM.

limitations under the License.
*/

package internal
Copy link

Choose a reason for hiding this comment

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

Should this be protected by a build tag rather than a variable?

I've generally recommended against open source projects using go-json-experiment due to the following effect:

  1. Let module M depend on modules A and B.
  2. Module A depends on go-json-experiment/[email protected].
  3. Module B depends on go-json-experiment/[email protected].
  4. Let’s suppose a breaking change occurs between v0.5.0 and v0.8.0.
  5. MVS dictates that v0.8.0 be selected to build module M.
  6. However, the use of v0.8.0 can break package A since it is using the API for v0.5.0, which is not compatible.

I don't know enough about the kubernetes/kube-openapi whether this is a module that is only used to build a binary, or whether it is a module intended to be depended upon by many other modules. If the former, then guarding it with a variable sounds fine. If the latter, then we probably want to guard it with a build tag.

@apelisse
Copy link
Member

apelisse commented Oct 3, 2022

I'm good on this, just waiting for Jordan to confirm if he's alright with the fork.

func (s *StringOrArray) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
switch k := dec.PeekKind(); k {
case '[':
return opts.UnmarshalNext(dec, (*[]string)(s))
Copy link
Member

Choose a reason for hiding this comment

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

does this switch StringOrArray from overwriting on multiple unmarshals to appending?

Copy link
Member Author

Choose a reason for hiding this comment

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

no, but ive added a line to reset s so it is explicit

Copy link
Member

Choose a reason for hiding this comment

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

Question since I'm curious, what does the spec requires when it comes to multiple unmarshaling? I was experimenting the other day and it felt inconsistent to me.

limitations under the License.
*/

package internal
Copy link
Member

Choose a reason for hiding this comment

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

forking into an internal third_party package with correct attribution as an internal implementation detail until the go-json-experiment stabilizes sgtm

@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 5, 2022
func (s *StringOrArray) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
switch k := dec.PeekKind(); k {
case '[':
return opts.UnmarshalNext(dec, (*[]string)(s))
Copy link
Member

Choose a reason for hiding this comment

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

Question since I'm curious, what does the spec requires when it comes to multiple unmarshaling? I was experimenting the other day and it felt inconsistent to me.

@apelisse
Copy link
Member

@dsnet The internal/third_party test is failing, is this something expected?!

@dsnet
Copy link

dsnet commented Oct 11, 2022

The minimum support version of go-json-experiment is Go1.19 since we're relying on updated behavior in the reflect package.

It seems that we should either run tests on Go1.19 or patching a revert for go-json-experiment/json@ef074b6.

@liggitt
Copy link
Member

liggitt commented Oct 11, 2022

/approve
for validation package changes

/hold for green CI on WIP PR in k/k

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 11, 2022
@apelisse
Copy link
Member

tests are looking good, https://prow.k8s.io/pr-history/?org=kubernetes&repo=kubernetes&pr=112988

/hold cancel

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 11, 2022
@apelisse
Copy link
Member

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 11, 2022
@liggitt
Copy link
Member

liggitt commented Oct 12, 2022

looks like the testdata and tests get dropped when importing into kube, which is nice, but the png images referenced from the readme get kept, which is less nice... can we drop those from our import of the dependency?

revert json experiment to 4987ed27d44794668c752d227a2d3a7be54570bc

correct commit hash

correct package path

remove pngs
…ype which needs it

adds subtest `UnmarshalEmbedded` that positively shows that there is at least one property per embedded field

also does not use field labels, so compiler errors are thrown in event any are missing

whoops
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 12, 2022
@apelisse
Copy link
Member

So did you just delete the files from the repo and that's it?

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 12, 2022
@apelisse
Copy link
Member

/approve

Thanks!

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alexzielenski, apelisse, dsnet, liggitt

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

The pull request process is described here

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 approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 12, 2022
@k8s-ci-robot k8s-ci-robot merged commit 172d655 into kubernetes:master Oct 12, 2022
@apelisse
Copy link
Member

What's next?

  • We probably have the same bug unmarshalling OpenAPI v3
  • There's probably room for improvement with marshalling OpenAPI v2/v3
  • Investigate the new trade-offs between json and protobuf, possibly encourage json moving forward

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants