Add GetResourceMapper to admission ObjectInterfaces

This commit is contained in:
Jordan Liggitt 2019-05-13 11:24:20 -04:00
parent 9071d21e3b
commit 92f735042e
13 changed files with 330 additions and 12 deletions

View File

@ -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,

View File

@ -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",

View File

@ -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 {

View 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)
}

View 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))
}
})
}
}

View File

@ -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.

View File

@ -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
}

View File

@ -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,

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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,

View File

@ -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,