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
CRs: Default non-nullable nulls #95423
Conversation
/triage accepted |
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm.go
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm.go
Show resolved
Hide resolved
} else if s.AdditionalProperties != nil { | ||
Default(v, s.AdditionalProperties.Structural) | ||
if isNonNullableNull(x[k], s.AdditionalProperties.Structural) { | ||
x[k] = runtime.DeepCopyJSONValue(s.AdditionalProperties.Structural.Default.Object) |
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.
you don't check that there is a default.
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.
Does it matter? At that point, we know that x[k]
is null, then that's going to reset it to null? That's a no-op.
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.
don't we need to check if s.AdditionalProperties.Structural
is non-nil so we don't panic? if so, add a unit test that would have caught this
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.
Yeah good point
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm.go
Show resolved
Hide resolved
7de1bdf
to
159ce40
Compare
Updated, still missing some higher-level tests specifically. |
159ce40
to
c8aac5d
Compare
@@ -1251,6 +1252,7 @@ func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error { | |||
// TODO: switch over pruning and coercing at the root to schemaobjectmeta.Coerce too | |||
structuralpruning.Prune(u.Object, v.structuralSchemas[gv.Version], false) | |||
} | |||
structuraldefaulting.PruneNulls(u.Object, v.structuralSchemas[gv.Version]) |
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.
why not inside the if statement?
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.
Now I am pretty sure we want this only for !v.preserveUnknownFields
.
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.
Now I am pretty sure we want this only for !v.preserveUnknownFields.
I'm trying to understand why. Clearly, unknownfields don't have a schema, so basically, they're always non-nullable non-defaulted, so all the nulls would be dropped. But I don't see why that's a problem, or consistent with what we're doing?
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.
unknownfields don't have a schema, so basically, they're always non-nullable non-defaulted
I don't think that's true... with no schema, we allow any value, including null, so they are nullable non-defaulted
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.
Ah, that's a good point. My perspective has been that "nullable" is false by default, so a lack of schema would imply non-nullable.
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.
Yes, good, that's what I expected.
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.
OK fixed!
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.
I consider the non-structural case as legacy we will never ever touch again. Hence, only change pruning of nulls in the if.
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.
As @apelisse found in our validation (he was 2s faster than me):
if spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields == true {
allErrs = append(allErrs, field.Invalid(fldPath.Child("preserveUnknownFields"), true, "must be false in order to use defaults in the schema"))
}
We forbid defaulting for preserveUnknownFields:true
. Hence, pruning some nulls without defaulting would be confusing.
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.
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm.go
Show resolved
Hide resolved
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/prunenulls.go
Outdated
Show resolved
Hide resolved
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: apelisse, 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 |
/priority important-soon |
Kubernetes 1.20 tests are currently broken, since our old invalid config is now considered valid (see kubernetes/kubernetes#95423). In fixing this, I realized we don't have any validation for WorkloadGroup! This adds validation, by applying the same validation WorkloadEntry has to the template of workload group. However, workload entry validation is pretty much nil, so this change isn't very beneficial beyond getting tests passing.
* Add validation for workload group Kubernetes 1.20 tests are currently broken, since our old invalid config is now considered valid (see kubernetes/kubernetes#95423). In fixing this, I realized we don't have any validation for WorkloadGroup! This adds validation, by applying the same validation WorkloadEntry has to the template of workload group. However, workload entry validation is pretty much nil, so this change isn't very beneficial beyond getting tests passing. * gen * fixes * Update * fmt
* Add validation for workload group Kubernetes 1.20 tests are currently broken, since our old invalid config is now considered valid (see kubernetes/kubernetes#95423). In fixing this, I realized we don't have any validation for WorkloadGroup! This adds validation, by applying the same validation WorkloadEntry has to the template of workload group. However, workload entry validation is pretty much nil, so this change isn't very beneficial beyond getting tests passing. * gen * fixes * Update * fmt (cherry picked from commit 6e649ab)
* Add validation for workload group (#29355) * Add validation for workload group Kubernetes 1.20 tests are currently broken, since our old invalid config is now considered valid (see kubernetes/kubernetes#95423). In fixing this, I realized we don't have any validation for WorkloadGroup! This adds validation, by applying the same validation WorkloadEntry has to the template of workload group. However, workload entry validation is pretty much nil, so this change isn't very beneficial beyond getting tests passing. * gen * fixes * Update * fmt (cherry picked from commit 6e649ab) * fix extra file
Currently, Whenever a `SP` is created with `Spec.Routes` field not being set from [golang types](https://github.com/linkerd/linkerd2/blob/main/controller/gen/apis/serviceprofile/v1alpha2/types.go#L13) , k8s API rejects them with the following error ```bash ServiceProfile.linkerd.io \"backend-svc.linkerd-smi-app.svc.cluster.local\" is invalid: spec.routes: Invalid value: \"null\": spec.routes in body must be of type array: \"null\" ``` This happens because, Golang automatically renders them it as `Routes: Null` whenever it marshalled into json. This is rejected by k8s API server as it expects that field to be an array. [**This is fixed in k8s >= 1.20**](kubernetes/kubernetes#95423) as non-nullable nulls are defaulted, and hence this error happens only in `<=1.19`. As `1.19` is a pretty recent version of k8s, and things like [smi-adaptor](https://github.com/linkerd/linkerd-smi/pull) may not want to manage and make sure `Spec.Routes` is not null all the time. This can be easily be fixed by marking `Spec.Routes` as `omitempty` in its json tagswhich means that the field is omitted whenever it is not set while being marshalled. This means that the k8s API won't error out, as that field isn't set to anything invalid. Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>
## Context Currently, Whenever a `SP` is created with `Spec.Routes` field not being set from [golang types](https://github.com/linkerd/linkerd2/blob/main/controller/gen/apis/serviceprofile/v1alpha2/types.go#L13), k8s API rejects them with the following error ```bash ServiceProfile.linkerd.io \"backend-svc.linkerd-smi-app.svc.cluster.local\" is invalid: spec.routes: Invalid value: \"null\": spec.routes in body must be of type array: \"null\" ``` This happens because, Golang automatically renders them it as `Routes: Null` whenever it marshaled into json. This is rejected by k8s API server as it expects that field to be an array. [This is fixed in k8s >= 1.20](kubernetes/kubernetes#95423) as non-nullable nulls are defaulted, and hence this error happens only in `<=1.19`. ## Problem As `1.19` is a pretty recent version of k8s, and things like [smi-adaptor](https://github.com/linkerd/linkerd-smi/pull) may not want to manage and make sure `Spec.Routes` is not null all the time. ## Fix This can be easily be fixed by marking `Spec.Routes` as `omitempty` in its json tags which means that the field is omitted whenever it is not set while being marshaled. This means that the k8s API won't error out, as that field isn't set to anything invalid. Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>
What type of PR is this?
/kind feature
What this PR does / why we need it:
Which issue(s) this PR fixes:
Special notes for your reviewer:
Does this PR introduce a user-facing change?:
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.: