mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
apiextensions: wire pruning into handler
This commit is contained in:
parent
52577aa908
commit
70ee02725f
@ -30,6 +30,8 @@ import (
|
|||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/conversion"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/conversion"
|
||||||
|
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
|
structuralpruning "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning"
|
||||||
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
||||||
informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion"
|
informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion"
|
||||||
listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion"
|
listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion"
|
||||||
@ -39,6 +41,7 @@ import (
|
|||||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/registry/customresource"
|
"k8s.io/apiextensions-apiserver/pkg/registry/customresource"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor"
|
"k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor"
|
||||||
|
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
@ -520,6 +523,20 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for nil because we dereference this throughout the handler code.
|
||||||
|
// Note: we always default this to non-nil. But we should guard these dereferences any way.
|
||||||
|
if crd.Spec.PreserveUnknownFields == nil {
|
||||||
|
return nil, fmt.Errorf("unexpected nil spec.preserveUnknownFields in the CustomResourceDefinition")
|
||||||
|
}
|
||||||
|
|
||||||
|
var structuralSchema *structuralschema.Structural
|
||||||
|
if validationSchema != nil {
|
||||||
|
structuralSchema, err = structuralschema.NewStructural(validationSchema.OpenAPIV3Schema)
|
||||||
|
if *crd.Spec.PreserveUnknownFields == false && err != nil {
|
||||||
|
return nil, err // validation should avoid this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var statusSpec *apiextensions.CustomResourceSubresourceStatus
|
var statusSpec *apiextensions.CustomResourceSubresourceStatus
|
||||||
var statusValidator *validate.SchemaValidator
|
var statusValidator *validate.SchemaValidator
|
||||||
subresources, err := apiextensions.GetSubresourcesForVersion(crd, v.Name)
|
subresources, err := apiextensions.GetSubresourcesForVersion(crd, v.Name)
|
||||||
@ -574,6 +591,8 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
|||||||
converter: safeConverter,
|
converter: safeConverter,
|
||||||
decoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name},
|
decoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name},
|
||||||
encoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: storageVersion},
|
encoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: storageVersion},
|
||||||
|
structuralSchema: structuralSchema,
|
||||||
|
preserveUnknownFields: *crd.Spec.PreserveUnknownFields,
|
||||||
},
|
},
|
||||||
crd.Status.AcceptedNames.Categories,
|
crd.Status.AcceptedNames.Categories,
|
||||||
table,
|
table,
|
||||||
@ -595,7 +614,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
|||||||
ClusterScoped: clusterScoped,
|
ClusterScoped: clusterScoped,
|
||||||
SelfLinkPathPrefix: selfLinkPrefix,
|
SelfLinkPathPrefix: selfLinkPrefix,
|
||||||
},
|
},
|
||||||
Serializer: unstructuredNegotiatedSerializer{typer: typer, creator: creator, converter: safeConverter},
|
Serializer: unstructuredNegotiatedSerializer{typer: typer, creator: creator, converter: safeConverter, structuralSchema: structuralSchema, preserveUnknownFields: *crd.Spec.PreserveUnknownFields},
|
||||||
ParameterCodec: parameterCodec,
|
ParameterCodec: parameterCodec,
|
||||||
|
|
||||||
Creater: creator,
|
Creater: creator,
|
||||||
@ -646,6 +665,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
|||||||
// shallow copy
|
// shallow copy
|
||||||
statusScope := *requestScopes[v.Name]
|
statusScope := *requestScopes[v.Name]
|
||||||
statusScope.Subresource = "status"
|
statusScope.Subresource = "status"
|
||||||
|
statusScope.Serializer = unstructuredNegotiatedSerializer{typer: typer, creator: creator, converter: safeConverter, structuralSchema: structuralSchema, preserveUnknownFields: *crd.Spec.PreserveUnknownFields}
|
||||||
statusScope.Namer = handlers.ContextBasedNaming{
|
statusScope.Namer = handlers.ContextBasedNaming{
|
||||||
SelfLinker: meta.NewAccessor(),
|
SelfLinker: meta.NewAccessor(),
|
||||||
ClusterScoped: clusterScoped,
|
ClusterScoped: clusterScoped,
|
||||||
@ -680,6 +700,9 @@ type unstructuredNegotiatedSerializer struct {
|
|||||||
typer runtime.ObjectTyper
|
typer runtime.ObjectTyper
|
||||||
creator runtime.ObjectCreater
|
creator runtime.ObjectCreater
|
||||||
converter runtime.ObjectConvertor
|
converter runtime.ObjectConvertor
|
||||||
|
|
||||||
|
structuralSchema *structuralschema.Structural
|
||||||
|
preserveUnknownFields bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s unstructuredNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
func (s unstructuredNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||||
@ -712,7 +735,7 @@ func (s unstructuredNegotiatedSerializer) EncoderForVersion(encoder runtime.Enco
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s unstructuredNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
func (s unstructuredNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||||
d := schemaCoercingDecoder{delegate: decoder, validator: unstructuredSchemaCoercer{}}
|
d := schemaCoercingDecoder{delegate: decoder, validator: unstructuredSchemaCoercer{structuralSchema: s.structuralSchema, preserveUnknownFields: s.preserveUnknownFields}}
|
||||||
return versioning.NewDefaultingCodecForScheme(Scheme, nil, d, nil, gv)
|
return versioning.NewDefaultingCodecForScheme(Scheme, nil, d, nil, gv)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,16 +827,20 @@ type crdConversionRESTOptionsGetter struct {
|
|||||||
converter runtime.ObjectConvertor
|
converter runtime.ObjectConvertor
|
||||||
encoderVersion schema.GroupVersion
|
encoderVersion schema.GroupVersion
|
||||||
decoderVersion schema.GroupVersion
|
decoderVersion schema.GroupVersion
|
||||||
|
structuralSchema *structuralschema.Structural
|
||||||
|
preserveUnknownFields bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t crdConversionRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
func (t crdConversionRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||||
ret, err := t.RESTOptionsGetter.GetRESTOptions(resource)
|
ret, err := t.RESTOptionsGetter.GetRESTOptions(resource)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
d := schemaCoercingDecoder{delegate: ret.StorageConfig.Codec, validator: unstructuredSchemaCoercer{
|
d := schemaCoercingDecoder{delegate: ret.StorageConfig.Codec, validator: unstructuredSchemaCoercer{
|
||||||
// drop invalid fields while decoding old CRs (before we had any ObjectMeta validation)
|
// drop invalid fields while decoding old CRs (before we haven't had any ObjectMeta validation)
|
||||||
dropInvalidMetadata: true,
|
dropInvalidMetadata: true,
|
||||||
|
structuralSchema: t.structuralSchema,
|
||||||
|
preserveUnknownFields: t.preserveUnknownFields,
|
||||||
}}
|
}}
|
||||||
c := schemaCoercingConverter{delegate: t.converter, validator: unstructuredSchemaCoercer{}}
|
c := schemaCoercingConverter{delegate: t.converter, validator: unstructuredSchemaCoercer{structuralSchema: t.structuralSchema, preserveUnknownFields: t.preserveUnknownFields}}
|
||||||
ret.StorageConfig.Codec = versioning.NewCodec(
|
ret.StorageConfig.Codec = versioning.NewCodec(
|
||||||
ret.StorageConfig.Codec,
|
ret.StorageConfig.Codec,
|
||||||
d,
|
d,
|
||||||
@ -894,13 +921,15 @@ func (v schemaCoercingConverter) ConvertFieldLabel(gvk schema.GroupVersionKind,
|
|||||||
return v.delegate.ConvertFieldLabel(gvk, label, value)
|
return v.delegate.ConvertFieldLabel(gvk, label, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unstructuredSchemaCoercer does the validation for Unstructured that json.Unmarshal
|
// unstructuredSchemaCoercer adds to unstructured unmarshalling what json.Unmarshal does
|
||||||
// does for native types. This includes:
|
// in addition for native types when decoding into Golang structs:
|
||||||
// - validating and pruning ObjectMeta (here with optional error instead of pruning)
|
//
|
||||||
// - TODO: application of an OpenAPI validator (against the whole object or a top-level field of it).
|
// - validating and pruning ObjectMeta
|
||||||
// - TODO: optionally application of post-validation algorithms like defaulting and/or OpenAPI based pruning.
|
// - generic pruning of unknown fields following a structural schema.
|
||||||
type unstructuredSchemaCoercer struct {
|
type unstructuredSchemaCoercer struct {
|
||||||
dropInvalidMetadata bool
|
dropInvalidMetadata bool
|
||||||
|
structuralSchema *structuralschema.Structural
|
||||||
|
preserveUnknownFields bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error {
|
func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error {
|
||||||
@ -918,6 +947,10 @@ func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !v.preserveUnknownFields {
|
||||||
|
structuralpruning.Prune(u.Object, v.structuralSchema)
|
||||||
|
}
|
||||||
|
|
||||||
// restore meta fields, starting clean
|
// restore meta fields, starting clean
|
||||||
if foundKind {
|
if foundKind {
|
||||||
u.SetKind(kind)
|
u.SetKind(kind)
|
||||||
|
Loading…
Reference in New Issue
Block a user