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
apiextensions: complete default-under-metadata validation and storage pruning #78829
apiextensions: complete default-under-metadata validation and storage pruning #78829
Conversation
90b489c
to
1e4f2f9
Compare
1e4f2f9
to
67d25f5
Compare
/assign @roycaihw |
/cc @liggitt |
67d25f5
to
4f8261b
Compare
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/validation.go
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/validation.go
Outdated
Show resolved
Hide resolved
@@ -80,7 +80,7 @@ func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) { | |||
ss.Items = item | |||
} | |||
|
|||
if len(s.Properties) > 0 { | |||
if s.Properties != nil { |
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.
can you provide an empty, but defined properties field
you cannot if you use client-go to wire a CRD to a JSON or proto (the empty field gets omitted in both cases); you can if you write a JSON and send it to apiserver directly
nit: this relaxes validation slightly, the validation error message might be inaccurate now
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.
@liggitt are we fine with that?
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.
we could add a formal validation that properties
for non-preserve-unknown-fields objects are never empty, making JSON and proto to behave the same.
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.
@liggitt are we fine with that?
not as written.
we have to be really careful here... this could allow a user to submit the following data that would pass validation, then fail validation after round-tripping through etcd:
openAPIV3Schema:
type: object
properties:
template:
type: object
x-kubernetes-embedded-resource: true
properties: {}
in 1.15, even if properties: {}
was submitted to the apiserver (via a manual JSON request), the len(s.Properties) > 0
check here meant that the structural schema had a nil Properties
field, and validation failed with ...properties: Required value: must not be empty if x-kubernetes-embedded-resource is true without x-kubernetes-preserve-unknown-fields
error
with this PR, the CRD is allowed to be created, and updates fail with that message (since round-tripping through protobuf in etcd truncated the properties field to nil).
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.
we could add a formal validation that properties for non-preserve-unknown-fields objects are never empty, making JSON and proto to behave the same.
that seems best. we know no existing schemas or existing 1.15 API clients successfully specified empty properties to get around that nil check validation, because of the len() > 0
check here.
I would:
- leave the
len() > 0
check here (preserving the difference between nil and 0-length doesn't seem useful if we know we can't round-trip it in through etcd) - switch the
if s.XEmbeddedResource && !s.XPreserveUnknownFields && s.Properties == nil {
check to ifs.XEmbeddedResource && !s.XPreserveUnknownFields && len(s.Properties) == 0 {
to make the check match what we can round-trip
If someone really wants an embedded resource with no other properties without preserving unknown fields, they can explicitly define apiVersion
/ kind
properties
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.
done ^^
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.
including tests.
staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/surroundingobject.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/prune.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/surroundingobject.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go
Outdated
Show resolved
Hide resolved
|
||
for _, tst := range tests { | ||
t.Run(strings.Join(tst.path, "."), func(t *testing.T) { | ||
obj, found, err := unstructured.NestedMap(foo.Object, tst.path...) |
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 tests that defaults of unknown fields didn't survive defaulting->serialization->etcd->decoding->pruning, but it's not clear that the pruning happened before data got persisted to etcd (which is what we really care about)
can we use the storage.NewEtcdObjectReader (from the conversion webhook tests) to pull data out of etcd for verification?
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.
yep, I noticed that as well. Let me check about etcd object reader.
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.
done. And verified that without the fixed default pruning algorithm it breaks 👍
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go
Show resolved
Hide resolved
if err != nil { | ||
return false, fmt.Errorf("failed to prune default value: %v", err) | ||
} | ||
pruning.Prune(obj, p.rootSchema, true) |
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.
just making sure I understand, this prunes both metadata and non-metadata fields? (though non-metadata defaults should be no-ops since we check there are no pruned fields in those at validation time?)
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.
no, it didn't. Fixed.
(also fix the golint verify errors :-/) |
5b49db8
to
1bfd7e9
Compare
/approve squash review commits, fixup lint, then lgtm |
1bfd7e9
to
55e2255
Compare
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: liggitt, sttts 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 |
55e2255
to
4fd200c
Compare
/lgtm |
Again no bazel test logs. /retest |
/area custom-resources |
Implement the validation part of kubernetes/enhancements#1166.
Storage default pruning will be a follow-up.Both the validation and storage part is included.Possible follow-ups (not in this PR):