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/admission"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/apiserver/authenticator"
"k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/capabilities"
@ -249,7 +253,7 @@ func Run(s *options.APIServer) error {
glog.Fatalf("Failure to start kubelet client: %v", err) glog.Fatalf("Failure to start kubelet client: %v", err)
} }
apiGroupVersionOverrides, err := parseRuntimeConfig(s) apiResourceConfigSource, err := parseRuntimeConfig(s)
if err != nil { if err != nil {
glog.Fatalf("error in parsing runtime-config: %s", err) glog.Fatalf("error in parsing runtime-config: %s", err)
} }
@ -292,7 +296,7 @@ func Run(s *options.APIServer) error {
} }
storageDestinations.AddAPIGroup("", etcdStorage) storageDestinations.AddAPIGroup("", etcdStorage)
if !apiGroupVersionOverrides["extensions/v1beta1"].Disable { if apiResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
glog.Infof("Configuring extensions/v1beta1 storage destination") glog.Infof("Configuring extensions/v1beta1 storage destination")
expGroup, err := registered.Group(extensions.GroupName) expGroup, err := registered.Group(extensions.GroupName)
if err != nil { if err != nil {
@ -321,7 +325,7 @@ func Run(s *options.APIServer) error {
// autoscaling/v1/horizontalpodautoscalers is a move from extensions/v1beta1/horizontalpodautoscalers. // autoscaling/v1/horizontalpodautoscalers is a move from extensions/v1beta1/horizontalpodautoscalers.
// The storage version needs to be either extensions/v1beta1 or autoscaling/v1. // 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. // 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") glog.Infof("Configuring autoscaling/v1 storage destination")
autoscalingGroup, err := registered.Group(autoscaling.GroupName) autoscalingGroup, err := registered.Group(autoscaling.GroupName)
if err != nil { if err != nil {
@ -348,7 +352,7 @@ func Run(s *options.APIServer) error {
// version needs to be either extensions/v1beta1 or batch/v1. Users // version needs to be either extensions/v1beta1 or batch/v1. Users
// must roll forward while using 1.2, because we will require the // must roll forward while using 1.2, because we will require the
// latter for 1.3. // latter for 1.3.
if !apiGroupVersionOverrides["batch/v1"].Disable { if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
glog.Infof("Configuring batch/v1 storage destination") glog.Infof("Configuring batch/v1 storage destination")
batchGroup, err := registered.Group(batch.GroupName) batchGroup, err := registered.Group(batch.GroupName)
if err != nil { if err != nil {
@ -464,7 +468,7 @@ func Run(s *options.APIServer) error {
SupportsBasicAuth: len(s.BasicAuthFile) > 0, SupportsBasicAuth: len(s.BasicAuthFile) > 0,
Authorizer: authorizer, Authorizer: authorizer,
AdmissionControl: admissionController, AdmissionControl: admissionController,
APIGroupVersionOverrides: apiGroupVersionOverrides, APIResourceConfigSource: apiResourceConfigSource,
MasterServiceNamespace: s.MasterServiceNamespace, MasterServiceNamespace: s.MasterServiceNamespace,
MasterCount: s.MasterCount, MasterCount: s.MasterCount,
ExternalHost: s.ExternalHost, ExternalHost: s.ExternalHost,
@ -511,13 +515,24 @@ func getRuntimeConfigValue(s *options.APIServer, apiKey string, defaultValue boo
return defaultValue return defaultValue
} }
// Parses the given runtime-config and formats it into map[string]ApiGroupVersionOverride // Parses the given runtime-config and formats it into genericapiserver.APIResourceConfigSource
func parseRuntimeConfig(s *options.APIServer) (map[string]genericapiserver.APIGroupVersionOverride, error) { 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. // "api/all=false" allows users to selectively enable specific api versions.
disableAllAPIs := false enableAPIByDefault := true
allAPIFlagValue, ok := s.RuntimeConfig["api/all"] allAPIFlagValue, ok := s.RuntimeConfig["api/all"]
if ok && allAPIFlagValue == "false" { if ok && allAPIFlagValue == "false" {
disableAllAPIs = true enableAPIByDefault = false
} }
// "api/legacy=false" allows users to disable legacy api versions. // "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. _ = 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. // This takes preference over api/all and api/legacy, if specified.
disableV1 := disableAllAPIs for version, resourceSpecifier := range versionToResourceSpecifier {
v1GroupVersion := "api/v1" enableVersion := getRuntimeConfigValue(s, resourceSpecifier, enableAPIByDefault)
disableV1 = !getRuntimeConfigValue(s, v1GroupVersion, !disableV1) if enableVersion {
apiGroupVersionOverrides := map[string]genericapiserver.APIGroupVersionOverride{} resourceConfig.EnableVersions(version)
if disableV1 { } else {
apiGroupVersionOverrides[v1GroupVersion] = genericapiserver.APIGroupVersionOverride{ resourceConfig.DisableVersions(version)
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 key := range s.RuntimeConfig { for key := range s.RuntimeConfig {
if strings.HasPrefix(key, v1GroupVersion+"/") { tokens := strings.Split(key, "/")
return nil, fmt.Errorf("api/v1 resources cannot be enabled/disabled individually") if len(tokens) != 3 {
} else if strings.HasPrefix(key, extensionsGroupVersion+"/") { continue
resource := strings.TrimPrefix(key, extensionsGroupVersion+"/") }
apiGroupVersionOverride := apiGroupVersionOverrides[extensionsGroupVersion] switch {
if apiGroupVersionOverride.ResourceOverrides == nil { case strings.HasPrefix(key, extensionsGroupVersionString+"/"):
apiGroupVersionOverride.ResourceOverrides = map[string]bool{} 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/cmd/kube-apiserver/app/options"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
@ -133,12 +135,14 @@ func TestUpdateEtcdOverrides(t *testing.T) {
func TestParseRuntimeConfig(t *testing.T) { func TestParseRuntimeConfig(t *testing.T) {
testCases := []struct { testCases := []struct {
runtimeConfig map[string]string runtimeConfig map[string]string
apiGroupVersionOverrides map[string]genericapiserver.APIGroupVersionOverride expectedAPIConfig func() *genericapiserver.ResourceConfig
err bool err bool
}{ }{
{ {
runtimeConfig: map[string]string{}, runtimeConfig: map[string]string{},
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{}, expectedAPIConfig: func() *genericapiserver.ResourceConfig {
return master.DefaultAPIResourceConfigSource()
},
err: false, err: false,
}, },
{ {
@ -146,7 +150,9 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{ runtimeConfig: map[string]string{
"api/v1/pods": "false", "api/v1/pods": "false",
}, },
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{}, expectedAPIConfig: func() *genericapiserver.ResourceConfig {
return master.DefaultAPIResourceConfigSource()
},
err: true, err: true,
}, },
{ {
@ -154,10 +160,10 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{ runtimeConfig: map[string]string{
"api/v1": "false", "api/v1": "false",
}, },
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{ expectedAPIConfig: func() *genericapiserver.ResourceConfig {
"api/v1": { config := master.DefaultAPIResourceConfigSource()
Disable: true, config.DisableVersions(unversioned.GroupVersion{Group: "", Version: "v1"})
}, return config
}, },
err: false, err: false,
}, },
@ -166,10 +172,10 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{ runtimeConfig: map[string]string{
"extensions/v1beta1": "false", "extensions/v1beta1": "false",
}, },
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{ expectedAPIConfig: func() *genericapiserver.ResourceConfig {
"extensions/v1beta1": { config := master.DefaultAPIResourceConfigSource()
Disable: true, config.DisableVersions(unversioned.GroupVersion{Group: "extensions", Version: "v1beta1"})
}, return config
}, },
err: false, err: false,
}, },
@ -178,28 +184,24 @@ func TestParseRuntimeConfig(t *testing.T) {
runtimeConfig: map[string]string{ runtimeConfig: map[string]string{
"extensions/v1beta1/deployments": "false", "extensions/v1beta1/deployments": "false",
}, },
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{ expectedAPIConfig: func() *genericapiserver.ResourceConfig {
"extensions/v1beta1": { config := master.DefaultAPIResourceConfigSource()
ResourceOverrides: map[string]bool{ config.DisableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"})
"deployments": false, return config
},
},
}, },
err: false, err: false,
}, },
{ {
// Enable deployments and disable jobs. // Enable deployments and disable jobs.
runtimeConfig: map[string]string{ runtimeConfig: map[string]string{
"extensions/v1beta1/deployments": "true", "extensions/v1beta1/anything": "true",
"extensions/v1beta1/jobs": "false", "extensions/v1beta1/jobs": "false",
}, },
apiGroupVersionOverrides: map[string]genericapiserver.APIGroupVersionOverride{ expectedAPIConfig: func() *genericapiserver.ResourceConfig {
"extensions/v1beta1": { config := master.DefaultAPIResourceConfigSource()
ResourceOverrides: map[string]bool{ config.DisableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "jobs"})
"deployments": true, config.EnableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "anything"})
"jobs": false, return config
},
},
}, },
err: false, err: false,
}, },
@ -208,7 +210,7 @@ func TestParseRuntimeConfig(t *testing.T) {
s := &options.APIServer{ s := &options.APIServer{
RuntimeConfig: test.runtimeConfig, RuntimeConfig: test.runtimeConfig,
} }
apiGroupVersionOverrides, err := parseRuntimeConfig(s) actualDisablers, err := parseRuntimeConfig(s)
if err == nil && test.err { if err == nil && test.err {
t.Fatalf("expected error for test: %v", test) 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) t.Fatalf("unexpected error: %s, for test: %v", err, test)
} }
if err == nil && !reflect.DeepEqual(apiGroupVersionOverrides, test.apiGroupVersionOverrides) { expectedConfig := test.expectedAPIConfig()
t.Fatalf("unexpected apiGroupVersionOverrides. Actual: %v, expected: %v", apiGroupVersionOverrides, test.apiGroupVersionOverrides) 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() 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. // Info about an API group.
type APIGroupInfo struct { type APIGroupInfo struct {
GroupMeta apimachinery.GroupMeta GroupMeta apimachinery.GroupMeta
@ -218,7 +209,7 @@ type Config struct {
// Note that this is ignored if either EnableSwaggerSupport or EnableUISupport is false. // Note that this is ignored if either EnableSwaggerSupport or EnableUISupport is false.
EnableSwaggerUI bool EnableSwaggerUI bool
// Allows api group versions or specific resources to be conditionally enabled/disabled. // 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 // allow downstream consumers to disable the index route
EnableIndex bool EnableIndex bool
EnableProfiling bool EnableProfiling bool
@ -322,7 +313,6 @@ type GenericAPIServer struct {
authorizer authorizer.Authorizer authorizer authorizer.Authorizer
AdmissionControl admission.Interface AdmissionControl admission.Interface
MasterCount int MasterCount int
ApiGroupVersionOverrides map[string]APIGroupVersionOverride
RequestContextMapper api.RequestContextMapper RequestContextMapper api.RequestContextMapper
// ExternalAddress is the address (hostname or IP and port) that should be used in // ExternalAddress is the address (hostname or IP and port) that should be used in
@ -466,7 +456,6 @@ func New(c *Config) (*GenericAPIServer, error) {
authenticator: c.Authenticator, authenticator: c.Authenticator,
authorizer: c.Authorizer, authorizer: c.Authorizer,
AdmissionControl: c.AdmissionControl, AdmissionControl: c.AdmissionControl,
ApiGroupVersionOverrides: c.APIGroupVersionOverrides,
RequestContextMapper: c.RequestContextMapper, RequestContextMapper: c.RequestContextMapper,
Serializer: c.Serializer, Serializer: c.Serializer,

View File

@ -88,7 +88,6 @@ func TestNew(t *testing.T) {
assert.Equal(s.authenticator, config.Authenticator) assert.Equal(s.authenticator, config.Authenticator)
assert.Equal(s.authorizer, config.Authorizer) assert.Equal(s.authorizer, config.Authorizer)
assert.Equal(s.AdmissionControl, config.AdmissionControl) assert.Equal(s.AdmissionControl, config.AdmissionControl)
assert.Equal(s.ApiGroupVersionOverrides, config.APIGroupVersionOverrides)
assert.Equal(s.RequestContextMapper, config.RequestContextMapper) assert.Equal(s.RequestContextMapper, config.RequestContextMapper)
assert.Equal(s.cacheTimeout, config.CacheTimeout) assert.Equal(s.cacheTimeout, config.CacheTimeout)
assert.Equal(s.ExternalAddress, config.ExternalHost) 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"
"k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver"
apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics" apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics"
"k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver"
@ -76,7 +80,6 @@ import (
"k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage"
etcdmetrics "k8s.io/kubernetes/pkg/storage/etcd/metrics" etcdmetrics "k8s.io/kubernetes/pkg/storage/etcd/metrics"
etcdutil "k8s.io/kubernetes/pkg/storage/etcd/util" etcdutil "k8s.io/kubernetes/pkg/storage/etcd/util"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/pkg/util/wait"
daemonetcd "k8s.io/kubernetes/pkg/registry/daemonset/etcd" daemonetcd "k8s.io/kubernetes/pkg/registry/daemonset/etcd"
@ -182,7 +185,7 @@ func (m *Master) InstallAPIs(c *Config) {
apiGroupsInfo := []genericapiserver.APIGroupInfo{} apiGroupsInfo := []genericapiserver.APIGroupInfo{}
// Install v1 unless disabled. // Install v1 unless disabled.
if !m.ApiGroupVersionOverrides["api/v1"].Disable { if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) {
// Install v1 API. // Install v1 API.
m.initV1ResourcesStorage(c) m.initV1ResourcesStorage(c)
apiGroupInfo := genericapiserver.APIGroupInfo{ apiGroupInfo := genericapiserver.APIGroupInfo{
@ -227,7 +230,7 @@ func (m *Master) InstallAPIs(c *Config) {
allGroups := []unversioned.APIGroup{} allGroups := []unversioned.APIGroup{}
// Install extensions unless disabled. // 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.thirdPartyStorage = c.StorageDestinations.APIGroups[extensions.GroupName].Default
m.thirdPartyResources = map[string]thirdPartyEntry{} m.thirdPartyResources = map[string]thirdPartyEntry{}
@ -269,12 +272,12 @@ func (m *Master) InstallAPIs(c *Config) {
} }
// Install autoscaling unless disabled. // Install autoscaling unless disabled.
if !m.ApiGroupVersionOverrides["autoscaling/v1"].Disable { if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv1.SchemeGroupVersion) {
autoscalingResources := m.getAutoscalingResources(c) autoscalingResources := m.getAutoscalingResources(c)
autoscalingGroupMeta := registered.GroupOrDie(autoscaling.GroupName) autoscalingGroupMeta := registered.GroupOrDie(autoscaling.GroupName)
// Hard code preferred group version to autoscaling/v1 // Hard code preferred group version to autoscaling/v1
autoscalingGroupMeta.GroupVersion = unversioned.GroupVersion{Group: "autoscaling", Version: "v1"} autoscalingGroupMeta.GroupVersion = autoscalingapiv1.SchemeGroupVersion
apiGroupInfo := genericapiserver.APIGroupInfo{ apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *autoscalingGroupMeta, GroupMeta: *autoscalingGroupMeta,
@ -301,12 +304,12 @@ func (m *Master) InstallAPIs(c *Config) {
} }
// Install batch unless disabled. // Install batch unless disabled.
if !m.ApiGroupVersionOverrides["batch/v1"].Disable { if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
batchResources := m.getBatchResources(c) batchResources := m.getBatchResources(c)
batchGroupMeta := registered.GroupOrDie(batch.GroupName) batchGroupMeta := registered.GroupOrDie(batch.GroupName)
// Hard code preferred group version to batch/v1 // Hard code preferred group version to batch/v1
batchGroupMeta.GroupVersion = unversioned.GroupVersion{Group: "batch", Version: "v1"} batchGroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion
apiGroupInfo := genericapiserver.APIGroupInfo{ apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *batchGroupMeta, GroupMeta: *batchGroupMeta,
@ -668,17 +671,6 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV
// getExperimentalResources returns the resources for extensions api // getExperimentalResources returns the resources for extensions api
func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage { 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 { restOptions := func(resource string) generic.RESTOptions {
return generic.RESTOptions{ return generic.RESTOptions{
Storage: c.StorageDestinations.Get(extensions.GroupName, resource), Storage: c.StorageDestinations.Get(extensions.GroupName, resource),
@ -686,18 +678,20 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
DeleteCollectionWorkers: m.deleteCollectionWorkers, DeleteCollectionWorkers: m.deleteCollectionWorkers,
} }
} }
// TODO update when we support more than one version of this group
version := extensionsapiv1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
if isEnabled("horizontalpodautoscalers") { if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
m.constructHPAResources(c, storage) m.constructHPAResources(c, storage)
controllerStorage := expcontrolleretcd.NewStorage( controllerStorage := expcontrolleretcd.NewStorage(
generic.RESTOptions{Storage: c.StorageDestinations.Get("", "replicationControllers"), Decorator: m.StorageDecorator(), DeleteCollectionWorkers: m.deleteCollectionWorkers}) generic.RESTOptions{Storage: c.StorageDestinations.Get("", "replicationControllers"), Decorator: m.StorageDecorator(), DeleteCollectionWorkers: m.deleteCollectionWorkers})
storage["replicationcontrollers"] = controllerStorage.ReplicationController storage["replicationcontrollers"] = controllerStorage.ReplicationController
storage["replicationcontrollers/scale"] = controllerStorage.Scale 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{ thirdPartyControl := ThirdPartyController{
master: m, master: m,
thirdPartyResourceRegistry: thirdPartyResourceStorage, thirdPartyResourceRegistry: thirdPartyResourceStorage,
@ -712,32 +706,32 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
storage["thirdpartyresources"] = thirdPartyResourceStorage 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"] = daemonSetStorage
storage["daemonsets/status"] = daemonSetStatusStorage 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"] = deploymentStorage.Deployment
storage["deployments/status"] = deploymentStorage.Status storage["deployments/status"] = deploymentStorage.Status
storage["deployments/rollback"] = deploymentStorage.Rollback storage["deployments/rollback"] = deploymentStorage.Rollback
storage["deployments/scale"] = deploymentStorage.Scale storage["deployments/scale"] = deploymentStorage.Scale
} }
if isEnabled("jobs") { if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
m.constructJobResources(c, storage) 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"] = ingressStorage
storage["ingresses/status"] = ingressStatusStorage 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 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"] = replicaSetStorage.ReplicaSet
storage["replicasets/status"] = replicaSetStorage.Status storage["replicasets/status"] = replicaSetStorage.Status
storage["replicasets/scale"] = replicaSetStorage.Scale 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 // getAutoscalingResources returns the resources for autoscaling api
func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage { func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage {
resourceOverrides := m.ApiGroupVersionOverrides["autoscaling/v1"].ResourceOverrides // TODO update when we support more than one version of this group
isEnabled := func(resource string) bool { version := autoscalingapiv1.SchemeGroupVersion
// Check if the resource has been overriden.
if enabled, ok := resourceOverrides[resource]; ok {
return enabled
}
return !m.ApiGroupVersionOverrides["autoscaling/v1"].Disable
}
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
if isEnabled("horizontalpodautoscalers") { if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
m.constructHPAResources(c, storage) m.constructHPAResources(c, storage)
} }
return 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 // getBatchResources returns the resources for batch api
func (m *Master) getBatchResources(c *Config) map[string]rest.Storage { func (m *Master) getBatchResources(c *Config) map[string]rest.Storage {
resourceOverrides := m.ApiGroupVersionOverrides["batch/v1"].ResourceOverrides // TODO update when we support more than one version of this group
isEnabled := func(resource string) bool { version := batchapiv1.SchemeGroupVersion
// Check if the resource has been overriden.
if enabled, ok := resourceOverrides[resource]; ok {
return enabled
}
return !m.ApiGroupVersionOverrides["batch/v1"].Disable
}
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
if isEnabled("jobs") { if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
m.constructJobResources(c, storage) m.constructJobResources(c, storage)
} }
return storage return storage
@ -869,3 +851,21 @@ func (m *Master) IsTunnelSyncHealthy(req *http.Request) error {
} }
return nil 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.KubeletClient = client.FakeKubeletClient{}
config.APIPrefix = "/api" config.APIPrefix = "/api"
config.APIGroupPrefix = "/apis" config.APIGroupPrefix = "/apis"
config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil } config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
config.ProxyTLSClientConfig = &tls.Config{} config.ProxyTLSClientConfig = &tls.Config{}
@ -126,7 +127,6 @@ func TestNew(t *testing.T) {
assert.Equal(master.tunneler, config.Tunneler) assert.Equal(master.tunneler, config.Tunneler)
assert.Equal(master.APIPrefix, config.APIPrefix) assert.Equal(master.APIPrefix, config.APIPrefix)
assert.Equal(master.APIGroupPrefix, config.APIGroupPrefix) assert.Equal(master.APIGroupPrefix, config.APIGroupPrefix)
assert.Equal(master.ApiGroupVersionOverrides, config.APIGroupVersionOverrides)
assert.Equal(master.RequestContextMapper, config.RequestContextMapper) assert.Equal(master.RequestContextMapper, config.RequestContextMapper)
assert.Equal(master.MasterCount, config.MasterCount) assert.Equal(master.MasterCount, config.MasterCount)
assert.Equal(master.ClusterIP, config.PublicAddress) assert.Equal(master.ClusterIP, config.PublicAddress)

View File

@ -170,6 +170,7 @@ func NewMasterConfig() *master.Config {
Config: &genericapiserver.Config{ Config: &genericapiserver.Config{
StorageDestinations: storageDestinations, StorageDestinations: storageDestinations,
StorageVersions: storageVersions, StorageVersions: storageVersions,
APIResourceConfigSource: master.DefaultAPIResourceConfigSource(),
APIPrefix: "/api", APIPrefix: "/api",
APIGroupPrefix: "/apis", APIGroupPrefix: "/apis",
Authorizer: apiserver.NewAlwaysAllowAuthorizer(), Authorizer: apiserver.NewAlwaysAllowAuthorizer(),
@ -186,6 +187,7 @@ func NewIntegrationTestMasterConfig() *master.Config {
masterConfig.EnableCoreControllers = true masterConfig.EnableCoreControllers = true
masterConfig.EnableIndex = true masterConfig.EnableIndex = true
masterConfig.PublicAddress = net.ParseIP("192.168.10.4") masterConfig.PublicAddress = net.ParseIP("192.168.10.4")
masterConfig.APIResourceConfigSource = master.DefaultAPIResourceConfigSource()
return masterConfig return masterConfig
} }