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

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