mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 23:15:14 +00:00
Add GetResourceMapper to admission ObjectInterfaces
This commit is contained in:
parent
9071d21e3b
commit
92f735042e
@ -493,6 +493,8 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
statusScopes := map[string]*handlers.RequestScope{}
|
||||
scaleScopes := map[string]*handlers.RequestScope{}
|
||||
|
||||
equivalentResourceRegistry := runtime.NewEquivalentResourceRegistry()
|
||||
|
||||
structuralSchemas := map[string]*structuralschema.Structural{}
|
||||
for _, v := range crd.Spec.Versions {
|
||||
val, err := apiextensions.GetSchemaForVersion(crd, v.Name)
|
||||
@ -526,7 +528,10 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
)
|
||||
parameterCodec := runtime.NewParameterCodec(parameterScheme)
|
||||
|
||||
resource := schema.GroupVersionResource{Group: crd.Spec.Group, Version: v.Name, Resource: crd.Status.AcceptedNames.Plural}
|
||||
kind := schema.GroupVersionKind{Group: crd.Spec.Group, Version: v.Name, Kind: crd.Status.AcceptedNames.Kind}
|
||||
equivalentResourceRegistry.RegisterKindFor(resource, "", kind)
|
||||
|
||||
typer := newUnstructuredObjectTyper(parameterScheme)
|
||||
creator := unstructuredCreator{}
|
||||
|
||||
@ -554,6 +559,8 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
return nil, fmt.Errorf("the server could not properly serve the CR subresources")
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && subresources != nil && subresources.Status != nil {
|
||||
equivalentResourceRegistry.RegisterKindFor(resource, "status", kind)
|
||||
|
||||
statusSpec = subresources.Status
|
||||
// for the status subresource, validate only against the status schema
|
||||
if validationSchema != nil && validationSchema.OpenAPIV3Schema != nil && validationSchema.OpenAPIV3Schema.Properties != nil {
|
||||
@ -569,6 +576,8 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
|
||||
var scaleSpec *apiextensions.CustomResourceSubresourceScale
|
||||
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && subresources != nil && subresources.Scale != nil {
|
||||
equivalentResourceRegistry.RegisterKindFor(resource, "scale", autoscalingv1.SchemeGroupVersion.WithKind("Scale"))
|
||||
|
||||
scaleSpec = subresources.Scale
|
||||
}
|
||||
|
||||
@ -583,8 +592,8 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
}
|
||||
|
||||
storages[v.Name] = customresource.NewStorage(
|
||||
schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Status.AcceptedNames.Plural},
|
||||
schema.GroupVersionKind{Group: crd.Spec.Group, Version: v.Name, Kind: crd.Status.AcceptedNames.Kind},
|
||||
resource.GroupResource(),
|
||||
kind,
|
||||
schema.GroupVersionKind{Group: crd.Spec.Group, Version: v.Name, Kind: crd.Status.AcceptedNames.ListKind},
|
||||
customresource.NewStrategy(
|
||||
typer,
|
||||
@ -640,6 +649,8 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
Typer: typer,
|
||||
UnsafeConvertor: unsafeConverter,
|
||||
|
||||
EquivalentResourceMapper: equivalentResourceRegistry,
|
||||
|
||||
Resource: schema.GroupVersionResource{Group: crd.Spec.Group, Version: v.Name, Resource: crd.Status.AcceptedNames.Plural},
|
||||
Kind: kind,
|
||||
|
||||
|
@ -15,6 +15,7 @@ go_test(
|
||||
"embedded_test.go",
|
||||
"extension_test.go",
|
||||
"local_scheme_test.go",
|
||||
"mapper_test.go",
|
||||
"scheme_test.go",
|
||||
"swagger_doc_generator_test.go",
|
||||
],
|
||||
@ -48,6 +49,7 @@ go_library(
|
||||
"generated.pb.go",
|
||||
"helper.go",
|
||||
"interfaces.go",
|
||||
"mapper.go",
|
||||
"register.go",
|
||||
"scheme.go",
|
||||
"scheme_builder.go",
|
||||
|
@ -210,6 +210,25 @@ type ObjectCreater interface {
|
||||
New(kind schema.GroupVersionKind) (out Object, err error)
|
||||
}
|
||||
|
||||
// EquivalentResourceMapper provides information about resources that address the same underlying data as a specified resource
|
||||
type EquivalentResourceMapper interface {
|
||||
// EquivalentResourcesFor returns a list of resources that address the same underlying data as resource.
|
||||
// If subresource is specified, only equivalent resources which also have the same subresource are included.
|
||||
// The specified resource can be included in the returned list.
|
||||
EquivalentResourcesFor(resource schema.GroupVersionResource, subresource string) []schema.GroupVersionResource
|
||||
// KindFor returns the kind expected by the specified resource[/subresource].
|
||||
// A zero value is returned if the kind is unknown.
|
||||
KindFor(resource schema.GroupVersionResource, subresource string) schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// EquivalentResourceRegistry provides an EquivalentResourceMapper interface,
|
||||
// and allows registering known resource[/subresource] -> kind
|
||||
type EquivalentResourceRegistry interface {
|
||||
EquivalentResourceMapper
|
||||
// RegisterKindFor registers the existence of the specified resource[/subresource] along with its expected kind.
|
||||
RegisterKindFor(resource schema.GroupVersionResource, subresource string, kind schema.GroupVersionKind)
|
||||
}
|
||||
|
||||
// ResourceVersioner provides methods for setting and retrieving
|
||||
// the resource version from an API object.
|
||||
type ResourceVersioner interface {
|
||||
|
98
staging/src/k8s.io/apimachinery/pkg/runtime/mapper.go
Normal file
98
staging/src/k8s.io/apimachinery/pkg/runtime/mapper.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type equivalentResourceRegistry struct {
|
||||
// keyFunc computes a key for the specified resource (this allows honoring colocated resources across API groups).
|
||||
// if null, or if "" is returned, resource.String() is used as the key
|
||||
keyFunc func(resource schema.GroupResource) string
|
||||
// resources maps key -> subresource -> equivalent resources (subresource is not included in the returned resources).
|
||||
// main resources are stored with subresource="".
|
||||
resources map[string]map[string][]schema.GroupVersionResource
|
||||
// kinds maps resource -> subresource -> kind
|
||||
kinds map[schema.GroupVersionResource]map[string]schema.GroupVersionKind
|
||||
// keys caches the computed key for each GroupResource
|
||||
keys map[schema.GroupResource]string
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
var _ EquivalentResourceMapper = (*equivalentResourceRegistry)(nil)
|
||||
var _ EquivalentResourceRegistry = (*equivalentResourceRegistry)(nil)
|
||||
|
||||
// NewEquivalentResourceRegistry creates a resource registry that considers all versions of a GroupResource to be equivalent.
|
||||
func NewEquivalentResourceRegistry() EquivalentResourceRegistry {
|
||||
return &equivalentResourceRegistry{}
|
||||
}
|
||||
|
||||
// NewEquivalentResourceRegistryWithIdentity creates a resource mapper with a custom identity function.
|
||||
// If "" is returned by the function, GroupResource#String is used as the identity.
|
||||
// GroupResources with the same identity string are considered equivalent.
|
||||
func NewEquivalentResourceRegistryWithIdentity(keyFunc func(schema.GroupResource) string) EquivalentResourceRegistry {
|
||||
return &equivalentResourceRegistry{keyFunc: keyFunc}
|
||||
}
|
||||
|
||||
func (r *equivalentResourceRegistry) EquivalentResourcesFor(resource schema.GroupVersionResource, subresource string) []schema.GroupVersionResource {
|
||||
r.mutex.RLock()
|
||||
defer r.mutex.RUnlock()
|
||||
return r.resources[r.keys[resource.GroupResource()]][subresource]
|
||||
}
|
||||
func (r *equivalentResourceRegistry) KindFor(resource schema.GroupVersionResource, subresource string) schema.GroupVersionKind {
|
||||
r.mutex.RLock()
|
||||
defer r.mutex.RUnlock()
|
||||
return r.kinds[resource][subresource]
|
||||
}
|
||||
func (r *equivalentResourceRegistry) RegisterKindFor(resource schema.GroupVersionResource, subresource string, kind schema.GroupVersionKind) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
if r.kinds == nil {
|
||||
r.kinds = map[schema.GroupVersionResource]map[string]schema.GroupVersionKind{}
|
||||
}
|
||||
if r.kinds[resource] == nil {
|
||||
r.kinds[resource] = map[string]schema.GroupVersionKind{}
|
||||
}
|
||||
r.kinds[resource][subresource] = kind
|
||||
|
||||
// get the shared key of the parent resource
|
||||
key := ""
|
||||
gr := resource.GroupResource()
|
||||
if r.keyFunc != nil {
|
||||
key = r.keyFunc(gr)
|
||||
}
|
||||
if key == "" {
|
||||
key = gr.String()
|
||||
}
|
||||
|
||||
if r.keys == nil {
|
||||
r.keys = map[schema.GroupResource]string{}
|
||||
}
|
||||
r.keys[gr] = key
|
||||
|
||||
if r.resources == nil {
|
||||
r.resources = map[string]map[string][]schema.GroupVersionResource{}
|
||||
}
|
||||
if r.resources[key] == nil {
|
||||
r.resources[key] = map[string][]schema.GroupVersionResource{}
|
||||
}
|
||||
r.resources[key][subresource] = append(r.resources[key][subresource], resource)
|
||||
}
|
132
staging/src/k8s.io/apimachinery/pkg/runtime/mapper_test.go
Normal file
132
staging/src/k8s.io/apimachinery/pkg/runtime/mapper_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
)
|
||||
|
||||
func TestResourceMapper(t *testing.T) {
|
||||
gvr := func(g, v, r string) schema.GroupVersionResource { return schema.GroupVersionResource{g, v, r} }
|
||||
|
||||
gvk := func(g, v, k string) schema.GroupVersionKind { return schema.GroupVersionKind{g, v, k} }
|
||||
|
||||
kindsToRegister := []struct {
|
||||
gvr schema.GroupVersionResource
|
||||
subresource string
|
||||
gvk schema.GroupVersionKind
|
||||
}{
|
||||
// pods
|
||||
{gvr("", "v1", "pods"), "", gvk("", "v1", "Pod")},
|
||||
// pods/status
|
||||
{gvr("", "v1", "pods"), "status", gvk("", "v1", "Pod")},
|
||||
// deployments
|
||||
{gvr("apps", "v1", "deployments"), "", gvk("apps", "v1", "Deployment")},
|
||||
{gvr("apps", "v1beta1", "deployments"), "", gvk("apps", "v1beta1", "Deployment")},
|
||||
{gvr("apps", "v1alpha1", "deployments"), "", gvk("apps", "v1alpha1", "Deployment")},
|
||||
{gvr("extensions", "v1beta1", "deployments"), "", gvk("extensions", "v1beta1", "Deployment")},
|
||||
// deployments/scale (omitted for apps/v1alpha1)
|
||||
{gvr("apps", "v1", "deployments"), "scale", gvk("", "", "Scale")},
|
||||
{gvr("apps", "v1beta1", "deployments"), "scale", gvk("", "", "Scale")},
|
||||
{gvr("extensions", "v1beta1", "deployments"), "scale", gvk("", "", "Scale")},
|
||||
// deployments/status (omitted for apps/v1alpha1)
|
||||
{gvr("apps", "v1", "deployments"), "status", gvk("apps", "v1", "Deployment")},
|
||||
{gvr("apps", "v1beta1", "deployments"), "status", gvk("apps", "v1beta1", "Deployment")},
|
||||
{gvr("extensions", "v1beta1", "deployments"), "status", gvk("extensions", "v1beta1", "Deployment")},
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
Name string
|
||||
IdentityFunc func(schema.GroupResource) string
|
||||
ResourcesForV1Deployment []schema.GroupVersionResource
|
||||
ResourcesForV1DeploymentScale []schema.GroupVersionResource
|
||||
ResourcesForV1DeploymentStatus []schema.GroupVersionResource
|
||||
}{
|
||||
{
|
||||
Name: "no identityfunc",
|
||||
ResourcesForV1Deployment: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("apps", "v1alpha1", "deployments")},
|
||||
ResourcesForV1DeploymentScale: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments")},
|
||||
ResourcesForV1DeploymentStatus: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments")},
|
||||
},
|
||||
{
|
||||
Name: "empty identityfunc",
|
||||
IdentityFunc: func(schema.GroupResource) string { return "" },
|
||||
// same group
|
||||
ResourcesForV1Deployment: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("apps", "v1alpha1", "deployments")},
|
||||
ResourcesForV1DeploymentScale: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments")},
|
||||
ResourcesForV1DeploymentStatus: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments")},
|
||||
},
|
||||
{
|
||||
Name: "common identityfunc",
|
||||
IdentityFunc: func(schema.GroupResource) string { return "x" },
|
||||
// all resources are seen as equivalent
|
||||
ResourcesForV1Deployment: []schema.GroupVersionResource{gvr("", "v1", "pods"), gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("apps", "v1alpha1", "deployments"), gvr("extensions", "v1beta1", "deployments")},
|
||||
// all resources with scale are seen as equivalent
|
||||
ResourcesForV1DeploymentScale: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("extensions", "v1beta1", "deployments")},
|
||||
// all resources with status are seen as equivalent
|
||||
ResourcesForV1DeploymentStatus: []schema.GroupVersionResource{gvr("", "v1", "pods"), gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("extensions", "v1beta1", "deployments")},
|
||||
},
|
||||
{
|
||||
Name: "colocated deployments",
|
||||
IdentityFunc: func(resource schema.GroupResource) string {
|
||||
if resource.Resource == "deployments" {
|
||||
return "deployments"
|
||||
}
|
||||
return ""
|
||||
},
|
||||
// all deployments are seen as equivalent
|
||||
ResourcesForV1Deployment: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("apps", "v1alpha1", "deployments"), gvr("extensions", "v1beta1", "deployments")},
|
||||
// all deployments with scale are seen as equivalent
|
||||
ResourcesForV1DeploymentScale: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("extensions", "v1beta1", "deployments")},
|
||||
// all deployments with status are seen as equivalent
|
||||
ResourcesForV1DeploymentStatus: []schema.GroupVersionResource{gvr("apps", "v1", "deployments"), gvr("apps", "v1beta1", "deployments"), gvr("extensions", "v1beta1", "deployments")},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
mapper := NewEquivalentResourceRegistryWithIdentity(tc.IdentityFunc)
|
||||
|
||||
// register
|
||||
for _, data := range kindsToRegister {
|
||||
mapper.RegisterKindFor(data.gvr, data.subresource, data.gvk)
|
||||
}
|
||||
// verify
|
||||
for _, data := range kindsToRegister {
|
||||
if kind := mapper.KindFor(data.gvr, data.subresource); kind != data.gvk {
|
||||
t.Errorf("KindFor(%#v, %v) returned %#v, expected %#v", data.gvr, data.subresource, kind, data.gvk)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify equivalents to primary resource
|
||||
if resources := mapper.EquivalentResourcesFor(gvr("apps", "v1", "deployments"), ""); !reflect.DeepEqual(resources, tc.ResourcesForV1Deployment) {
|
||||
t.Errorf("diff:\n%s", diff.ObjectReflectDiff(tc.ResourcesForV1Deployment, resources))
|
||||
}
|
||||
// Verify equivalents to subresources
|
||||
if resources := mapper.EquivalentResourcesFor(gvr("apps", "v1", "deployments"), "scale"); !reflect.DeepEqual(resources, tc.ResourcesForV1DeploymentScale) {
|
||||
t.Errorf("diff:\n%s", diff.ObjectReflectDiff(tc.ResourcesForV1DeploymentScale, resources))
|
||||
}
|
||||
if resources := mapper.EquivalentResourcesFor(gvr("apps", "v1", "deployments"), "status"); !reflect.DeepEqual(resources, tc.ResourcesForV1DeploymentStatus) {
|
||||
t.Errorf("diff:\n%s", diff.ObjectReflectDiff(tc.ResourcesForV1DeploymentStatus, resources))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -76,6 +76,8 @@ type ObjectInterfaces interface {
|
||||
GetObjectDefaulter() runtime.ObjectDefaulter
|
||||
// GetObjectConvertor is the ObjectConvertor appropriate for the requested object.
|
||||
GetObjectConvertor() runtime.ObjectConvertor
|
||||
// GetEquivalentResourceMapper is the EquivalentResourceMapper appropriate for finding equivalent resources and expected kind for the requested object.
|
||||
GetEquivalentResourceMapper() runtime.EquivalentResourceMapper
|
||||
}
|
||||
|
||||
// privateAnnotationsGetter is a private interface which allows users to get annotations from Attributes.
|
||||
|
@ -23,10 +23,11 @@ type RuntimeObjectInterfaces struct {
|
||||
runtime.ObjectTyper
|
||||
runtime.ObjectDefaulter
|
||||
runtime.ObjectConvertor
|
||||
runtime.EquivalentResourceMapper
|
||||
}
|
||||
|
||||
func NewObjectInterfacesFromScheme(scheme *runtime.Scheme) ObjectInterfaces {
|
||||
return &RuntimeObjectInterfaces{scheme, scheme, scheme, scheme}
|
||||
return &RuntimeObjectInterfaces{scheme, scheme, scheme, scheme, runtime.NewEquivalentResourceRegistry()}
|
||||
}
|
||||
|
||||
func (r *RuntimeObjectInterfaces) GetObjectCreater() runtime.ObjectCreater {
|
||||
@ -41,3 +42,6 @@ func (r *RuntimeObjectInterfaces) GetObjectDefaulter() runtime.ObjectDefaulter {
|
||||
func (r *RuntimeObjectInterfaces) GetObjectConvertor() runtime.ObjectConvertor {
|
||||
return r.ObjectConvertor
|
||||
}
|
||||
func (r *RuntimeObjectInterfaces) GetEquivalentResourceMapper() runtime.EquivalentResourceMapper {
|
||||
return r.EquivalentResourceMapper
|
||||
}
|
||||
|
@ -229,6 +229,8 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
|
||||
Linker: selfLinker,
|
||||
RootScopedKinds: sets.NewString("SimpleRoot"),
|
||||
|
||||
EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
|
||||
|
||||
ParameterCodec: parameterCodec,
|
||||
|
||||
Admit: admissionControl,
|
||||
@ -3554,6 +3556,8 @@ func TestParentResourceIsRequired(t *testing.T) {
|
||||
Linker: selfLinker,
|
||||
RootScopedKinds: sets.NewString("SimpleRoot"),
|
||||
|
||||
EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
|
||||
|
||||
Admit: admissionControl,
|
||||
|
||||
GroupVersion: newGroupVersion,
|
||||
@ -3584,6 +3588,8 @@ func TestParentResourceIsRequired(t *testing.T) {
|
||||
Typer: scheme,
|
||||
Linker: selfLinker,
|
||||
|
||||
EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
|
||||
|
||||
Admit: admissionControl,
|
||||
|
||||
GroupVersion: newGroupVersion,
|
||||
@ -4301,6 +4307,8 @@ func TestXGSubresource(t *testing.T) {
|
||||
Typer: scheme,
|
||||
Linker: selfLinker,
|
||||
|
||||
EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
|
||||
|
||||
ParameterCodec: parameterCodec,
|
||||
|
||||
Admit: admissionControl,
|
||||
|
@ -72,6 +72,8 @@ type APIGroupVersion struct {
|
||||
Linker runtime.SelfLinker
|
||||
UnsafeConvertor runtime.ObjectConvertor
|
||||
|
||||
EquivalentResourceRegistry runtime.EquivalentResourceRegistry
|
||||
|
||||
// Authorizer determines whether a user is allowed to make a certain request. The Handler does a preliminary
|
||||
// authorization check using the request URI but it may be necessary to make additional checks, such as in
|
||||
// the create-on-update case
|
||||
|
@ -57,6 +57,8 @@ type RequestScope struct {
|
||||
UnsafeConvertor runtime.ObjectConvertor
|
||||
Authorizer authorizer.Authorizer
|
||||
|
||||
EquivalentResourceMapper runtime.EquivalentResourceMapper
|
||||
|
||||
TableConvertor rest.TableConvertor
|
||||
FieldManager *fieldmanager.FieldManager
|
||||
|
||||
@ -108,6 +110,9 @@ func (r *RequestScope) GetObjectCreater() runtime.ObjectCreater { return r.C
|
||||
func (r *RequestScope) GetObjectTyper() runtime.ObjectTyper { return r.Typer }
|
||||
func (r *RequestScope) GetObjectDefaulter() runtime.ObjectDefaulter { return r.Defaulter }
|
||||
func (r *RequestScope) GetObjectConvertor() runtime.ObjectConvertor { return r.Convertor }
|
||||
func (r *RequestScope) GetEquivalentResourceMapper() runtime.EquivalentResourceMapper {
|
||||
return r.EquivalentResourceMapper
|
||||
}
|
||||
|
||||
// ConnectResource returns a function that handles a connect request on a rest.Storage object.
|
||||
func ConnectResource(connecter rest.Connecter, scope *RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc {
|
||||
|
@ -532,6 +532,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
UnsafeConvertor: a.group.UnsafeConvertor,
|
||||
Authorizer: a.group.Authorizer,
|
||||
|
||||
EquivalentResourceMapper: a.group.EquivalentResourceRegistry,
|
||||
|
||||
// TODO: Check for the interface on storage
|
||||
TableConvertor: tableProvider,
|
||||
|
||||
@ -925,6 +927,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
apiResource.Kind = gvk.Kind
|
||||
}
|
||||
|
||||
// Record the existence of the GVR and the corresponding GVK
|
||||
a.group.EquivalentResourceRegistry.RegisterKindFor(reqScope.Resource, reqScope.Subresource, fqKindToRegister)
|
||||
|
||||
return &apiResource, nil
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import (
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
|
||||
@ -188,6 +189,10 @@ type Config struct {
|
||||
// kube-proxy, services, etc.) can reach the GenericAPIServer.
|
||||
// If nil or 0.0.0.0, the host's default interface will be used.
|
||||
PublicAddress net.IP
|
||||
|
||||
// EquivalentResourceRegistry provides information about resources equivalent to a given resource,
|
||||
// and the kind associated with a given resource. As resources are installed, they are registered here.
|
||||
EquivalentResourceRegistry runtime.EquivalentResourceRegistry
|
||||
}
|
||||
|
||||
type RecommendedConfig struct {
|
||||
@ -417,6 +422,21 @@ func (c *Config) Complete(informers informers.SharedInformerFactory) CompletedCo
|
||||
c.RequestInfoResolver = NewRequestInfoResolver(c)
|
||||
}
|
||||
|
||||
if c.EquivalentResourceRegistry == nil {
|
||||
if c.RESTOptionsGetter == nil {
|
||||
c.EquivalentResourceRegistry = runtime.NewEquivalentResourceRegistry()
|
||||
} else {
|
||||
c.EquivalentResourceRegistry = runtime.NewEquivalentResourceRegistryWithIdentity(func(groupResource schema.GroupResource) string {
|
||||
// use the storage prefix as the key if possible
|
||||
if opts, err := c.RESTOptionsGetter.GetRESTOptions(groupResource); err == nil {
|
||||
return opts.ResourcePrefix
|
||||
}
|
||||
// otherwise return "" to use the default key (parent GV name)
|
||||
return ""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedConfig{&completedConfig{c, informers}}
|
||||
}
|
||||
|
||||
@ -436,6 +456,9 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
||||
if c.LoopbackClientConfig == nil {
|
||||
return nil, fmt.Errorf("Genericapiserver.New() called with config.LoopbackClientConfig == nil")
|
||||
}
|
||||
if c.EquivalentResourceRegistry == nil {
|
||||
return nil, fmt.Errorf("Genericapiserver.New() called with config.EquivalentResourceRegistry == nil")
|
||||
}
|
||||
|
||||
handlerChainBuilder := func(handler http.Handler) http.Handler {
|
||||
return c.BuildHandlerChainFunc(handler, c.Config)
|
||||
@ -443,15 +466,16 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
||||
apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
|
||||
|
||||
s := &GenericAPIServer{
|
||||
discoveryAddresses: c.DiscoveryAddresses,
|
||||
LoopbackClientConfig: c.LoopbackClientConfig,
|
||||
legacyAPIGroupPrefixes: c.LegacyAPIGroupPrefixes,
|
||||
admissionControl: c.AdmissionControl,
|
||||
Serializer: c.Serializer,
|
||||
AuditBackend: c.AuditBackend,
|
||||
Authorizer: c.Authorization.Authorizer,
|
||||
delegationTarget: delegationTarget,
|
||||
HandlerChainWaitGroup: c.HandlerChainWaitGroup,
|
||||
discoveryAddresses: c.DiscoveryAddresses,
|
||||
LoopbackClientConfig: c.LoopbackClientConfig,
|
||||
legacyAPIGroupPrefixes: c.LegacyAPIGroupPrefixes,
|
||||
admissionControl: c.AdmissionControl,
|
||||
Serializer: c.Serializer,
|
||||
AuditBackend: c.AuditBackend,
|
||||
Authorizer: c.Authorization.Authorizer,
|
||||
delegationTarget: delegationTarget,
|
||||
EquivalentResourceRegistry: c.EquivalentResourceRegistry,
|
||||
HandlerChainWaitGroup: c.HandlerChainWaitGroup,
|
||||
|
||||
minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
|
||||
ShutdownTimeout: c.RequestTimeout,
|
||||
|
@ -157,6 +157,10 @@ type GenericAPIServer struct {
|
||||
// the create-on-update case
|
||||
Authorizer authorizer.Authorizer
|
||||
|
||||
// EquivalentResourceRegistry provides information about resources equivalent to a given resource,
|
||||
// and the kind associated with a given resource. As resources are installed, they are registered here.
|
||||
EquivalentResourceRegistry runtime.EquivalentResourceRegistry
|
||||
|
||||
// enableAPIResponseCompression indicates whether API Responses should support compression
|
||||
// if the client requests it via Accept-Encoding
|
||||
enableAPIResponseCompression bool
|
||||
@ -467,6 +471,8 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
|
||||
Typer: apiGroupInfo.Scheme,
|
||||
Linker: runtime.SelfLinker(meta.NewAccessor()),
|
||||
|
||||
EquivalentResourceRegistry: s.EquivalentResourceRegistry,
|
||||
|
||||
Admit: s.admissionControl,
|
||||
MinRequestTimeout: s.minRequestTimeout,
|
||||
EnableAPIResponseCompression: s.enableAPIResponseCompression,
|
||||
|
Loading…
Reference in New Issue
Block a user