From c0aaf94159781d5d45f9fb05aa06022017b79df8 Mon Sep 17 00:00:00 2001 From: deads2k Date: Wed, 31 May 2017 15:03:23 -0400 Subject: [PATCH] enforce proper TypeMeta --- .../pkg/apiserver/customresource_handler.go | 9 +- .../pkg/registry/customresource/strategy.go | 83 ++++++++++++++----- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go index 2ba73858ba4..1a251fda2e3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go @@ -267,11 +267,16 @@ func (r *crdHandler) getServingInfoFor(crd *apiextensions.CustomResourceDefiniti return ret } + kind := schema.GroupVersionKind{Group: crd.Spec.Group, Version: crd.Spec.Version, Kind: crd.Spec.Names.Kind} storage := customresource.NewREST( schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural}, schema.GroupVersionKind{Group: crd.Spec.Group, Version: crd.Spec.Version, Kind: crd.Spec.Names.ListKind}, UnstructuredCopier{}, - customresource.NewStrategy(discovery.NewUnstructuredObjectTyper(nil), crd.Spec.Scope == apiextensions.NamespaceScoped), + customresource.NewStrategy( + discovery.NewUnstructuredObjectTyper(nil), + crd.Spec.Scope == apiextensions.NamespaceScoped, + kind, + ), r.restOptionsGetter, ) @@ -319,7 +324,7 @@ func (r *crdHandler) getServingInfoFor(crd *apiextensions.CustomResourceDefiniti UnsafeConvertor: unstructured.UnstructuredObjectConverter{}, Resource: schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural}, - Kind: schema.GroupVersionKind{Group: crd.Spec.Group, Version: crd.Spec.Version, Kind: crd.Spec.Names.Kind}, + Kind: kind, Subresource: "", MetaGroupVersion: metav1.SchemeGroupVersion, diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go index bafe064ac1c..8ebc90c1799 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go @@ -17,12 +17,15 @@ limitations under the License. package customresource import ( + "fmt" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/storage" @@ -34,10 +37,19 @@ type CustomResourceDefinitionStorageStrategy struct { names.NameGenerator namespaceScoped bool + validator customResourceValidator } -func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool) CustomResourceDefinitionStorageStrategy { - return CustomResourceDefinitionStorageStrategy{typer, names.SimpleNameGenerator, namespaceScoped} +func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.GroupVersionKind) CustomResourceDefinitionStorageStrategy { + return CustomResourceDefinitionStorageStrategy{ + ObjectTyper: typer, + NameGenerator: names.SimpleNameGenerator, + namespaceScoped: namespaceScoped, + validator: customResourceValidator{ + namespaceScoped: namespaceScoped, + kind: kind, + }, + } } func (a CustomResourceDefinitionStorageStrategy) NamespaceScoped() bool { @@ -51,12 +63,7 @@ func (CustomResourceDefinitionStorageStrategy) PrepareForUpdate(ctx genericapire } func (a CustomResourceDefinitionStorageStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { - accessor, err := meta.Accessor(obj) - if err != nil { - return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())} - } - - return validation.ValidateObjectMetaAccessor(accessor, a.namespaceScoped, validation.NameIsDNSSubdomain, field.NewPath("metadata")) + return a.validator.Validate(ctx, obj) } func (CustomResourceDefinitionStorageStrategy) AllowCreateOnUpdate() bool { @@ -70,17 +77,8 @@ func (CustomResourceDefinitionStorageStrategy) AllowUnconditionalUpdate() bool { func (CustomResourceDefinitionStorageStrategy) Canonicalize(obj runtime.Object) { } -func (CustomResourceDefinitionStorageStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { - objAccessor, err := meta.Accessor(obj) - if err != nil { - return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())} - } - oldAccessor, err := meta.Accessor(old) - if err != nil { - return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())} - } - - return validation.ValidateObjectMetaAccessorUpdate(objAccessor, oldAccessor, field.NewPath("metadata")) +func (a CustomResourceDefinitionStorageStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { + return a.validator.ValidateUpdate(ctx, obj, old) } func (a CustomResourceDefinitionStorageStrategy) GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { @@ -111,3 +109,50 @@ func (a CustomResourceDefinitionStorageStrategy) MatchCustomResourceDefinitionSt GetAttrs: a.GetAttrs, } } + +type customResourceValidator struct { + namespaceScoped bool + kind schema.GroupVersionKind +} + +func (a customResourceValidator) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { + accessor, err := meta.Accessor(obj) + if err != nil { + return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())} + } + typeAccessor, err := meta.TypeAccessor(obj) + if err != nil { + return field.ErrorList{field.Invalid(field.NewPath("kind"), nil, err.Error())} + } + if typeAccessor.GetKind() != a.kind.Kind { + return field.ErrorList{field.Invalid(field.NewPath("kind"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Kind))} + } + if typeAccessor.GetAPIVersion() != a.kind.Group+"/"+a.kind.Version { + return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Group+"/"+a.kind.Version))} + } + + return validation.ValidateObjectMetaAccessor(accessor, a.namespaceScoped, validation.NameIsDNSSubdomain, field.NewPath("metadata")) +} + +func (a customResourceValidator) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { + objAccessor, err := meta.Accessor(obj) + if err != nil { + return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())} + } + oldAccessor, err := meta.Accessor(old) + if err != nil { + return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())} + } + typeAccessor, err := meta.TypeAccessor(obj) + if err != nil { + return field.ErrorList{field.Invalid(field.NewPath("kind"), nil, err.Error())} + } + if typeAccessor.GetKind() != a.kind.Kind { + return field.ErrorList{field.Invalid(field.NewPath("kind"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Kind))} + } + if typeAccessor.GetAPIVersion() != a.kind.Group+"/"+a.kind.Version { + return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Group+"/"+a.kind.Version))} + } + + return validation.ValidateObjectMetaAccessorUpdate(objAccessor, oldAccessor, field.NewPath("metadata")) +}