diff --git a/api/swagger-spec/resourceListing.json b/api/swagger-spec/resourceListing.json index 037c94acbc0..de827cb8406 100644 --- a/api/swagger-spec/resourceListing.json +++ b/api/swagger-spec/resourceListing.json @@ -25,6 +25,14 @@ "path": "/apis/extensions", "description": "get information of a group" }, + { + "path": "/apis/apps/v1alpha1", + "description": "API at /apis/apps/v1alpha1" + }, + { + "path": "/apis/apps", + "description": "get information of a group" + }, { "path": "/apis/autoscaling/v1", "description": "API at /apis/autoscaling/v1" @@ -45,22 +53,6 @@ "path": "/apis/batch", "description": "get information of a group" }, - { - "path": "/apis/policy/v1alpha1", - "description": "API at /apis/policy/v1alpha1" - }, - { - "path": "/apis/policy", - "description": "get information of a group" - }, - { - "path": "/apis/apps/v1alpha1", - "description": "API at /apis/apps/v1alpha1" - }, - { - "path": "/apis/apps", - "description": "get information of a group" - }, { "path": "/apis/certificates/v1alpha1", "description": "API at /apis/certificates/v1alpha1" @@ -69,6 +61,14 @@ "path": "/apis/certificates", "description": "get information of a group" }, + { + "path": "/apis/policy/v1alpha1", + "description": "API at /apis/policy/v1alpha1" + }, + { + "path": "/apis/policy", + "description": "get information of a group" + }, { "path": "/apis/rbac.authorization.k8s.io/v1alpha1", "description": "API at /apis/rbac.authorization.k8s.io/v1alpha1" diff --git a/pkg/genericapiserver/resource_config.go b/pkg/genericapiserver/resource_config.go index 15cdda2b72f..4d8ee7a9035 100644 --- a/pkg/genericapiserver/resource_config.go +++ b/pkg/genericapiserver/resource_config.go @@ -27,6 +27,7 @@ type APIResourceConfigSource interface { ResourceEnabled(resource unversioned.GroupVersionResource) bool AllResourcesForVersionEnabled(version unversioned.GroupVersion) bool AnyResourcesForVersionEnabled(version unversioned.GroupVersion) bool + AnyResourcesForGroupEnabled(group string) bool } // Specifies the overrides for various API group versions. @@ -170,3 +171,15 @@ func (o *ResourceConfig) AnyResourcesForVersionEnabled(version unversioned.Group return versionOverride.Enable } + +func (o *ResourceConfig) AnyResourcesForGroupEnabled(group string) bool { + for version := range o.GroupVersionResourceConfigs { + if version.Group == group { + if o.AnyResourcesForVersionEnabled(version) { + return true + } + } + } + + return false +} diff --git a/pkg/genericapiserver/resource_config_test.go b/pkg/genericapiserver/resource_config_test.go index 9dc389f0f4e..a73fbcc2883 100644 --- a/pkg/genericapiserver/resource_config_test.go +++ b/pkg/genericapiserver/resource_config_test.go @@ -97,3 +97,64 @@ func TestDisabledVersion(t *testing.T) { } } + +func TestAnyResourcesForGroupEnabled(t *testing.T) { + tests := []struct { + name string + creator func() APIResourceConfigSource + testGroup string + + expectedResult bool + }{ + { + name: "empty", + creator: func() APIResourceConfigSource { + return NewResourceConfig() + }, + testGroup: "one", + + expectedResult: false, + }, + { + name: "present, but disabled", + creator: func() APIResourceConfigSource { + ret := NewResourceConfig() + ret.DisableVersions(unversioned.GroupVersion{Group: "one", Version: "version1"}) + return ret + }, + testGroup: "one", + + expectedResult: false, + }, + { + name: "present, and one version enabled", + creator: func() APIResourceConfigSource { + ret := NewResourceConfig() + ret.DisableVersions(unversioned.GroupVersion{Group: "one", Version: "version1"}) + ret.EnableVersions(unversioned.GroupVersion{Group: "one", Version: "version2"}) + return ret + }, + testGroup: "one", + + expectedResult: true, + }, + { + name: "present, and one resource", + creator: func() APIResourceConfigSource { + ret := NewResourceConfig() + ret.DisableVersions(unversioned.GroupVersion{Group: "one", Version: "version1"}) + ret.EnableResources(unversioned.GroupVersionResource{Group: "one", Version: "version2", Resource: "foo"}) + return ret + }, + testGroup: "one", + + expectedResult: true, + }, + } + + for _, tc := range tests { + if e, a := tc.expectedResult, tc.creator().AnyResourcesForGroupEnabled(tc.testGroup); e != a { + t.Errorf("%s: expected %v, got %v", tc.name, e, a) + } + } +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 64b46206ca6..3636d92280f 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -107,6 +107,7 @@ import ( "k8s.io/kubernetes/pkg/storage" etcdmetrics "k8s.io/kubernetes/pkg/storage/etcd/metrics" etcdutil "k8s.io/kubernetes/pkg/storage/etcd/util" + "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/wait" daemonetcd "k8s.io/kubernetes/pkg/registry/daemonset/etcd" @@ -132,6 +133,8 @@ type Config struct { DeleteCollectionWorkers int EventTTL time.Duration KubeletClient kubeletclient.KubeletClient + // RESTStorageProviders provides RESTStorage building methods keyed by groupName + RESTStorageProviders map[string]RESTStorageProvider // Used to start and monitor tunneling Tunneler genericapiserver.Tunneler @@ -184,6 +187,9 @@ type thirdPartyEntry struct { group unversioned.APIGroup } +type RESTOptionsGetter func(resource unversioned.GroupResource) generic.RESTOptions +type RESTStorageProvider func(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) + // New returns a new instance of Master from the given config. // Certain config fields will be set to a default value if unset. // Certain config fields must be specified, including: @@ -206,6 +212,17 @@ func New(c *Config) (*Master, error) { disableThirdPartyControllerForTesting: c.disableThirdPartyControllerForTesting, } + + // Add some hardcoded storage for now. Append to the map. + if c.RESTStorageProviders == nil { + c.RESTStorageProviders = map[string]RESTStorageProvider{} + } + c.RESTStorageProviders[autoscaling.GroupName] = buildAutoscalingResources + c.RESTStorageProviders[batch.GroupName] = buildBatchResources + c.RESTStorageProviders[policy.GroupName] = buildPolicyResources + c.RESTStorageProviders[appsapi.GroupName] = buildAppsResources + c.RESTStorageProviders[rbac.GroupName] = buildRBACResources(c.AuthorizerRBACSuperUser) + c.RESTStorageProviders[certificates.GroupName] = buildCertificateResources m.InstallAPIs(c) // TODO: Attempt clean shutdown? @@ -297,130 +314,22 @@ func (m *Master) InstallAPIs(c *Config) { apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) } - // Install autoscaling unless disabled. - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv1.SchemeGroupVersion) { - autoscalingResources := m.getAutoscalingResources(c) - autoscalingGroupMeta := registered.GroupOrDie(autoscaling.GroupName) - - // Hard code preferred group version to autoscaling/v1 - autoscalingGroupMeta.GroupVersion = autoscalingapiv1.SchemeGroupVersion - - apiGroupInfo := genericapiserver.APIGroupInfo{ - GroupMeta: *autoscalingGroupMeta, - VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ - "v1": autoscalingResources, - }, - OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, - Scheme: api.Scheme, - ParameterCodec: api.ParameterCodec, - NegotiatedSerializer: api.Codecs, - } - apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) + restOptionsGetter := func(resource unversioned.GroupResource) generic.RESTOptions { + return m.GetRESTOptionsOrDie(c, resource) } - // Install batch unless disabled. - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) || - c.APIResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv2alpha1.SchemeGroupVersion) { - batchv1Resources := m.getBatchResources(c, batchapiv1.SchemeGroupVersion) - batchGroupMeta := registered.GroupOrDie(batch.GroupName) - - // Hard code preferred group version to batch/v1 - batchGroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion - - apiGroupInfo := genericapiserver.APIGroupInfo{ - GroupMeta: *batchGroupMeta, - VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ - "v1": batchv1Resources, - }, - OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, - Scheme: api.Scheme, - ParameterCodec: api.ParameterCodec, - NegotiatedSerializer: api.Codecs, + // stabilize order. + // TODO find a better way to configure priority of groups + for _, group := range sets.StringKeySet(c.RESTStorageProviders).List() { + if !c.APIResourceConfigSource.AnyResourcesForGroupEnabled(group) { + continue } - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv2alpha1.SchemeGroupVersion) { - batchv2alpha1Resources := m.getBatchResources(c, batchapiv2alpha1.SchemeGroupVersion) - apiGroupInfo.VersionedResourcesStorageMap["v2alpha1"] = batchv2alpha1Resources + restStorageBuilder := c.RESTStorageProviders[group] + apiGroupInfo, enabled := restStorageBuilder(c.APIResourceConfigSource, restOptionsGetter) + if !enabled { + continue } - apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) - } - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(policyapiv1alpha1.SchemeGroupVersion) { - policyResources := m.getPolicyResources(c) - policyGroupMeta := registered.GroupOrDie(policy.GroupName) - - // Hard code preferred group version to policy/v1alpha1 - policyGroupMeta.GroupVersion = policyapiv1alpha1.SchemeGroupVersion - - apiGroupInfo := genericapiserver.APIGroupInfo{ - GroupMeta: *policyGroupMeta, - VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ - "v1alpha1": policyResources, - }, - OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, - Scheme: api.Scheme, - ParameterCodec: api.ParameterCodec, - NegotiatedSerializer: api.Codecs, - } - apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) - } - - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(appsapi.SchemeGroupVersion) { - appsResources := m.getAppsResources(c) - appsGroupMeta := registered.GroupOrDie(apps.GroupName) - - // Hard code preferred group version to apps/v1alpha1 - appsGroupMeta.GroupVersion = appsapi.SchemeGroupVersion - - apiGroupInfo := genericapiserver.APIGroupInfo{ - GroupMeta: *appsGroupMeta, - VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ - "v1alpha1": appsResources, - }, - OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, - Scheme: api.Scheme, - ParameterCodec: api.ParameterCodec, - NegotiatedSerializer: api.Codecs, - } - apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) - } - - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(certificatesapiv1alpha1.SchemeGroupVersion) { - certificateResources := m.getCertificateResources(c) - certificatesGroupMeta := registered.GroupOrDie(certificates.GroupName) - - // Hard code preferred group version to certificates/v1alpha1 - certificatesGroupMeta.GroupVersion = certificatesapiv1alpha1.SchemeGroupVersion - - apiGroupInfo := genericapiserver.APIGroupInfo{ - GroupMeta: *certificatesGroupMeta, - VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ - "v1alpha1": certificateResources, - }, - OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, - Scheme: api.Scheme, - ParameterCodec: api.ParameterCodec, - NegotiatedSerializer: api.Codecs, - } - apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) - } - - if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(rbacapi.SchemeGroupVersion) { - rbacResources := m.getRBACResources(c) - rbacGroupMeta := registered.GroupOrDie(rbac.GroupName) - - // Hard code preferred group version to rbac/v1alpha1 - rbacGroupMeta.GroupVersion = rbacapi.SchemeGroupVersion - - apiGroupInfo := genericapiserver.APIGroupInfo{ - GroupMeta: *rbacGroupMeta, - VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ - "v1alpha1": rbacResources, - }, - OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, - Scheme: api.Scheme, - ParameterCodec: api.ParameterCodec, - NegotiatedSerializer: api.Codecs, - } apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) } @@ -901,116 +810,177 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage { return storage } -// getAutoscalingResources returns the resources for autoscaling api -func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage { - // TODO update when we support more than one version of this group - version := autoscalingapiv1.SchemeGroupVersion +// NewDefaultAPIGroupInfo returns a complete APIGroupInfo stubbed with "normal" values +// exposed for easier composition from other packages +func NewDefaultAPIGroupInfo(group string) genericapiserver.APIGroupInfo { + groupMeta := registered.GroupOrDie(group) - storage := map[string]rest.Storage{} - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) { - hpaStorage, hpaStatusStorage := horizontalpodautoscaleretcd.NewREST(m.GetRESTOptionsOrDie(c, autoscaling.Resource("horizontalpodautoscalers"))) - storage["horizontalpodautoscalers"] = hpaStorage - storage["horizontalpodautoscalers/status"] = hpaStatusStorage + return genericapiserver.APIGroupInfo{ + GroupMeta: *groupMeta, + VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, + OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, } - return storage } -// getCertificateResources returns the resources for certificates API -func (m *Master) getCertificateResources(c *Config) map[string]rest.Storage { - restOptions := func(resource string) generic.RESTOptions { - return m.GetRESTOptionsOrDie(c, certificates.Resource(resource)) +func buildCertificateResources(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := NewDefaultAPIGroupInfo(certificates.GroupName) + + storageForVersion := func(version unversioned.GroupVersion) map[string]rest.Storage { + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("certificatesigningrequests")) { + csrStorage, csrStatusStorage, csrApprovalStorage := certificateetcd.NewREST(restOptionsGetter(certificates.Resource("certificatesigningrequests"))) + storage["certificatesigningrequests"] = csrStorage + storage["certificatesigningrequests/status"] = csrStatusStorage + storage["certificatesigningrequests/approval"] = csrApprovalStorage + } + return storage } - // TODO update when we support more than one version of this group - version := certificatesapiv1alpha1.SchemeGroupVersion - - storage := map[string]rest.Storage{} - - csrStorage, csrStatusStorage, csrApprovalStorage := certificateetcd.NewREST(restOptions("certificatesigningrequests")) - - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("certificatesigningrequests")) { - storage["certificatesigningrequests"] = csrStorage - storage["certificatesigningrequests/status"] = csrStatusStorage - storage["certificatesigningrequests/approval"] = csrApprovalStorage + if apiResourceConfigSource.AnyResourcesForVersionEnabled(certificatesapiv1alpha1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1alpha1.SchemeGroupVersion.Version] = storageForVersion(certificatesapiv1alpha1.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = certificatesapiv1alpha1.SchemeGroupVersion } - return storage + return apiGroupInfo, true } -// getBatchResources returns the resources for batch api -func (m *Master) getBatchResources(c *Config, version unversioned.GroupVersion) map[string]rest.Storage { - storage := map[string]rest.Storage{} - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) { - jobsStorage, jobsStatusStorage := jobetcd.NewREST(m.GetRESTOptionsOrDie(c, batch.Resource("jobs"))) - storage["jobs"] = jobsStorage - storage["jobs/status"] = jobsStatusStorage +func buildAutoscalingResources(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := NewDefaultAPIGroupInfo(autoscaling.GroupName) + + storageForVersion := func(version unversioned.GroupVersion) map[string]rest.Storage { + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) { + hpaStorage, hpaStatusStorage := horizontalpodautoscaleretcd.NewREST(restOptionsGetter(autoscaling.Resource("horizontalpodautoscalers"))) + storage["horizontalpodautoscalers"] = hpaStorage + storage["horizontalpodautoscalers/status"] = hpaStatusStorage + } + return storage } - return storage + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[autoscalingapiv1.SchemeGroupVersion.Version] = storageForVersion(autoscalingapiv1.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = autoscalingapiv1.SchemeGroupVersion + } + + return apiGroupInfo, true } -// getPolicyResources returns the resources for policy api -func (m *Master) getPolicyResources(c *Config) map[string]rest.Storage { - // TODO update when we support more than one version of this group - version := policyapiv1alpha1.SchemeGroupVersion +func buildBatchResources(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := NewDefaultAPIGroupInfo(batch.GroupName) - storage := map[string]rest.Storage{} - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("poddisruptionbudgets")) { - poddisruptionbudgetStorage, poddisruptionbudgetStatusStorage := poddisruptionbudgetetcd.NewREST(m.GetRESTOptionsOrDie(c, policy.Resource("poddisruptionbudgets"))) - storage["poddisruptionbudgets"] = poddisruptionbudgetStorage - storage["poddisruptionbudgets/status"] = poddisruptionbudgetStatusStorage + storageForVersion := func(version unversioned.GroupVersion) map[string]rest.Storage { + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) { + jobsStorage, jobsStatusStorage := jobetcd.NewREST(restOptionsGetter(batch.Resource("jobs"))) + storage["jobs"] = jobsStorage + storage["jobs/status"] = jobsStatusStorage + } + return storage } - return storage + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv2alpha1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[batchapiv2alpha1.SchemeGroupVersion.Version] = storageForVersion(batchapiv2alpha1.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = batchapiv2alpha1.SchemeGroupVersion + } + if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[batchapiv1.SchemeGroupVersion.Version] = storageForVersion(batchapiv1.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion + } + + return apiGroupInfo, true } -// getAppsResources returns the resources for apps api -func (m *Master) getAppsResources(c *Config) map[string]rest.Storage { - // TODO update when we support more than one version of this group - version := appsapi.SchemeGroupVersion +func buildPolicyResources(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := NewDefaultAPIGroupInfo(policy.GroupName) - storage := map[string]rest.Storage{} - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("petsets")) { - petsetStorage, petsetStatusStorage := petsetetcd.NewREST(m.GetRESTOptionsOrDie(c, apps.Resource("petsets"))) - storage["petsets"] = petsetStorage - storage["petsets/status"] = petsetStatusStorage + storageForVersion := func(version unversioned.GroupVersion) map[string]rest.Storage { + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("poddisruptionbudgets")) { + poddisruptionbudgetStorage, poddisruptionbudgetStatusStorage := poddisruptionbudgetetcd.NewREST(restOptionsGetter(policy.Resource("poddisruptionbudgets"))) + storage["poddisruptionbudgets"] = poddisruptionbudgetStorage + storage["poddisruptionbudgets/status"] = poddisruptionbudgetStatusStorage + } + return storage } - return storage + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(policyapiv1alpha1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[policyapiv1alpha1.SchemeGroupVersion.Version] = storageForVersion(policyapiv1alpha1.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = policyapiv1alpha1.SchemeGroupVersion + } + + return apiGroupInfo, true } -func (m *Master) getRBACResources(c *Config) map[string]rest.Storage { - version := rbacapi.SchemeGroupVersion +func buildAppsResources(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := NewDefaultAPIGroupInfo(appsapi.GroupName) - once := new(sync.Once) - var authorizationRuleResolver rbacvalidation.AuthorizationRuleResolver - newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver { - once.Do(func() { - authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver( - role.NewRegistry(roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles")))), - rolebinding.NewRegistry(rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings")))), - clusterrole.NewRegistry(clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles")))), - clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings")))), - ) - }) - return authorizationRuleResolver + storageForVersion := func(version unversioned.GroupVersion) map[string]rest.Storage { + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("petsets")) { + petsetStorage, petsetStatusStorage := petsetetcd.NewREST(restOptionsGetter(apps.Resource("petsets"))) + storage["petsets"] = petsetStorage + storage["petsets/status"] = petsetStatusStorage + } + return storage } - storage := map[string]rest.Storage{} - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("roles")) { - rolesStorage := roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles"))) - storage["roles"] = rolepolicybased.NewStorage(rolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) + if apiResourceConfigSource.AnyResourcesForVersionEnabled(appsapi.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[appsapi.SchemeGroupVersion.Version] = storageForVersion(appsapi.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = appsapi.SchemeGroupVersion } - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("rolebindings")) { - roleBindingsStorage := rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings"))) - storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) + + return apiGroupInfo, true +} + +func buildRBACResources(authorizerRBACSuperUser string) RESTStorageProvider { + return func(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := NewDefaultAPIGroupInfo(rbac.GroupName) + + storageForVersion := func(version unversioned.GroupVersion) map[string]rest.Storage { + once := new(sync.Once) + var authorizationRuleResolver rbacvalidation.AuthorizationRuleResolver + newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver { + once.Do(func() { + authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver( + role.NewRegistry(roleetcd.NewREST(restOptionsGetter(rbac.Resource("roles")))), + rolebinding.NewRegistry(rolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("rolebindings")))), + clusterrole.NewRegistry(clusterroleetcd.NewREST(restOptionsGetter(rbac.Resource("clusterroles")))), + clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("clusterrolebindings")))), + ) + }) + return authorizationRuleResolver + } + + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("roles")) { + rolesStorage := roleetcd.NewREST(restOptionsGetter(rbac.Resource("roles"))) + storage["roles"] = rolepolicybased.NewStorage(rolesStorage, newRuleValidator(), authorizerRBACSuperUser) + } + if apiResourceConfigSource.ResourceEnabled(version.WithResource("rolebindings")) { + roleBindingsStorage := rolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("rolebindings"))) + storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, newRuleValidator(), authorizerRBACSuperUser) + } + if apiResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) { + clusterRolesStorage := clusterroleetcd.NewREST(restOptionsGetter(rbac.Resource("clusterroles"))) + storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, newRuleValidator(), authorizerRBACSuperUser) + } + if apiResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) { + clusterRoleBindingsStorage := clusterrolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("clusterrolebindings"))) + storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, newRuleValidator(), authorizerRBACSuperUser) + } + return storage + } + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapi.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[rbacapi.SchemeGroupVersion.Version] = storageForVersion(rbacapi.SchemeGroupVersion) + apiGroupInfo.GroupMeta.GroupVersion = rbacapi.SchemeGroupVersion + } + + return apiGroupInfo, true } - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) { - clusterRolesStorage := clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles"))) - storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) - } - if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) { - clusterRoleBindingsStorage := clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings"))) - storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) - } - return storage } // findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP. @@ -1065,7 +1035,16 @@ func (m *Master) IsTunnelSyncHealthy(req *http.Request) error { func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig { ret := genericapiserver.NewResourceConfig() - ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion, rbacapi.SchemeGroupVersion, certificatesapiv1alpha1.SchemeGroupVersion) + ret.EnableVersions( + apiv1.SchemeGroupVersion, + extensionsapiv1beta1.SchemeGroupVersion, + batchapiv1.SchemeGroupVersion, + autoscalingapiv1.SchemeGroupVersion, + appsapi.SchemeGroupVersion, + policyapiv1alpha1.SchemeGroupVersion, + rbacapi.SchemeGroupVersion, + certificatesapiv1alpha1.SchemeGroupVersion, + ) // all extensions resources except these are disabled by default ret.EnableResources( diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index a52e31ca83b..fd66d59b785 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -136,9 +136,14 @@ func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *ass // limitedAPIResourceConfigSource only enables the core group, the extensions group, the batch group, and the autoscaling group. func limitedAPIResourceConfigSource() *genericapiserver.ResourceConfig { ret := genericapiserver.NewResourceConfig() - ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, - batchapiv1.SchemeGroupVersion, batchapiv2alpha1.SchemeGroupVersion, - appsapi.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion) + ret.EnableVersions( + apiv1.SchemeGroupVersion, + extensionsapiv1beta1.SchemeGroupVersion, + batchapiv1.SchemeGroupVersion, + batchapiv2alpha1.SchemeGroupVersion, + appsapi.SchemeGroupVersion, + autoscalingapiv1.SchemeGroupVersion, + ) return ret } @@ -493,7 +498,7 @@ func TestDiscoveryAtAPIS(t *testing.T) { }, } - assert.Equal(3, len(groupList.Groups)) + assert.Equal(4, len(groupList.Groups)) for _, group := range groupList.Groups { if !expectGroupNames.Has(group.Name) { t.Errorf("got unexpected group %s", group.Name) @@ -520,7 +525,7 @@ func TestDiscoveryAtAPIS(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - assert.Equal(4, len(groupList.Groups)) + assert.Equal(5, len(groupList.Groups)) expectGroupNames.Insert("company.com") expectVersions["company.com"] = []unversioned.GroupVersionForDiscovery{thirdPartyGV}