Skip to content

Commit 3a3be8c

Browse files
committed
controlplane: add generic storage construction
Signed-off-by: Dr. Stefan Schimanski <[email protected]>
1 parent 548d50d commit 3a3be8c

File tree

3 files changed

+186
-66
lines changed

3 files changed

+186
-66
lines changed

pkg/controlplane/apiserver/apis.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,20 @@ import (
2222
"k8s.io/apiserver/pkg/registry/generic"
2323
genericapiserver "k8s.io/apiserver/pkg/server"
2424
serverstorage "k8s.io/apiserver/pkg/server/storage"
25+
"k8s.io/client-go/discovery"
2526
"k8s.io/klog/v2"
27+
svmrest "k8s.io/kubernetes/pkg/registry/storagemigration/rest"
28+
29+
admissionregistrationrest "k8s.io/kubernetes/pkg/registry/admissionregistration/rest"
30+
apiserverinternalrest "k8s.io/kubernetes/pkg/registry/apiserverinternal/rest"
31+
authenticationrest "k8s.io/kubernetes/pkg/registry/authentication/rest"
32+
authorizationrest "k8s.io/kubernetes/pkg/registry/authorization/rest"
33+
certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest"
34+
coordinationrest "k8s.io/kubernetes/pkg/registry/coordination/rest"
35+
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
36+
eventsrest "k8s.io/kubernetes/pkg/registry/events/rest"
37+
flowcontrolrest "k8s.io/kubernetes/pkg/registry/flowcontrol/rest"
38+
rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
2639
)
2740

2841
// RESTStorageProvider is a factory type for REST storage.
@@ -31,6 +44,45 @@ type RESTStorageProvider interface {
3144
NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, error)
3245
}
3346

47+
// NewCoreGenericConfig returns a new core rest generic config.
48+
func (c *CompletedConfig) NewCoreGenericConfig() *corerest.GenericConfig {
49+
return &corerest.GenericConfig{
50+
StorageFactory: c.Extra.StorageFactory,
51+
EventTTL: c.Extra.EventTTL,
52+
LoopbackClientConfig: c.Generic.LoopbackClientConfig,
53+
ServiceAccountIssuer: c.Extra.ServiceAccountIssuer,
54+
ExtendExpiration: c.Extra.ExtendExpiration,
55+
ServiceAccountMaxExpiration: c.Extra.ServiceAccountMaxExpiration,
56+
APIAudiences: c.Generic.Authentication.APIAudiences,
57+
Informers: c.Extra.VersionedInformers,
58+
}
59+
}
60+
61+
// GenericStorageProviders returns a set of APIs for a generic control plane.
62+
// They ought to be a subset of those served by kube-apiserver.
63+
func (c *CompletedConfig) GenericStorageProviders(discovery discovery.DiscoveryInterface) ([]RESTStorageProvider, error) {
64+
// The order here is preserved in discovery.
65+
// If resources with identical names exist in more than one of these groups (e.g. "deployments.apps"" and "deployments.extensions"),
66+
// the order of this list determines which group an unqualified resource name (e.g. "deployments") should prefer.
67+
// This priority order is used for local discovery, but it ends up aggregated in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go
68+
// with specific priorities.
69+
// TODO: describe the priority all the way down in the RESTStorageProviders and plumb it back through the various discovery
70+
// handlers that we have.
71+
return []RESTStorageProvider{
72+
c.NewCoreGenericConfig(),
73+
apiserverinternalrest.StorageProvider{},
74+
authenticationrest.RESTStorageProvider{Authenticator: c.Generic.Authentication.Authenticator, APIAudiences: c.Generic.Authentication.APIAudiences},
75+
authorizationrest.RESTStorageProvider{Authorizer: c.Generic.Authorization.Authorizer, RuleResolver: c.Generic.RuleResolver},
76+
certificatesrest.RESTStorageProvider{},
77+
coordinationrest.RESTStorageProvider{},
78+
rbacrest.RESTStorageProvider{Authorizer: c.Generic.Authorization.Authorizer},
79+
svmrest.RESTStorageProvider{},
80+
flowcontrolrest.RESTStorageProvider{InformerFactory: c.Generic.SharedInformerFactory},
81+
admissionregistrationrest.RESTStorageProvider{Authorizer: c.Generic.Authorization.Authorizer, DiscoveryClient: discovery},
82+
eventsrest.RESTStorageProvider{TTL: c.EventTTL},
83+
}, nil
84+
}
85+
3486
// InstallAPIs will install the APIs for the restStorageProviders if they are enabled.
3587
func (s *Server) InstallAPIs(restStorageProviders ...RESTStorageProvider) error {
3688
nonLegacy := []*genericapiserver.APIGroupInfo{}

pkg/controlplane/instance.go

Lines changed: 55 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import (
5858
genericapiserver "k8s.io/apiserver/pkg/server"
5959
serverstorage "k8s.io/apiserver/pkg/server/storage"
6060
utilfeature "k8s.io/apiserver/pkg/util/feature"
61+
clientdiscovery "k8s.io/client-go/discovery"
6162
"k8s.io/client-go/kubernetes"
6263
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
6364
discoveryclient "k8s.io/client-go/kubernetes/typed/discovery/v1"
@@ -322,67 +323,11 @@ func (c CompletedConfig) New(delegationTarget genericapiserver.DelegationTarget)
322323
return nil, err
323324
}
324325

325-
// TODO: update to a version that caches success but will recheck on failure, unlike memcache discovery
326-
discoveryClientForAdmissionRegistration := client.Discovery()
327-
328-
legacyRESTStorageProvider, err := corerest.New(corerest.Config{
329-
GenericConfig: corerest.GenericConfig{
330-
StorageFactory: c.ControlPlane.Extra.StorageFactory,
331-
EventTTL: c.ControlPlane.Extra.EventTTL,
332-
LoopbackClientConfig: c.ControlPlane.Generic.LoopbackClientConfig,
333-
ServiceAccountIssuer: c.ControlPlane.Extra.ServiceAccountIssuer,
334-
ExtendExpiration: c.ControlPlane.Extra.ExtendExpiration,
335-
ServiceAccountMaxExpiration: c.ControlPlane.Extra.ServiceAccountMaxExpiration,
336-
APIAudiences: c.ControlPlane.Generic.Authentication.APIAudiences,
337-
Informers: c.ControlPlane.Extra.VersionedInformers,
338-
},
339-
Proxy: corerest.ProxyConfig{
340-
Transport: c.ControlPlane.Extra.ProxyTransport,
341-
KubeletClientConfig: c.Extra.KubeletClientConfig,
342-
},
343-
Services: corerest.ServicesConfig{
344-
ClusterIPRange: c.Extra.ServiceIPRange,
345-
SecondaryClusterIPRange: c.Extra.SecondaryServiceIPRange,
346-
NodePortRange: c.Extra.ServiceNodePortRange,
347-
IPRepairInterval: c.Extra.RepairServicesInterval,
348-
},
349-
})
326+
restStorageProviders, err := c.StorageProviders(client.Discovery())
350327
if err != nil {
351328
return nil, err
352329
}
353330

354-
// The order here is preserved in discovery.
355-
// If resources with identical names exist in more than one of these groups (e.g. "deployments.apps"" and "deployments.extensions"),
356-
// the order of this list determines which group an unqualified resource name (e.g. "deployments") should prefer.
357-
// This priority order is used for local discovery, but it ends up aggregated in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go
358-
// with specific priorities.
359-
// TODO: describe the priority all the way down in the RESTStorageProviders and plumb it back through the various discovery
360-
// handlers that we have.
361-
restStorageProviders := []controlplaneapiserver.RESTStorageProvider{
362-
legacyRESTStorageProvider,
363-
apiserverinternalrest.StorageProvider{},
364-
authenticationrest.RESTStorageProvider{Authenticator: c.ControlPlane.Generic.Authentication.Authenticator, APIAudiences: c.ControlPlane.Generic.Authentication.APIAudiences},
365-
authorizationrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer, RuleResolver: c.ControlPlane.Generic.RuleResolver},
366-
autoscalingrest.RESTStorageProvider{},
367-
batchrest.RESTStorageProvider{},
368-
certificatesrest.RESTStorageProvider{},
369-
coordinationrest.RESTStorageProvider{},
370-
discoveryrest.StorageProvider{},
371-
networkingrest.RESTStorageProvider{},
372-
noderest.RESTStorageProvider{},
373-
policyrest.RESTStorageProvider{},
374-
rbacrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer},
375-
schedulingrest.RESTStorageProvider{},
376-
storagerest.RESTStorageProvider{},
377-
svmrest.RESTStorageProvider{},
378-
flowcontrolrest.RESTStorageProvider{InformerFactory: c.ControlPlane.Generic.SharedInformerFactory},
379-
// keep apps after extensions so legacy clients resolve the extensions versions of shared resource names.
380-
// See https://github.com/kubernetes/kubernetes/issues/42392
381-
appsrest.StorageProvider{},
382-
admissionregistrationrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer, DiscoveryClient: discoveryClientForAdmissionRegistration},
383-
eventsrest.RESTStorageProvider{TTL: c.ControlPlane.EventTTL},
384-
resourcerest.RESTStorageProvider{},
385-
}
386331
if err := s.ControlPlane.InstallAPIs(restStorageProviders...); err != nil {
387332
return nil, err
388333
}
@@ -426,6 +371,59 @@ func (c CompletedConfig) New(delegationTarget genericapiserver.DelegationTarget)
426371
}
427372

428373
return s, nil
374+
375+
}
376+
377+
func (c CompletedConfig) StorageProviders(discovery clientdiscovery.DiscoveryInterface) ([]controlplaneapiserver.RESTStorageProvider, error) {
378+
legacyRESTStorageProvider, err := corerest.New(corerest.Config{
379+
GenericConfig: *c.ControlPlane.NewCoreGenericConfig(),
380+
Proxy: corerest.ProxyConfig{
381+
Transport: c.ControlPlane.Extra.ProxyTransport,
382+
KubeletClientConfig: c.Extra.KubeletClientConfig,
383+
},
384+
Services: corerest.ServicesConfig{
385+
ClusterIPRange: c.Extra.ServiceIPRange,
386+
SecondaryClusterIPRange: c.Extra.SecondaryServiceIPRange,
387+
NodePortRange: c.Extra.ServiceNodePortRange,
388+
IPRepairInterval: c.Extra.RepairServicesInterval,
389+
},
390+
})
391+
if err != nil {
392+
return nil, err
393+
}
394+
395+
// The order here is preserved in discovery.
396+
// If resources with identical names exist in more than one of these groups (e.g. "deployments.apps"" and "deployments.extensions"),
397+
// the order of this list determines which group an unqualified resource name (e.g. "deployments") should prefer.
398+
// This priority order is used for local discovery, but it ends up aggregated in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go
399+
// with specific priorities.
400+
// TODO: describe the priority all the way down in the RESTStorageProviders and plumb it back through the various discovery
401+
// handlers that we have.
402+
return []controlplaneapiserver.RESTStorageProvider{
403+
legacyRESTStorageProvider,
404+
apiserverinternalrest.StorageProvider{},
405+
authenticationrest.RESTStorageProvider{Authenticator: c.ControlPlane.Generic.Authentication.Authenticator, APIAudiences: c.ControlPlane.Generic.Authentication.APIAudiences},
406+
authorizationrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer, RuleResolver: c.ControlPlane.Generic.RuleResolver},
407+
autoscalingrest.RESTStorageProvider{},
408+
batchrest.RESTStorageProvider{},
409+
certificatesrest.RESTStorageProvider{},
410+
coordinationrest.RESTStorageProvider{},
411+
discoveryrest.StorageProvider{},
412+
networkingrest.RESTStorageProvider{},
413+
noderest.RESTStorageProvider{},
414+
policyrest.RESTStorageProvider{},
415+
rbacrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer},
416+
schedulingrest.RESTStorageProvider{},
417+
storagerest.RESTStorageProvider{},
418+
svmrest.RESTStorageProvider{},
419+
flowcontrolrest.RESTStorageProvider{InformerFactory: c.ControlPlane.Generic.SharedInformerFactory},
420+
// keep apps after extensions so legacy clients resolve the extensions versions of shared resource names.
421+
// See https://github.com/kubernetes/kubernetes/issues/42392
422+
appsrest.StorageProvider{},
423+
admissionregistrationrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer, DiscoveryClient: discovery},
424+
eventsrest.RESTStorageProvider{TTL: c.ControlPlane.EventTTL},
425+
resourcerest.RESTStorageProvider{},
426+
}, nil
429427
}
430428

431429
var (

pkg/controlplane/instance_test.go

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
"testing"
3030

3131
"github.com/stretchr/testify/assert"
32+
autoscalingrest "k8s.io/kubernetes/pkg/registry/autoscaling/rest"
33+
resourcerest "k8s.io/kubernetes/pkg/registry/resource/rest"
3234

3335
autoscalingapiv2beta1 "k8s.io/api/autoscaling/v2beta1"
3436
autoscalingapiv2beta2 "k8s.io/api/autoscaling/v2beta2"
@@ -69,9 +71,17 @@ import (
6971
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
7072
"k8s.io/kubernetes/pkg/kubeapiserver"
7173
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
74+
appsrest "k8s.io/kubernetes/pkg/registry/apps/rest"
75+
batchrest "k8s.io/kubernetes/pkg/registry/batch/rest"
7276
certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest"
7377
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
78+
discoveryrest "k8s.io/kubernetes/pkg/registry/discovery/rest"
79+
networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest"
80+
noderest "k8s.io/kubernetes/pkg/registry/node/rest"
81+
policyrest "k8s.io/kubernetes/pkg/registry/policy/rest"
7482
"k8s.io/kubernetes/pkg/registry/registrytest"
83+
schedulingrest "k8s.io/kubernetes/pkg/registry/scheduling/rest"
84+
storagerest "k8s.io/kubernetes/pkg/registry/storage/rest"
7585
)
7686

7787
// setUp is a convenience function for setting up for (most) tests.
@@ -159,12 +169,7 @@ func TestLegacyRestStorageStrategies(t *testing.T) {
159169
defer etcdserver.Terminate(t)
160170

161171
storageProvider, err := corerest.New(corerest.Config{
162-
GenericConfig: corerest.GenericConfig{
163-
StorageFactory: apiserverCfg.ControlPlane.Extra.StorageFactory,
164-
EventTTL: apiserverCfg.ControlPlane.Extra.EventTTL,
165-
LoopbackClientConfig: apiserverCfg.ControlPlane.Generic.LoopbackClientConfig,
166-
Informers: apiserverCfg.ControlPlane.Extra.VersionedInformers,
167-
},
172+
GenericConfig: *apiserverCfg.ControlPlane.NewCoreGenericConfig(),
168173
Proxy: corerest.ProxyConfig{
169174
Transport: apiserverCfg.ControlPlane.Extra.ProxyTransport,
170175
KubeletClientConfig: apiserverCfg.Extra.KubeletClientConfig,
@@ -206,15 +211,16 @@ func TestCertificatesRestStorageStrategies(t *testing.T) {
206211
}
207212
}
208213

209-
func newInstance(t *testing.T) (*Instance, *etcd3testing.EtcdTestServer, Config, *assert.Assertions) {
214+
func newInstance(t *testing.T) (*Instance, *etcd3testing.EtcdTestServer, CompletedConfig, *assert.Assertions) {
210215
etcdserver, config, assert := setUp(t)
211216

212-
apiserver, err := config.Complete().New(genericapiserver.NewEmptyDelegate())
217+
completed := config.Complete()
218+
apiserver, err := completed.New(genericapiserver.NewEmptyDelegate())
213219
if err != nil {
214220
t.Fatalf("Error in bringing up the master: %v", err)
215221
}
216222

217-
return apiserver, etcdserver, config, assert
223+
return apiserver, etcdserver, completed, assert
218224
}
219225

220226
// TestVersion tests /version
@@ -480,3 +486,67 @@ func TestNewBetaResourcesEnabledByDefault(t *testing.T) {
480486
t.Errorf("no new beta resources can be enabled by default, see https://github.com/kubernetes/enhancements/blob/0ad0fc8269165ca300d05ca51c7ce190a79976a5/keps/sig-architecture/3136-beta-apis-off-by-default/README.md: %v", gvr)
481487
}
482488
}
489+
490+
// TestGenericStorageProviders is a smoke test that ensures that the kube
491+
// storage providers and the generic storage providers don't unexpectedly
492+
// divert, i.e. the later is an equally ordered subset.
493+
func TestGenericStorageProviders(t *testing.T) {
494+
_, config, _ := setUp(t)
495+
completed := config.Complete()
496+
497+
// create kube storage providers
498+
client, err := kubernetes.NewForConfig(config.ControlPlane.Generic.LoopbackClientConfig)
499+
if err != nil {
500+
t.Fatal(err)
501+
}
502+
kube, err := completed.StorageProviders(client.Discovery())
503+
if err != nil {
504+
t.Fatal(err)
505+
}
506+
507+
// create generic storage providers. These should be an equally ordered subset
508+
generic, err := completed.ControlPlane.GenericStorageProviders(client.Discovery())
509+
if err != nil {
510+
t.Fatal(err)
511+
}
512+
513+
g := 0 // generic index
514+
for k := range kube {
515+
kt := reflect.TypeOf(kube[k])
516+
var gt reflect.Type
517+
if g < len(generic) {
518+
gt = reflect.TypeOf(generic[g])
519+
}
520+
521+
// special case: we identify full core and generic core
522+
if kt.Kind() == reflect.Ptr && kt.Elem().PkgPath() == reflect.TypeOf(corerest.Config{}).PkgPath() {
523+
kt = reflect.TypeOf(&corerest.GenericConfig{})
524+
}
525+
526+
if kt == gt {
527+
g++
528+
continue
529+
}
530+
531+
switch kube[k].(type) {
532+
case autoscalingrest.RESTStorageProvider,
533+
batchrest.RESTStorageProvider,
534+
discoveryrest.StorageProvider,
535+
networkingrest.RESTStorageProvider,
536+
noderest.RESTStorageProvider,
537+
policyrest.RESTStorageProvider,
538+
schedulingrest.RESTStorageProvider,
539+
storagerest.RESTStorageProvider,
540+
appsrest.StorageProvider,
541+
resourcerest.RESTStorageProvider:
542+
// all these are non-generic, but kube specific
543+
continue
544+
default:
545+
t.Errorf("Unexpected, uncategorized storage %T from %s. Put into the list above for kube-specific APIs, or into GenericStorageProviders for generic APIs", kube[k], kt.PkgPath())
546+
}
547+
}
548+
549+
if g != len(generic) {
550+
t.Errorf("Unexpected, generic APIs found: %#v", generic[g:])
551+
}
552+
}

0 commit comments

Comments
 (0)