refactor resource overrides as positive logic interface

This commit is contained in:
deads2k 2016-03-22 12:45:23 -04:00
parent 3b469ff2eb
commit e8fb35d4d8
9 changed files with 453 additions and 194 deletions

View File

@ -36,10 +36,14 @@ import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/apis/batch"
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/apiserver/authenticator"
"k8s.io/kubernetes/pkg/capabilities"
@ -249,7 +253,7 @@ func Run(s *options.APIServer) error {
glog.Fatalf("Failure to start kubelet client: %v", err)
}
apiGroupVersionOverrides, err := parseRuntimeConfig(s)
apiResourceConfigSource, err := parseRuntimeConfig(s)
if err != nil {
glog.Fatalf("error in parsing runtime-config: %s", err)
}
@ -292,7 +296,7 @@ func Run(s *options.APIServer) error {
}
storageDestinations.AddAPIGroup("", etcdStorage)
if !apiGroupVersionOverrides["extensions/v1beta1"].Disable {
if apiResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
glog.Infof("Configuring extensions/v1beta1 storage destination")
expGroup, err := registered.Group(extensions.GroupName)
if err != nil {
@ -321,7 +325,7 @@ func Run(s *options.APIServer) error {
// autoscaling/v1/horizontalpodautoscalers is a move from extensions/v1beta1/horizontalpodautoscalers.
// The storage version needs to be either extensions/v1beta1 or autoscaling/v1.
// Users must roll forward while using 1.2, because we will require the latter for 1.3.
if !apiGroupVersionOverrides["autoscaling/v1"].Disable {
if apiResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv1.SchemeGroupVersion) {
glog.Infof("Configuring autoscaling/v1 storage destination")
autoscalingGroup, err := registered.Group(autoscaling.GroupName)
if err != nil {
@ -348,7 +352,7 @@ func Run(s *options.APIServer) error {
// version needs to be either extensions/v1beta1 or batch/v1. Users
// must roll forward while using 1.2, because we will require the
// latter for 1.3.
if !apiGroupVersionOverrides["batch/v1"].Disable {
if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
glog.Infof("Configuring batch/v1 storage destination")
batchGroup, err := registered.Group(batch.GroupName)
if err != nil {
@ -464,7 +468,7 @@ func Run(s *options.APIServer) error {
SupportsBasicAuth: len(s.BasicAuthFile) > 0,
Authorizer: authorizer,
AdmissionControl: admissionController,
APIGroupVersionOverrides: apiGroupVersionOverrides,
APIResourceConfigSource: apiResourceConfigSource,
MasterServiceNamespace: s.MasterServiceNamespace,
MasterCount: s.MasterCount,
ExternalHost: s.ExternalHost,
@ -511,13 +515,24 @@ func getRuntimeConfigValue(s *options.APIServer, apiKey string, defaultValue boo
return defaultValue
}
// Parses the given runtime-config and formats it into map[string]ApiGroupVersionOverride
func parseRuntimeConfig(s *options.APIServer) (map[string]genericapiserver.APIGroupVersionOverride, error) {
// Parses the given runtime-config and formats it into genericapiserver.APIResourceConfigSource
func parseRuntimeConfig(s *options.APIServer) (genericapiserver.APIResourceConfigSource, error) {
v1GroupVersionString := "api/v1"
extensionsGroupVersionString := extensionsapiv1beta1.SchemeGroupVersion.String()
versionToResourceSpecifier := map[unversioned.GroupVersion]string{
apiv1.SchemeGroupVersion: v1GroupVersionString,
extensionsapiv1beta1.SchemeGroupVersion: extensionsGroupVersionString,
batchapiv1.SchemeGroupVersion: batchapiv1.SchemeGroupVersion.String(),
autoscalingapiv1.SchemeGroupVersion: autoscalingapiv1.SchemeGroupVersion.String(),
}
resourceConfig := master.DefaultAPIResourceConfigSource()
// "api/all=false" allows users to selectively enable specific api versions.
disableAllAPIs := false
enableAPIByDefault := true
allAPIFlagValue, ok := s.RuntimeConfig["api/all"]
if ok && allAPIFlagValue == "false" {
disableAllAPIs = true
enableAPIByDefault = false
}
// "api/legacy=false" allows users to disable legacy api versions.
@ -528,60 +543,40 @@ func parseRuntimeConfig(s *options.APIServer) (map[string]genericapiserver.APIGr
}
_ = disableLegacyAPIs // hush the compiler while we don't have legacy APIs to disable.
// "api/v1={true|false} allows users to enable/disable v1 API.
// "<resourceSpecifier>={true|false} allows users to enable/disable API.
// This takes preference over api/all and api/legacy, if specified.
disableV1 := disableAllAPIs
v1GroupVersion := "api/v1"
disableV1 = !getRuntimeConfigValue(s, v1GroupVersion, !disableV1)
apiGroupVersionOverrides := map[string]genericapiserver.APIGroupVersionOverride{}
if disableV1 {
apiGroupVersionOverrides[v1GroupVersion] = genericapiserver.APIGroupVersionOverride{
Disable: true,
}
}
// "extensions/v1beta1={true|false} allows users to enable/disable the extensions API.
// This takes preference over api/all, if specified.
disableExtensions := disableAllAPIs
extensionsGroupVersion := "extensions/v1beta1"
// TODO: Make this a loop over all group/versions when there are more of them.
disableExtensions = !getRuntimeConfigValue(s, extensionsGroupVersion, !disableExtensions)
if disableExtensions {
apiGroupVersionOverrides[extensionsGroupVersion] = genericapiserver.APIGroupVersionOverride{
Disable: true,
}
}
disableAutoscaling := disableAllAPIs
autoscalingGroupVersion := "autoscaling/v1"
disableAutoscaling = !getRuntimeConfigValue(s, autoscalingGroupVersion, !disableAutoscaling)
if disableAutoscaling {
apiGroupVersionOverrides[autoscalingGroupVersion] = genericapiserver.APIGroupVersionOverride{
Disable: true,
}
}
disableBatch := disableAllAPIs
batchGroupVersion := "batch/v1"
disableBatch = !getRuntimeConfigValue(s, batchGroupVersion, !disableBatch)
if disableBatch {
apiGroupVersionOverrides[batchGroupVersion] = genericapiserver.APIGroupVersionOverride{
Disable: true,
for version, resourceSpecifier := range versionToResourceSpecifier {
enableVersion := getRuntimeConfigValue(s, resourceSpecifier, enableAPIByDefault)
if enableVersion {
resourceConfig.EnableVersions(version)
} else {
resourceConfig.DisableVersions(version)
}
}
for key := range s.RuntimeConfig {
if strings.HasPrefix(key, v1GroupVersion+"/") {
return nil, fmt.Errorf("api/v1 resources cannot be enabled/disabled individually")
} else if strings.HasPrefix(key, extensionsGroupVersion+"/") {
resource := strings.TrimPrefix(key, extensionsGroupVersion+"/")
tokens := strings.Split(key, "/")
if len(tokens) != 3 {
continue
}
apiGroupVersionOverride := apiGroupVersionOverrides[extensionsGroupVersion]
if apiGroupVersionOverride.ResourceOverrides == nil {
apiGroupVersionOverride.ResourceOverrides = map[string]bool{}
switch {
case strings.HasPrefix(key, extensionsGroupVersionString+"/"):
if !resourceConfig.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
return nil, fmt.Errorf("%v is disabled, you cannot configure its resources individually", extensionsapiv1beta1.SchemeGroupVersion)
}
apiGroupVersionOverride.ResourceOverrides[resource] = getRuntimeConfigValue(s, key, false)
apiGroupVersionOverrides[extensionsGroupVersion] = apiGroupVersionOverride
resource := strings.TrimPrefix(key, extensionsGroupVersionString+"/")
if getRuntimeConfigValue(s, key, false) {
resourceConfig.EnableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource(resource))
} else {
resourceConfig.DisableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource(resource))
}
default:
// TODO enable individual resource capability for all GroupVersionResources
return nil, fmt.Errorf("%v resources cannot be enabled/disabled individually", key)
}
}
return apiGroupVersionOverrides, nil
return resourceConfig, nil
}

View File

@ -24,8 +24,10 @@ import (
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
@ -132,13 +134,15 @@ func TestUpdateEtcdOverrides(t *testing.T) {
func TestParseRuntimeConfig(t *testing.T) {
testCases := []struct {
runtimeConfig map[string]string
apiGroupVersionOverrides map[string]genericapiserver.APIGroupVersionOverride
err bool
runtimeConfig map[string]string
expectedAPIConfig func() *genericapiserver.ResourceConfig
err bool
}{
{
runtimeConfig: map[string]string{},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{},
runtimeConfig: map[string]string{},
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
return master.DefaultAPIResourceConfigSource()
},
err: false,
},
{
@ -146,7 +150,9 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{
"api/v1/pods": "false",
},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{},
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
return master.DefaultAPIResourceConfigSource()
},
err: true,
},
{
@ -154,10 +160,10 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{
"api/v1": "false",
},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
"api/v1": {
Disable: true,
},
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
config := master.DefaultAPIResourceConfigSource()
config.DisableVersions(unversioned.GroupVersion{Group: "", Version: "v1"})
return config
},
err: false,
},
@ -166,10 +172,10 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{
"extensions/v1beta1": "false",
},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
"extensions/v1beta1": {
Disable: true,
},
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
config := master.DefaultAPIResourceConfigSource()
config.DisableVersions(unversioned.GroupVersion{Group: "extensions", Version: "v1beta1"})
return config
},
err: false,
},
@ -178,28 +184,24 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{
"extensions/v1beta1/deployments": "false",
},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
"extensions/v1beta1": {
ResourceOverrides: map[string]bool{
"deployments": false,
},
},
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
config := master.DefaultAPIResourceConfigSource()
config.DisableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"})
return config
},
err: false,
},
{
// Enable deployments and disable jobs.
runtimeConfig: map[string]string{
"extensions/v1beta1/deployments": "true",
"extensions/v1beta1/jobs": "false",
"extensions/v1beta1/anything": "true",
"extensions/v1beta1/jobs": "false",
},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{
"extensions/v1beta1": {
ResourceOverrides: map[string]bool{
"deployments": true,
"jobs": false,
},
},
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
config := master.DefaultAPIResourceConfigSource()
config.DisableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "jobs"})
config.EnableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "anything"})
return config
},
err: false,
},
@ -208,7 +210,7 @@ func TestParseRuntimeConfig(t *testing.T) {
s := &options.APIServer{
RuntimeConfig: test.runtimeConfig,
}
apiGroupVersionOverrides, err := parseRuntimeConfig(s)
actualDisablers, err := parseRuntimeConfig(s)
if err == nil && test.err {
t.Fatalf("expected error for test: %v", test)
@ -216,8 +218,9 @@ func TestParseRuntimeConfig(t *testing.T) {
t.Fatalf("unexpected error: %s, for test: %v", err, test)
}
if err == nil && !reflect.DeepEqual(apiGroupVersionOverrides, test.apiGroupVersionOverrides) {
t.Fatalf("unexpected apiGroupVersionOverrides. Actual: %v, expected: %v", apiGroupVersionOverrides, test.apiGroupVersionOverrides)
expectedConfig := test.expectedAPIConfig()
if err == nil && !reflect.DeepEqual(actualDisablers, expectedConfig) {
t.Fatalf("%v: unexpected apiResourceDisablers. Actual: %q\n expected: %q", test.runtimeConfig, actualDisablers, expectedConfig)
}
}

View File

@ -164,15 +164,6 @@ func (s *StorageDestinations) Backends() []string {
return backends.List()
}
// Specifies the overrides for various API group versions.
// This can be used to enable/disable entire group versions or specific resources.
type APIGroupVersionOverride struct {
// Whether to enable or disable this group version.
Disable bool
// List of overrides for individual resources in this group version.
ResourceOverrides map[string]bool
}
// Info about an API group.
type APIGroupInfo struct {
GroupMeta apimachinery.GroupMeta
@ -218,7 +209,7 @@ type Config struct {
// Note that this is ignored if either EnableSwaggerSupport or EnableUISupport is false.
EnableSwaggerUI bool
// Allows api group versions or specific resources to be conditionally enabled/disabled.
APIGroupVersionOverrides map[string]APIGroupVersionOverride
APIResourceConfigSource APIResourceConfigSource
// allow downstream consumers to disable the index route
EnableIndex bool
EnableProfiling bool
@ -305,25 +296,24 @@ type GenericAPIServer struct {
cacheTimeout time.Duration
MinRequestTimeout time.Duration
mux apiserver.Mux
MuxHelper *apiserver.MuxHelper
HandlerContainer *restful.Container
RootWebService *restful.WebService
enableLogsSupport bool
enableUISupport bool
enableSwaggerSupport bool
enableSwaggerUI bool
enableProfiling bool
enableWatchCache bool
APIPrefix string
APIGroupPrefix string
corsAllowedOriginList []string
authenticator authenticator.Request
authorizer authorizer.Authorizer
AdmissionControl admission.Interface
MasterCount int
ApiGroupVersionOverrides map[string]APIGroupVersionOverride
RequestContextMapper api.RequestContextMapper
mux apiserver.Mux
MuxHelper *apiserver.MuxHelper
HandlerContainer *restful.Container
RootWebService *restful.WebService
enableLogsSupport bool
enableUISupport bool
enableSwaggerSupport bool
enableSwaggerUI bool
enableProfiling bool
enableWatchCache bool
APIPrefix string
APIGroupPrefix string
corsAllowedOriginList []string
authenticator authenticator.Request
authorizer authorizer.Authorizer
AdmissionControl admission.Interface
MasterCount int
RequestContextMapper api.RequestContextMapper
// ExternalAddress is the address (hostname or IP and port) that should be used in
// external (public internet) URLs for this GenericAPIServer.
@ -451,24 +441,23 @@ func New(c *Config) (*GenericAPIServer, error) {
setDefaults(c)
s := &GenericAPIServer{
ServiceClusterIPRange: c.ServiceClusterIPRange,
ServiceNodePortRange: c.ServiceNodePortRange,
RootWebService: new(restful.WebService),
enableLogsSupport: c.EnableLogsSupport,
enableUISupport: c.EnableUISupport,
enableSwaggerSupport: c.EnableSwaggerSupport,
enableSwaggerUI: c.EnableSwaggerUI,
enableProfiling: c.EnableProfiling,
enableWatchCache: c.EnableWatchCache,
APIPrefix: c.APIPrefix,
APIGroupPrefix: c.APIGroupPrefix,
corsAllowedOriginList: c.CorsAllowedOriginList,
authenticator: c.Authenticator,
authorizer: c.Authorizer,
AdmissionControl: c.AdmissionControl,
ApiGroupVersionOverrides: c.APIGroupVersionOverrides,
RequestContextMapper: c.RequestContextMapper,
Serializer: c.Serializer,
ServiceClusterIPRange: c.ServiceClusterIPRange,
ServiceNodePortRange: c.ServiceNodePortRange,
RootWebService: new(restful.WebService),
enableLogsSupport: c.EnableLogsSupport,
enableUISupport: c.EnableUISupport,
enableSwaggerSupport: c.EnableSwaggerSupport,
enableSwaggerUI: c.EnableSwaggerUI,
enableProfiling: c.EnableProfiling,
enableWatchCache: c.EnableWatchCache,
APIPrefix: c.APIPrefix,
APIGroupPrefix: c.APIGroupPrefix,
corsAllowedOriginList: c.CorsAllowedOriginList,
authenticator: c.Authenticator,
authorizer: c.Authorizer,
AdmissionControl: c.AdmissionControl,
RequestContextMapper: c.RequestContextMapper,
Serializer: c.Serializer,
cacheTimeout: c.CacheTimeout,
MinRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,

View File

@ -88,7 +88,6 @@ func TestNew(t *testing.T) {
assert.Equal(s.authenticator, config.Authenticator)
assert.Equal(s.authorizer, config.Authorizer)
assert.Equal(s.AdmissionControl, config.AdmissionControl)
assert.Equal(s.ApiGroupVersionOverrides, config.APIGroupVersionOverrides)
assert.Equal(s.RequestContextMapper, config.RequestContextMapper)
assert.Equal(s.cacheTimeout, config.CacheTimeout)
assert.Equal(s.ExternalAddress, config.ExternalHost)

View File

@ -0,0 +1,172 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 genericapiserver
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util/sets"
)
// APIResourceConfigSource is the interface to determine which versions and resources are enabled
type APIResourceConfigSource interface {
AnyVersionOfResourceEnabled(resource unversioned.GroupResource) bool
ResourceEnabled(resource unversioned.GroupVersionResource) bool
AllResourcesForVersionEnabled(version unversioned.GroupVersion) bool
AnyResourcesForVersionEnabled(version unversioned.GroupVersion) bool
}
// Specifies the overrides for various API group versions.
// This can be used to enable/disable entire group versions or specific resources.
type GroupVersionResourceConfig struct {
// Whether to enable or disable this entire group version. This dominates any enablement check.
// Enable=true means the group version is enabled, and EnabledResources/DisabledResources are considered.
// Enable=false means the group version is disabled, and EnabledResources/DisabledResources are not considered.
Enable bool
// DisabledResources lists the resources that are specifically disabled for a group/version
// DisabledResources trumps EnabledResources
DisabledResources sets.String
// EnabledResources lists the resources that should be enabled by default. This is a little
// unusual, but we need it for compatibility with old code for now. An empty set means
// enable all, a non-empty set means that all other resources are disabled.
EnabledResources sets.String
}
var _ APIResourceConfigSource = &ResourceConfig{}
type ResourceConfig struct {
GroupVersionResourceConfigs map[unversioned.GroupVersion]*GroupVersionResourceConfig
}
func NewResourceConfig() *ResourceConfig {
return &ResourceConfig{GroupVersionResourceConfigs: map[unversioned.GroupVersion]*GroupVersionResourceConfig{}}
}
func NewGroupVersionResourceConfig() *GroupVersionResourceConfig {
return &GroupVersionResourceConfig{Enable: true, DisabledResources: sets.String{}, EnabledResources: sets.String{}}
}
// DisableVersions disables the versions entirely. No resources (even those whitelisted in EnabledResources) will be enabled
func (o *ResourceConfig) DisableVersions(versions ...unversioned.GroupVersion) {
for _, version := range versions {
_, versionExists := o.GroupVersionResourceConfigs[version]
if !versionExists {
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
}
o.GroupVersionResourceConfigs[version].Enable = false
}
}
func (o *ResourceConfig) EnableVersions(versions ...unversioned.GroupVersion) {
for _, version := range versions {
_, versionExists := o.GroupVersionResourceConfigs[version]
if !versionExists {
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
}
o.GroupVersionResourceConfigs[version].Enable = true
}
}
func (o *ResourceConfig) DisableResources(resources ...unversioned.GroupVersionResource) {
for _, resource := range resources {
version := resource.GroupVersion()
_, versionExists := o.GroupVersionResourceConfigs[version]
if !versionExists {
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
}
o.GroupVersionResourceConfigs[version].DisabledResources.Insert(resource.Resource)
}
}
func (o *ResourceConfig) EnableResources(resources ...unversioned.GroupVersionResource) {
for _, resource := range resources {
version := resource.GroupVersion()
_, versionExists := o.GroupVersionResourceConfigs[version]
if !versionExists {
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
}
o.GroupVersionResourceConfigs[version].EnabledResources.Insert(resource.Resource)
o.GroupVersionResourceConfigs[version].DisabledResources.Delete(resource.Resource)
}
}
// AnyResourcesForVersionEnabled only considers matches based on exactly group/resource lexical matching. This means that
// resource renames across versions are NOT considered to be the same resource by this method. You'll need to manually check
// using the ResourceEnabled function.
func (o *ResourceConfig) AnyVersionOfResourceEnabled(resource unversioned.GroupResource) bool {
for version := range o.GroupVersionResourceConfigs {
if version.Group != resource.Group {
continue
}
if o.ResourceEnabled(version.WithResource(resource.Resource)) {
return true
}
}
return false
}
func (o *ResourceConfig) ResourceEnabled(resource unversioned.GroupVersionResource) bool {
versionOverride, versionExists := o.GroupVersionResourceConfigs[resource.GroupVersion()]
if !versionExists {
return false
}
if !versionOverride.Enable {
return false
}
if versionOverride.DisabledResources.Has(resource.Resource) {
return false
}
if len(versionOverride.EnabledResources) > 0 {
return versionOverride.EnabledResources.Has(resource.Resource)
}
return true
}
func (o *ResourceConfig) AllResourcesForVersionEnabled(version unversioned.GroupVersion) bool {
versionOverride, versionExists := o.GroupVersionResourceConfigs[version]
if !versionExists {
return false
}
if !versionOverride.Enable {
return false
}
if len(versionOverride.EnabledResources) == 0 && len(versionOverride.DisabledResources) == 0 {
return true
}
return false
}
func (o *ResourceConfig) AnyResourcesForVersionEnabled(version unversioned.GroupVersion) bool {
versionOverride, versionExists := o.GroupVersionResourceConfigs[version]
if !versionExists {
return false
}
return versionOverride.Enable
}

View File

@ -0,0 +1,99 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 genericapiserver
import (
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
)
func TestDisabledVersion(t *testing.T) {
g1v1 := unversioned.GroupVersion{Group: "group1", Version: "version1"}
g1v2 := unversioned.GroupVersion{Group: "group1", Version: "version2"}
g2v1 := unversioned.GroupVersion{Group: "group2", Version: "version1"}
g3v1 := unversioned.GroupVersion{Group: "group3", Version: "version1"}
resourceType := "the-resource"
disabledResourceType := "the-disabled-resource"
config := NewResourceConfig()
config.DisableVersions(g1v1)
config.EnableVersions(g1v2, g3v1)
config.EnableResources(g1v1.WithResource(resourceType), g2v1.WithResource(resourceType))
config.DisableResources(g1v2.WithResource(disabledResourceType))
expectedEnabledResources := []unversioned.GroupVersionResource{
g1v2.WithResource(resourceType),
g2v1.WithResource(resourceType),
}
expectedDisabledResources := []unversioned.GroupVersionResource{
g1v1.WithResource(resourceType), g1v1.WithResource(disabledResourceType),
g1v2.WithResource(disabledResourceType),
g2v1.WithResource(disabledResourceType),
}
for _, expectedResource := range expectedEnabledResources {
if !config.ResourceEnabled(expectedResource) {
t.Errorf("expected enabled for %v, from %v", expectedResource, config)
}
}
for _, expectedResource := range expectedDisabledResources {
if config.ResourceEnabled(expectedResource) {
t.Errorf("expected disabled for %v, from %v", expectedResource, config)
}
}
if e, a := false, config.AnyResourcesForVersionEnabled(g1v1); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := false, config.AllResourcesForVersionEnabled(g1v1); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := true, config.AnyResourcesForVersionEnabled(g1v2); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := false, config.AllResourcesForVersionEnabled(g1v2); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := true, config.AnyResourcesForVersionEnabled(g3v1); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := true, config.AllResourcesForVersionEnabled(g3v1); e != a {
t.Errorf("expected %v, got %v", e, a)
}
expectedEnabledAnyVersionResources := []unversioned.GroupResource{
{Group: "group1", Resource: resourceType},
}
expectedDisabledAnyResources := []unversioned.GroupResource{
{Group: "group1", Resource: disabledResourceType},
}
for _, expectedResource := range expectedEnabledAnyVersionResources {
if !config.AnyVersionOfResourceEnabled(expectedResource) {
t.Errorf("expected enabled for %v, from %v", expectedResource, config)
}
}
for _, expectedResource := range expectedDisabledAnyResources {
if config.AnyVersionOfResourceEnabled(expectedResource) {
t.Errorf("expected disabled for %v, from %v", expectedResource, config)
}
}
}

View File

@ -30,10 +30,14 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/unversioned"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/apis/batch"
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/apiserver"
apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics"
"k8s.io/kubernetes/pkg/genericapiserver"
@ -76,7 +80,6 @@ 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"
@ -182,7 +185,7 @@ func (m *Master) InstallAPIs(c *Config) {
apiGroupsInfo := []genericapiserver.APIGroupInfo{}
// Install v1 unless disabled.
if !m.ApiGroupVersionOverrides["api/v1"].Disable {
if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) {
// Install v1 API.
m.initV1ResourcesStorage(c)
apiGroupInfo := genericapiserver.APIGroupInfo{
@ -227,7 +230,7 @@ func (m *Master) InstallAPIs(c *Config) {
allGroups := []unversioned.APIGroup{}
// Install extensions unless disabled.
if !m.ApiGroupVersionOverrides["extensions/v1beta1"].Disable {
if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
m.thirdPartyStorage = c.StorageDestinations.APIGroups[extensions.GroupName].Default
m.thirdPartyResources = map[string]thirdPartyEntry{}
@ -269,12 +272,12 @@ func (m *Master) InstallAPIs(c *Config) {
}
// Install autoscaling unless disabled.
if !m.ApiGroupVersionOverrides["autoscaling/v1"].Disable {
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 = unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}
autoscalingGroupMeta.GroupVersion = autoscalingapiv1.SchemeGroupVersion
apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *autoscalingGroupMeta,
@ -301,12 +304,12 @@ func (m *Master) InstallAPIs(c *Config) {
}
// Install batch unless disabled.
if !m.ApiGroupVersionOverrides["batch/v1"].Disable {
if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
batchResources := m.getBatchResources(c)
batchGroupMeta := registered.GroupOrDie(batch.GroupName)
// Hard code preferred group version to batch/v1
batchGroupMeta.GroupVersion = unversioned.GroupVersion{Group: "batch", Version: "v1"}
batchGroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion
apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *batchGroupMeta,
@ -668,17 +671,6 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV
// getExperimentalResources returns the resources for extensions api
func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
// All resources except these are disabled by default.
enabledResources := sets.NewString("daemonsets", "deployments", "horizontalpodautoscalers", "ingresses", "jobs", "replicasets", "thirdpartyresources")
resourceOverrides := m.ApiGroupVersionOverrides["extensions/v1beta1"].ResourceOverrides
isEnabled := func(resource string) bool {
// Check if the resource has been overriden.
enabled, ok := resourceOverrides[resource]
if !ok {
return enabledResources.Has(resource)
}
return enabled
}
restOptions := func(resource string) generic.RESTOptions {
return generic.RESTOptions{
Storage: c.StorageDestinations.Get(extensions.GroupName, resource),
@ -686,18 +678,20 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
DeleteCollectionWorkers: m.deleteCollectionWorkers,
}
}
// TODO update when we support more than one version of this group
version := extensionsapiv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if isEnabled("horizontalpodautoscalers") {
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
m.constructHPAResources(c, storage)
controllerStorage := expcontrolleretcd.NewStorage(
generic.RESTOptions{Storage: c.StorageDestinations.Get("", "replicationControllers"), Decorator: m.StorageDecorator(), DeleteCollectionWorkers: m.deleteCollectionWorkers})
storage["replicationcontrollers"] = controllerStorage.ReplicationController
storage["replicationcontrollers/scale"] = controllerStorage.Scale
}
if isEnabled("thirdpartyresources") {
thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(restOptions("thirdpartyresources"))
thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(restOptions("thirdpartyresources"))
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("thirdpartyresources")) {
thirdPartyControl := ThirdPartyController{
master: m,
thirdPartyResourceRegistry: thirdPartyResourceStorage,
@ -712,32 +706,32 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
storage["thirdpartyresources"] = thirdPartyResourceStorage
}
if isEnabled("daemonsets") {
daemonSetStorage, daemonSetStatusStorage := daemonetcd.NewREST(restOptions("daemonsets"))
daemonSetStorage, daemonSetStatusStorage := daemonetcd.NewREST(restOptions("daemonsets"))
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) {
storage["daemonsets"] = daemonSetStorage
storage["daemonsets/status"] = daemonSetStatusStorage
}
if isEnabled("deployments") {
deploymentStorage := deploymentetcd.NewStorage(restOptions("deployments"))
deploymentStorage := deploymentetcd.NewStorage(restOptions("deployments"))
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) {
storage["deployments"] = deploymentStorage.Deployment
storage["deployments/status"] = deploymentStorage.Status
storage["deployments/rollback"] = deploymentStorage.Rollback
storage["deployments/scale"] = deploymentStorage.Scale
}
if isEnabled("jobs") {
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
m.constructJobResources(c, storage)
}
if isEnabled("ingresses") {
ingressStorage, ingressStatusStorage := ingressetcd.NewREST(restOptions("ingresses"))
ingressStorage, ingressStatusStorage := ingressetcd.NewREST(restOptions("ingresses"))
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("ingresses")) {
storage["ingresses"] = ingressStorage
storage["ingresses/status"] = ingressStatusStorage
}
if isEnabled("podsecuritypolicy") {
podSecurityPolicyStorage := pspetcd.NewREST(restOptions("podsecuritypolicy"))
podSecurityPolicyStorage := pspetcd.NewREST(restOptions("podsecuritypolicy"))
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("podsecuritypolicy")) {
storage["podSecurityPolicies"] = podSecurityPolicyStorage
}
if isEnabled("replicasets") {
replicaSetStorage := replicasetetcd.NewStorage(restOptions("replicasets"))
replicaSetStorage := replicasetetcd.NewStorage(restOptions("replicasets"))
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("replicasets")) {
storage["replicasets"] = replicaSetStorage.ReplicaSet
storage["replicasets/status"] = replicaSetStorage.Status
storage["replicasets/scale"] = replicaSetStorage.Scale
@ -767,17 +761,11 @@ func (m *Master) constructHPAResources(c *Config, restStorage map[string]rest.St
// getAutoscalingResources returns the resources for autoscaling api
func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage {
resourceOverrides := m.ApiGroupVersionOverrides["autoscaling/v1"].ResourceOverrides
isEnabled := func(resource string) bool {
// Check if the resource has been overriden.
if enabled, ok := resourceOverrides[resource]; ok {
return enabled
}
return !m.ApiGroupVersionOverrides["autoscaling/v1"].Disable
}
// TODO update when we support more than one version of this group
version := autoscalingapiv1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if isEnabled("horizontalpodautoscalers") {
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
m.constructHPAResources(c, storage)
}
return storage
@ -804,17 +792,11 @@ func (m *Master) constructJobResources(c *Config, restStorage map[string]rest.St
// getBatchResources returns the resources for batch api
func (m *Master) getBatchResources(c *Config) map[string]rest.Storage {
resourceOverrides := m.ApiGroupVersionOverrides["batch/v1"].ResourceOverrides
isEnabled := func(resource string) bool {
// Check if the resource has been overriden.
if enabled, ok := resourceOverrides[resource]; ok {
return enabled
}
return !m.ApiGroupVersionOverrides["batch/v1"].Disable
}
// TODO update when we support more than one version of this group
version := batchapiv1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if isEnabled("jobs") {
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
m.constructJobResources(c, storage)
}
return storage
@ -869,3 +851,21 @@ func (m *Master) IsTunnelSyncHealthy(req *http.Request) error {
}
return nil
}
func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig {
ret := genericapiserver.NewResourceConfig()
ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion)
// all extensions resources except these are disabled by default
ret.EnableResources(
extensionsapiv1beta1.SchemeGroupVersion.WithResource("daemonsets"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("deployments"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("horizontalpodautoscalers"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("jobs"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets"),
extensionsapiv1beta1.SchemeGroupVersion.WithResource("thirdpartyresources"),
)
return ret
}

View File

@ -95,6 +95,7 @@ func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *ass
config.KubeletClient = client.FakeKubeletClient{}
config.APIPrefix = "/api"
config.APIGroupPrefix = "/apis"
config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
config.ProxyTLSClientConfig = &tls.Config{}
@ -126,7 +127,6 @@ func TestNew(t *testing.T) {
assert.Equal(master.tunneler, config.Tunneler)
assert.Equal(master.APIPrefix, config.APIPrefix)
assert.Equal(master.APIGroupPrefix, config.APIGroupPrefix)
assert.Equal(master.ApiGroupVersionOverrides, config.APIGroupVersionOverrides)
assert.Equal(master.RequestContextMapper, config.RequestContextMapper)
assert.Equal(master.MasterCount, config.MasterCount)
assert.Equal(master.ClusterIP, config.PublicAddress)

View File

@ -168,13 +168,14 @@ func NewMasterConfig() *master.Config {
return &master.Config{
Config: &genericapiserver.Config{
StorageDestinations: storageDestinations,
StorageVersions: storageVersions,
APIPrefix: "/api",
APIGroupPrefix: "/apis",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
Serializer: api.Codecs,
StorageDestinations: storageDestinations,
StorageVersions: storageVersions,
APIResourceConfigSource: master.DefaultAPIResourceConfigSource(),
APIPrefix: "/api",
APIGroupPrefix: "/apis",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
AdmissionControl: admit.NewAlwaysAdmit(),
Serializer: api.Codecs,
},
KubeletClient: kubeletclient.FakeKubeletClient{},
}
@ -186,6 +187,7 @@ func NewIntegrationTestMasterConfig() *master.Config {
masterConfig.EnableCoreControllers = true
masterConfig.EnableIndex = true
masterConfig.PublicAddress = net.ParseIP("192.168.10.4")
masterConfig.APIResourceConfigSource = master.DefaultAPIResourceConfigSource()
return masterConfig
}