diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 6396bb9fa..c0cdfc9e2 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -704,7 +704,7 @@ }, { "ImportPath": "k8s.io/api", - "Rev": "e55a4a108a76" + "Rev": "86cef11b7287" }, { "ImportPath": "k8s.io/apimachinery", @@ -712,7 +712,7 @@ }, { "ImportPath": "k8s.io/client-go", - "Rev": "f7d41dd5f901" + "Rev": "4e267f6cae31" }, { "ImportPath": "k8s.io/component-base", diff --git a/go.mod b/go.mod index 3d769334e..0715b2f2d 100644 --- a/go.mod +++ b/go.mod @@ -42,9 +42,9 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/square/go-jose.v2 v2.2.2 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.0.0-20210415154719-e55a4a108a76 + k8s.io/api v0.0.0-20210416194706-86cef11b7287 k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540 - k8s.io/client-go v0.0.0-20210415154953-f7d41dd5f901 + k8s.io/client-go v0.0.0-20210416194931-4e267f6cae31 k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704 k8s.io/klog/v2 v2.8.0 k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 @@ -55,8 +55,8 @@ require ( ) replace ( - k8s.io/api => k8s.io/api v0.0.0-20210415154719-e55a4a108a76 + k8s.io/api => k8s.io/api v0.0.0-20210416194706-86cef11b7287 k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540 - k8s.io/client-go => k8s.io/client-go v0.0.0-20210415154953-f7d41dd5f901 + k8s.io/client-go => k8s.io/client-go v0.0.0-20210416194931-4e267f6cae31 k8s.io/component-base => k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704 ) diff --git a/go.sum b/go.sum index 99e2ce5e5..d65b9c69d 100644 --- a/go.sum +++ b/go.sum @@ -602,9 +602,9 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.0.0-20210415154719-e55a4a108a76/go.mod h1:DHqxvzZf7mMtXnGgywYgvLoyfA2SGsMd1vdGAEcDjbA= +k8s.io/api v0.0.0-20210416194706-86cef11b7287/go.mod h1:DHqxvzZf7mMtXnGgywYgvLoyfA2SGsMd1vdGAEcDjbA= k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540/go.mod h1:CRK4uy8GKqwLtMa4Gj9i/B2EBjcqOsuvwP2xZJWUwmk= -k8s.io/client-go v0.0.0-20210415154953-f7d41dd5f901/go.mod h1:gZDn8z9shVCQQs7oeSaWAS3c3gp2/YAq4WY5npomrUw= +k8s.io/client-go v0.0.0-20210416194931-4e267f6cae31/go.mod h1:RbsIgkosH5M8OkVp5z3OCH5jtynzbv79nvpmcUh94EM= k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704/go.mod h1:zEytEW+Y696JL4g97mo/bP6hzeY26r9zDRw2IFbc7fE= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= diff --git a/pkg/endpoints/handlers/create.go b/pkg/endpoints/handlers/create.go index 79fc5afab..d6f8025e3 100644 --- a/pkg/endpoints/handlers/create.go +++ b/pkg/endpoints/handlers/create.go @@ -123,7 +123,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int scope.err(err, w, req) return } - if gvk.GroupVersion() != gv { + if !scope.AcceptsGroupVersion(gvk.GroupVersion()) { err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String())) scope.err(err, w, req) return diff --git a/pkg/endpoints/handlers/rest.go b/pkg/endpoints/handlers/rest.go index f618b3867..4c2e63e10 100644 --- a/pkg/endpoints/handlers/rest.go +++ b/pkg/endpoints/handlers/rest.go @@ -89,8 +89,14 @@ type RequestScope struct { TableConvertor rest.TableConvertor FieldManager *fieldmanager.FieldManager - Resource schema.GroupVersionResource - Kind schema.GroupVersionKind + Resource schema.GroupVersionResource + Kind schema.GroupVersionKind + + // AcceptsGroupVersionDelegate is an optional delegate that can be queried about whether a given GVK + // can be accepted in create or update requests. If nil, only scope.Kind is accepted. + // Note that this does not enable multi-version support for reads from a single endpoint. + AcceptsGroupVersionDelegate rest.GroupVersionAcceptor + Subresource string MetaGroupVersion schema.GroupVersion @@ -105,6 +111,17 @@ func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Reque responsewriters.ErrorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), w, req) } +// AcceptsGroupVersion returns true if the specified GroupVersion is allowed +// in create and update requests. +func (scope *RequestScope) AcceptsGroupVersion(gv schema.GroupVersion) bool { + // If there's a custom acceptor, delegate to it. This is extremely rare. + if scope.AcceptsGroupVersionDelegate != nil { + return scope.AcceptsGroupVersionDelegate.AcceptsGroupVersion(gv) + } + // Fall back to only allowing the singular Kind. This is the typical behavior. + return gv == scope.Kind.GroupVersion() +} + func (scope *RequestScope) AllowsMediaTypeTransform(mimeType, mimeSubType string, gvk *schema.GroupVersionKind) bool { // some handlers like CRDs can't serve all the mime types that PartialObjectMetadata or Table can - if // gvk is nil (no conversion) allow StandardSerializers to further restrict the set of mime types. diff --git a/pkg/endpoints/handlers/update.go b/pkg/endpoints/handlers/update.go index b66b57a8b..ceae03eee 100644 --- a/pkg/endpoints/handlers/update.go +++ b/pkg/endpoints/handlers/update.go @@ -110,7 +110,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa scope.err(err, w, req) return } - if gvk.GroupVersion() != defaultGVK.GroupVersion() { + if !scope.AcceptsGroupVersion(gvk.GroupVersion()) { err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion())) scope.err(err, w, req) return diff --git a/pkg/endpoints/installer.go b/pkg/endpoints/installer.go index 215ecf715..caaa4b2e8 100644 --- a/pkg/endpoints/installer.go +++ b/pkg/endpoints/installer.go @@ -251,6 +251,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag connecter, isConnecter := storage.(rest.Connecter) storageMeta, isMetadata := storage.(rest.StorageMetadata) storageVersionProvider, isStorageVersionProvider := storage.(rest.StorageVersionProvider) + gvAcceptor, _ := storage.(rest.GroupVersionAcceptor) if !isMetadata { storageMeta = defaultStorageMetadata{} } @@ -587,6 +588,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Subresource: subresource, Kind: fqKindToRegister, + AcceptsGroupVersionDelegate: gvAcceptor, + HubGroupVersion: schema.GroupVersion{Group: fqKindToRegister.Group, Version: runtime.APIVersionInternal}, MetaGroupVersion: metav1.SchemeGroupVersion, diff --git a/pkg/registry/rest/rest.go b/pkg/registry/rest/rest.go index 8dba9b84b..a489095d6 100644 --- a/pkg/registry/rest/rest.go +++ b/pkg/registry/rest/rest.go @@ -92,6 +92,13 @@ type GroupVersionKindProvider interface { GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind } +// GroupVersionAcceptor is used to determine if a particular GroupVersion is acceptable to send to an endpoint. +// This is used for endpoints which accept multiple versions (which is extremely rare). +// The only known instance is pods/evictions which accepts policy/v1, but also policy/v1beta1 for backwards compatibility. +type GroupVersionAcceptor interface { + AcceptsGroupVersion(gv schema.GroupVersion) bool +} + // Lister is an object that can retrieve resources that match the provided field and label criteria. type Lister interface { // NewList returns an empty object that can be used with the List call.