diff --git a/pkg/master/master.go b/pkg/master/master.go index a5a736ec7a..fcffb2d944 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -474,8 +474,6 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { ret.EnableVersions( admissionregistrationv1beta1.SchemeGroupVersion, apiv1.SchemeGroupVersion, - appsv1beta1.SchemeGroupVersion, - appsv1beta2.SchemeGroupVersion, appsv1.SchemeGroupVersion, authenticationv1.SchemeGroupVersion, authenticationv1beta1.SchemeGroupVersion, @@ -499,6 +497,24 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { storageapiv1beta1.SchemeGroupVersion, schedulingapiv1beta1.SchemeGroupVersion, ) + // enable non-deprecated beta resources in extensions/v1beta1 explicitly so we have a full list of what's possible to serve + ret.EnableResources( + extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses"), + ) + // enable deprecated beta resources in extensions/v1beta1 explicitly so we have a full list of what's possible to serve + ret.EnableResources( + extensionsapiv1beta1.SchemeGroupVersion.WithResource("daemonsets"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("networkpolicies"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("podsecuritypolicies"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicationcontrollers"), + ) + // enable deprecated beta versions explicitly so we have a full list of what's possible to serve + ret.EnableVersions( + appsv1beta1.SchemeGroupVersion, + appsv1beta2.SchemeGroupVersion, + ) // disable alpha versions explicitly so we have a full list of what's possible to serve ret.DisableVersions( auditregistrationv1alpha1.SchemeGroupVersion, diff --git a/pkg/registry/extensions/rest/storage_extensions.go b/pkg/registry/extensions/rest/storage_extensions.go index 2b21e502f0..83bdce27c3 100644 --- a/pkg/registry/extensions/rest/storage_extensions.go +++ b/pkg/registry/extensions/rest/storage_extensions.go @@ -52,39 +52,53 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag // This is a dummy replication controller for scale subresource purposes. // TODO: figure out how to enable this only if needed as a part of scale subresource GA. - controllerStorage := expcontrollerstore.NewStorage(restOptionsGetter) - storage["replicationcontrollers"] = controllerStorage.ReplicationController - storage["replicationcontrollers/scale"] = controllerStorage.Scale + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicationcontrollers")) { + controllerStorage := expcontrollerstore.NewStorage(restOptionsGetter) + storage["replicationcontrollers"] = controllerStorage.ReplicationController + storage["replicationcontrollers/scale"] = controllerStorage.Scale + } // daemonsets - daemonSetStorage, daemonSetStatusStorage := daemonstore.NewREST(restOptionsGetter) - storage["daemonsets"] = daemonSetStorage.WithCategories(nil) - storage["daemonsets/status"] = daemonSetStatusStorage + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("daemonsets")) { + daemonSetStorage, daemonSetStatusStorage := daemonstore.NewREST(restOptionsGetter) + storage["daemonsets"] = daemonSetStorage.WithCategories(nil) + storage["daemonsets/status"] = daemonSetStatusStorage + } //deployments - deploymentStorage := deploymentstore.NewStorage(restOptionsGetter) - storage["deployments"] = deploymentStorage.Deployment.WithCategories(nil) - storage["deployments/status"] = deploymentStorage.Status - storage["deployments/rollback"] = deploymentStorage.Rollback - storage["deployments/scale"] = deploymentStorage.Scale + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments")) { + deploymentStorage := deploymentstore.NewStorage(restOptionsGetter) + storage["deployments"] = deploymentStorage.Deployment.WithCategories(nil) + storage["deployments/status"] = deploymentStorage.Status + storage["deployments/rollback"] = deploymentStorage.Rollback + storage["deployments/scale"] = deploymentStorage.Scale + } // ingresses - ingressStorage, ingressStatusStorage := ingressstore.NewREST(restOptionsGetter) - storage["ingresses"] = ingressStorage - storage["ingresses/status"] = ingressStatusStorage + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses")) { + ingressStorage, ingressStatusStorage := ingressstore.NewREST(restOptionsGetter) + storage["ingresses"] = ingressStorage + storage["ingresses/status"] = ingressStatusStorage + } // podsecuritypolicy - podSecurityPolicyStorage := pspstore.NewREST(restOptionsGetter) - storage["podSecurityPolicies"] = podSecurityPolicyStorage + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("podsecuritypolicies")) { + podSecurityPolicyStorage := pspstore.NewREST(restOptionsGetter) + storage["podSecurityPolicies"] = podSecurityPolicyStorage + } // replicasets - replicaSetStorage := replicasetstore.NewStorage(restOptionsGetter) - storage["replicasets"] = replicaSetStorage.ReplicaSet.WithCategories(nil) - storage["replicasets/status"] = replicaSetStorage.Status - storage["replicasets/scale"] = replicaSetStorage.Scale + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets")) { + replicaSetStorage := replicasetstore.NewStorage(restOptionsGetter) + storage["replicasets"] = replicaSetStorage.ReplicaSet.WithCategories(nil) + storage["replicasets/status"] = replicaSetStorage.Status + storage["replicasets/scale"] = replicaSetStorage.Scale + } // networkpolicies - networkExtensionsStorage := networkpolicystore.NewREST(restOptionsGetter) - storage["networkpolicies"] = networkExtensionsStorage + if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("networkpolicies")) { + networkExtensionsStorage := networkpolicystore.NewREST(restOptionsGetter) + storage["networkpolicies"] = networkExtensionsStorage + } return storage } diff --git a/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers.go b/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers.go index cb1c54e3f0..1c3c6eee20 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers.go +++ b/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers.go @@ -94,7 +94,7 @@ func MergeAPIResourceConfigs( } tokens := strings.Split(key, "/") - if len(tokens) != 2 { + if len(tokens) < 2 { continue } groupVersionString := tokens[0] + "/" + tokens[1] @@ -103,6 +103,12 @@ func MergeAPIResourceConfigs( return nil, fmt.Errorf("invalid key %s", key) } + // individual resource enablement/disablement is only supported in the extensions/v1beta1 API group for legacy reasons. + // all other API groups are expected to contain coherent sets of resources that are enabled/disabled together. + if len(tokens) > 2 && (groupVersion != schema.GroupVersion{Group: "extensions", Version: "v1beta1"}) { + return nil, fmt.Errorf("invalid key %s, individual resource enablement/disablement is not supported in %s", key, groupVersion.String()) + } + // Exclude group not registered into the registry. if !registry.IsGroupRegistered(groupVersion.Group) { continue @@ -117,10 +123,22 @@ func MergeAPIResourceConfigs( return nil, err } if enabled { + // enable the groupVersion for "group/version=true" and "group/version/resource=true" resourceConfig.EnableVersions(groupVersion) - } else { + } else if len(tokens) == 2 { + // disable the groupVersion only for "group/version=false", not "group/version/resource=false" resourceConfig.DisableVersions(groupVersion) } + + if len(tokens) < 3 { + continue + } + groupVersionResource := groupVersion.WithResource(tokens[2]) + if enabled { + resourceConfig.EnableResources(groupVersionResource) + } else { + resourceConfig.DisableResources(groupVersionResource) + } } return resourceConfig, nil diff --git a/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers_test.go b/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers_test.go index 13bf72b25f..5caa706525 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers_test.go +++ b/staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers_test.go @@ -117,6 +117,11 @@ func TestParseRuntimeConfig(t *testing.T) { expectedAPIConfig: func() *serverstore.ResourceConfig { config := newFakeAPIResourceConfigSource() config.EnableVersions(scheme.PrioritizedVersionsAllGroups()...) + config.EnableResources( + extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("daemonsets"), + ) return config }, err: false, @@ -133,10 +138,71 @@ func TestParseRuntimeConfig(t *testing.T) { expectedAPIConfig: func() *serverstore.ResourceConfig { config := newFakeAPIResourceConfigSource() config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion) + config.DisableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses")) return config }, err: false, }, + { + // enable specific extensions resources + runtimeConfig: map[string]string{ + "extensions/v1beta1/deployments": "true", + }, + defaultResourceConfig: func() *serverstore.ResourceConfig { + return newFakeAPIResourceConfigSource() + }, + expectedAPIConfig: func() *serverstore.ResourceConfig { + config := newFakeAPIResourceConfigSource() + config.EnableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments")) + return config + }, + err: false, + }, + { + // disable specific extensions resources + runtimeConfig: map[string]string{ + "extensions/v1beta1/ingresses": "false", + }, + defaultResourceConfig: func() *serverstore.ResourceConfig { + return newFakeAPIResourceConfigSource() + }, + expectedAPIConfig: func() *serverstore.ResourceConfig { + config := newFakeAPIResourceConfigSource() + config.DisableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses")) + return config + }, + err: false, + }, + { + // disable all extensions resources + runtimeConfig: map[string]string{ + "extensions/v1beta1": "false", + }, + defaultResourceConfig: func() *serverstore.ResourceConfig { + return newFakeAPIResourceConfigSource() + }, + expectedAPIConfig: func() *serverstore.ResourceConfig { + config := newFakeAPIResourceConfigSource() + config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion) + return config + }, + err: false, + }, + { + // disable a non-extensions resource + runtimeConfig: map[string]string{ + "apps/v1/deployments": "false", + }, + defaultResourceConfig: func() *serverstore.ResourceConfig { + return newFakeAPIResourceConfigSource() + }, + expectedAPIConfig: func() *serverstore.ResourceConfig { + config := newFakeAPIResourceConfigSource() + config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion) + return config + }, + err: true, + }, } for index, test := range testCases { t.Log(scheme.PrioritizedVersionsAllGroups()) @@ -161,6 +227,14 @@ func newFakeAPIResourceConfigSource() *serverstore.ResourceConfig { apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, ) + ret.EnableResources( + extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses"), + ) + ret.DisableResources( + extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets"), + extensionsapiv1beta1.SchemeGroupVersion.WithResource("daemonsets"), + ) return ret } diff --git a/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go b/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go index d16be4279a..d613403fba 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go +++ b/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go @@ -23,6 +23,7 @@ import ( // APIResourceConfigSource is the interface to determine which groups and versions are enabled type APIResourceConfigSource interface { VersionEnabled(version schema.GroupVersion) bool + ResourceEnabled(resource schema.GroupVersionResource) bool AnyVersionForGroupEnabled(group string) bool } @@ -30,22 +31,29 @@ var _ APIResourceConfigSource = &ResourceConfig{} type ResourceConfig struct { GroupVersionConfigs map[schema.GroupVersion]bool + ResourceConfigs map[schema.GroupVersionResource]bool } func NewResourceConfig() *ResourceConfig { - return &ResourceConfig{GroupVersionConfigs: map[schema.GroupVersion]bool{}} + return &ResourceConfig{GroupVersionConfigs: map[schema.GroupVersion]bool{}, ResourceConfigs: map[schema.GroupVersionResource]bool{}} } func (o *ResourceConfig) DisableAll() { for k := range o.GroupVersionConfigs { o.GroupVersionConfigs[k] = false } + for k := range o.ResourceConfigs { + o.ResourceConfigs[k] = false + } } func (o *ResourceConfig) EnableAll() { for k := range o.GroupVersionConfigs { o.GroupVersionConfigs[k] = true } + for k := range o.ResourceConfigs { + o.ResourceConfigs[k] = true + } } // DisableVersions disables the versions entirely. @@ -70,6 +78,29 @@ func (o *ResourceConfig) VersionEnabled(version schema.GroupVersion) bool { return false } +func (o *ResourceConfig) DisableResources(resources ...schema.GroupVersionResource) { + for _, resource := range resources { + o.ResourceConfigs[resource] = false + } +} + +func (o *ResourceConfig) EnableResources(resources ...schema.GroupVersionResource) { + for _, resource := range resources { + o.ResourceConfigs[resource] = true + } +} + +func (o *ResourceConfig) ResourceEnabled(resource schema.GroupVersionResource) bool { + if !o.VersionEnabled(resource.GroupVersion()) { + return false + } + resourceEnabled, explicitlySet := o.ResourceConfigs[resource] + if !explicitlySet { + return true + } + return resourceEnabled +} + func (o *ResourceConfig) AnyVersionForGroupEnabled(group string) bool { for version := range o.GroupVersionConfigs { if version.Group == group { diff --git a/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config_test.go b/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config_test.go index a22d6227df..71983f6c51 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config_test.go +++ b/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config_test.go @@ -43,6 +43,84 @@ func TestDisabledVersion(t *testing.T) { } } +func TestDisabledResource(t *testing.T) { + g1v1 := schema.GroupVersion{Group: "group1", Version: "version1"} + g1v1rUnspecified := g1v1.WithResource("unspecified") + g1v1rEnabled := g1v1.WithResource("enabled") + g1v1rDisabled := g1v1.WithResource("disabled") + g1v2 := schema.GroupVersion{Group: "group1", Version: "version2"} + g1v2rUnspecified := g1v2.WithResource("unspecified") + g1v2rEnabled := g1v2.WithResource("enabled") + g1v2rDisabled := g1v2.WithResource("disabled") + g2v1 := schema.GroupVersion{Group: "group2", Version: "version1"} + g2v1rUnspecified := g2v1.WithResource("unspecified") + g2v1rEnabled := g2v1.WithResource("enabled") + g2v1rDisabled := g2v1.WithResource("disabled") + + config := NewResourceConfig() + + config.DisableVersions(g1v1) + config.EnableVersions(g1v2, g2v1) + + config.EnableResources(g1v1rEnabled, g1v2rEnabled, g2v1rEnabled) + config.DisableResources(g1v1rDisabled, g1v2rDisabled, g2v1rDisabled) + + // all resources under g1v1 are disabled because the group-version is disabled + if config.ResourceEnabled(g1v1rUnspecified) { + t.Errorf("expected disabled for %v, from %v", g1v1rUnspecified, config) + } + if config.ResourceEnabled(g1v1rEnabled) { + t.Errorf("expected disabled for %v, from %v", g1v1rEnabled, config) + } + if config.ResourceEnabled(g1v1rDisabled) { + t.Errorf("expected disabled for %v, from %v", g1v1rDisabled, config) + } + + // explicitly disabled resources in enabled group-versions are disabled + if config.ResourceEnabled(g1v2rDisabled) { + t.Errorf("expected disabled for %v, from %v", g1v2rDisabled, config) + } + if config.ResourceEnabled(g2v1rDisabled) { + t.Errorf("expected disabled for %v, from %v", g2v1rDisabled, config) + } + + // unspecified and explicitly enabled resources in enabled group-versions are enabled + if !config.ResourceEnabled(g1v2rUnspecified) { + t.Errorf("expected enabled for %v, from %v", g1v2rUnspecified, config) + } + if !config.ResourceEnabled(g1v2rEnabled) { + t.Errorf("expected enabled for %v, from %v", g1v2rEnabled, config) + } + if !config.ResourceEnabled(g2v1rUnspecified) { + t.Errorf("expected enabled for %v, from %v", g2v1rUnspecified, config) + } + if !config.ResourceEnabled(g2v1rEnabled) { + t.Errorf("expected enabled for %v, from %v", g2v1rEnabled, config) + } + + // Enable all enables specific resources + config.EnableAll() + + // all resources under g1v1 are now enabled + if !config.ResourceEnabled(g1v1rUnspecified) { + t.Errorf("expected enabled for %v, from %v", g1v1rUnspecified, config) + } + if !config.ResourceEnabled(g1v1rEnabled) { + t.Errorf("expected enabled for %v, from %v", g1v1rEnabled, config) + } + if !config.ResourceEnabled(g1v1rDisabled) { + t.Errorf("expected enabled for %v, from %v", g1v1rDisabled, config) + } + + // previously disabled resources are now enabled + if !config.ResourceEnabled(g1v2rDisabled) { + t.Errorf("expected enabled for %v, from %v", g1v2rDisabled, config) + } + if !config.ResourceEnabled(g2v1rDisabled) { + t.Errorf("expected enabled for %v, from %v", g2v1rDisabled, config) + } +} + func TestAnyVersionForGroupEnabled(t *testing.T) { tests := []struct { name string