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

[release-1.19] Fix kubectl CRD validation with preserve-unknown-fields #96562

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -172,8 +172,7 @@ func TestNewBuilder(t *testing.T) {
},
"embedded-object": {
"x-kubernetes-embedded-resource": true,
"x-kubernetes-preserve-unknown-fields": true,
"type":"object"
"x-kubernetes-preserve-unknown-fields": true
}
},
"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]
Expand Down
Expand Up @@ -24,6 +24,7 @@ go_test(
"//vendor/github.com/googleapis/gnostic/openapiv2:go_default_library",
"//vendor/gopkg.in/yaml.v2:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library",
],
)

Expand Down
Expand Up @@ -73,6 +73,21 @@ func ToStructuralOpenAPIV2(in *structuralschema.Structural) *structuralschema.St
changed = true
}

if s.Items == nil && s.Type == "array" {
// kubectl cannot cope with array without item schema, e.g. due to XPreserveUnknownFields case above
// https://github.com/kubernetes/kube-openapi/blob/64514a1d5d596b96e6f957e2be275ae14d6b0804/pkg/util/proto/document.go#L185
s.Type = ""

changed = true
}

if s.XPreserveUnknownFields && s.Type == "object" {
// similar as above, kubectl doesn't properly handle object fields with `x-kubernetes-preserve-unknown-fields: true`
s.Type = ""

changed = true
}

for f, fs := range s.Properties {
if fs.Nullable {
s.ValueValidation.Required, changed = filterOut(s.ValueValidation.Required, f)
Expand Down
Expand Up @@ -36,6 +36,7 @@ import (
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
"k8s.io/kube-openapi/pkg/util/proto"
"k8s.io/utils/pointer"
)

func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) {
Expand Down Expand Up @@ -643,6 +644,30 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2SchemaByType(t *testing.T) {
WithExample(testStr),
expectDiff: true,
},
{
name: "preserve-unknown-fields in arrays",
in: &apiextensions.JSONSchemaProps{
XPreserveUnknownFields: pointer.BoolPtr(true),
Type: "array",
Items: &apiextensions.JSONSchemaPropsOrArray{Schema: &apiextensions.JSONSchemaProps{
Type: "string",
}},
},
expected: withVendorExtensions(new(spec.Schema), "x-kubernetes-preserve-unknown-fields", true),
},
{
name: "preserve-unknown-fields in objects",
in: &apiextensions.JSONSchemaProps{
XPreserveUnknownFields: pointer.BoolPtr(true),
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {
Type: "string",
},
},
},
expected: withVendorExtensions(new(spec.Schema), "x-kubernetes-preserve-unknown-fields", true),
},
}

for _, test := range tests {
Expand All @@ -666,6 +691,11 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2SchemaByType(t *testing.T) {
}
}

func withVendorExtensions(s *spec.Schema, key string, value interface{}) *spec.Schema {
s.VendorExtensible.AddExtension(key, value)
return s
}

func refEqual(x spec.Ref, y spec.Ref) bool {
return x.String() == y.String()
}
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/apimachinery/crd_publish_openapi.go
Expand Up @@ -230,7 +230,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)

ginkgo.By("client-side validation (kubectl create and apply) allows request with any unknown properties")
randomCR := fmt.Sprintf(`{%s,"spec":{"b":[{"c":"d"}]}}`, meta)
randomCR := fmt.Sprintf(`{%s,"spec":{"a":null,"b":[{"c":"d"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil {
framework.Failf("failed to create random CR %s for CRD that allows unknown properties in a nested object: %v", randomCR, err)
}
Expand Down