pass APIEnablement through apiserver chain

This commit is contained in:
hzxuzhonghu 2017-12-21 11:27:20 +08:00
parent b7b5bbb20f
commit 2f403b7ad1
20 changed files with 618 additions and 436 deletions

View File

@ -61,6 +61,14 @@ func createAggregatorConfig(kubeAPIServerConfig genericapiserver.Config, command
etcdOptions.StorageConfig.Codec = aggregatorapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion)
genericConfig.RESTOptionsGetter = &genericoptions.SimpleRestOptionsFactory{Options: etcdOptions}
// override MergedResourceConfig with aggregator defaults and registry
if err := commandOptions.APIEnablement.ApplyTo(
&genericConfig,
aggregatorapiserver.DefaultAPIResourceConfigSource(),
aggregatorapiserver.Registry); err != nil {
return nil, err
}
var err error
var certBytes, keyBytes []byte
if len(commandOptions.ProxyClientCertFile) > 0 && len(commandOptions.ProxyClientKeyFile) > 0 {

View File

@ -39,6 +39,14 @@ func createAPIExtensionsConfig(kubeAPIServerConfig genericapiserver.Config, exte
etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion)
genericConfig.RESTOptionsGetter = &genericoptions.SimpleRestOptionsFactory{Options: etcdOptions}
// override MergedResourceConfig with apiextensions defaults and registry
if err := commandOptions.APIEnablement.ApplyTo(
&genericConfig,
apiextensionsapiserver.DefaultAPIResourceConfigSource(),
apiextensionsapiserver.Registry); err != nil {
return nil, err
}
apiextensionsConfig := &apiextensionsapiserver.Config{
GenericConfig: &genericapiserver.RecommendedConfig{
Config: genericConfig,

View File

@ -51,7 +51,7 @@ type ServerRunOptions struct {
Authorization *kubeoptions.BuiltInAuthorizationOptions
CloudProvider *kubeoptions.CloudProviderOptions
StorageSerialization *kubeoptions.StorageSerializationOptions
APIEnablement *kubeoptions.APIEnablementOptions
APIEnablement *genericoptions.APIEnablementOptions
AllowPrivileged bool
EnableLogsHandler bool
@ -87,7 +87,7 @@ func NewServerRunOptions() *ServerRunOptions {
Authorization: kubeoptions.NewBuiltInAuthorizationOptions(),
CloudProvider: kubeoptions.NewCloudProviderOptions(),
StorageSerialization: kubeoptions.NewStorageSerializationOptions(),
APIEnablement: kubeoptions.NewAPIEnablementOptions(),
APIEnablement: genericoptions.NewAPIEnablementOptions(),
EnableLogsHandler: true,
EventTTL: 1 * time.Hour,

View File

@ -27,7 +27,7 @@ import (
"k8s.io/apimachinery/pkg/util/diff"
apiserveroptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/pkg/storage/storagebackend"
utilconfig "k8s.io/apiserver/pkg/util/flag"
utilflag "k8s.io/apiserver/pkg/util/flag"
auditwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme"
@ -235,8 +235,8 @@ func TestAddFlags(t *testing.T) {
StorageVersions: legacyscheme.Registry.AllPreferredGroupVersions(),
DefaultStorageVersions: legacyscheme.Registry.AllPreferredGroupVersions(),
},
APIEnablement: &kubeoptions.APIEnablementOptions{
RuntimeConfig: utilconfig.ConfigurationMap{},
APIEnablement: &apiserveroptions.APIEnablementOptions{
RuntimeConfig: utilflag.ConfigurationMap{},
},
EnableLogsHandler: false,
EnableAggregatorRouting: true,

View File

@ -18,6 +18,10 @@ package options
import (
"fmt"
apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
"k8s.io/kubernetes/pkg/api/legacyscheme"
)
// TODO: Longer term we should read this from some config store, rather than a flag.
@ -75,5 +79,9 @@ func (options *ServerRunOptions) Validate() []error {
if options.MasterCount <= 0 {
errors = append(errors, fmt.Errorf("--apiserver-count should be a positive number, but value '%d' provided", options.MasterCount))
}
if errs := options.APIEnablement.Validate(legacyscheme.Registry, apiextensionsapiserver.Registry, aggregatorapiserver.Registry); len(errs) > 0 {
errors = append(errors, errs...)
}
return errors
}

View File

@ -142,6 +142,7 @@ func CreateServerChain(runOptions *options.ServerRunOptions, stopCh <-chan struc
if err != nil {
return nil, err
}
kubeAPIServerConfig, sharedInformers, versionedInformers, insecureServingOptions, serviceResolver, err := CreateKubeAPIServerConfig(runOptions, nodeTunneler, proxyTransport)
if err != nil {
return nil, err
@ -304,7 +305,7 @@ func CreateKubeAPIServerConfig(s *options.ServerRunOptions, nodeTunneler tunnele
return nil, nil, nil, nil, nil, err
}
storageFactory, err := BuildStorageFactory(s)
storageFactory, err := BuildStorageFactory(s, genericConfig.MergedResourceConfig)
if err != nil {
return nil, nil, nil, nil, nil, err
}
@ -384,6 +385,9 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp
if err := s.Features.ApplyTo(genericConfig); err != nil {
return nil, nil, nil, nil, nil, err
}
if err := s.APIEnablement.ApplyTo(genericConfig, master.DefaultAPIResourceConfigSource(), legacyscheme.Registry); err != nil {
return nil, nil, nil, nil, nil, err
}
genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, legacyscheme.Scheme)
genericConfig.OpenAPIConfig.PostProcessSpec = postProcessOpenAPISpecForBackwardCompatibility
@ -398,7 +402,7 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp
kubeVersion := version.Get()
genericConfig.Version = &kubeVersion
storageFactory, err := BuildStorageFactory(s)
storageFactory, err := BuildStorageFactory(s, genericConfig.MergedResourceConfig)
if err != nil {
return nil, nil, nil, nil, nil, err
}
@ -561,7 +565,7 @@ func BuildAuthorizer(s *options.ServerRunOptions, sharedInformers informers.Shar
// BuildStorageFactory constructs the storage factory. If encryption at rest is used, it expects
// all supported KMS plugins to be registered in the KMS plugin registry before being called.
func BuildStorageFactory(s *options.ServerRunOptions) (*serverstorage.DefaultStorageFactory, error) {
func BuildStorageFactory(s *options.ServerRunOptions, apiResourceConfig *serverstorage.ResourceConfig) (*serverstorage.DefaultStorageFactory, error) {
storageGroupsToEncodingVersion, err := s.StorageSerialization.StorageGroupsToEncodingVersion()
if err != nil {
return nil, fmt.Errorf("error generating storage version map: %s", err)
@ -577,7 +581,7 @@ func BuildStorageFactory(s *options.ServerRunOptions) (*serverstorage.DefaultSto
storage.Resource("volumeattachments").WithVersion("v1alpha1"),
admissionregistration.Resource("initializerconfigurations").WithVersion("v1alpha1"),
},
master.DefaultAPIResourceConfigSource(), s.APIEnablement.RuntimeConfig)
apiResourceConfig)
if err != nil {
return nil, fmt.Errorf("error in initializing storage factory: %s", err)
}
@ -698,6 +702,20 @@ func defaultOptions(s *options.ServerRunOptions) error {
}
}
// TODO: remove when we stop supporting the legacy group version.
if s.APIEnablement.RuntimeConfig != nil {
for key, value := range s.APIEnablement.RuntimeConfig {
if key == "v1" || strings.HasPrefix(key, "v1/") ||
key == "api/v1" || strings.HasPrefix(key, "api/v1/") {
delete(s.APIEnablement.RuntimeConfig, key)
s.APIEnablement.RuntimeConfig["/v1"] = value
}
if key == "api/legacy" {
delete(s.APIEnablement.RuntimeConfig, key)
}
}
}
return nil
}

View File

@ -17,16 +17,11 @@ limitations under the License.
package kubeapiserver
import (
"fmt"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/server/resourceconfig"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiserver/pkg/storage/storagebackend"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/kubernetes/pkg/api/legacyscheme"
)
// SpecialDefaultResourcePrefixes are prefixes compiled into Kubernetes.
@ -41,151 +36,17 @@ var SpecialDefaultResourcePrefixes = map[schema.GroupResource]string{
}
// NewStorageFactory builds the DefaultStorageFactory.
// Merges defaultResourceConfig with the user specified overrides and merges
// defaultAPIResourceConfig with the corresponding user specified overrides as well.
func NewStorageFactory(storageConfig storagebackend.Config, defaultMediaType string, serializer runtime.StorageSerializer,
defaultResourceEncoding *serverstorage.DefaultResourceEncodingConfig, storageEncodingOverrides map[string]schema.GroupVersion, resourceEncodingOverrides []schema.GroupVersionResource,
defaultAPIResourceConfig *serverstorage.ResourceConfig, resourceConfigOverrides utilflag.ConfigurationMap) (*serverstorage.DefaultStorageFactory, error) {
resourceEncodingConfig := mergeGroupEncodingConfigs(defaultResourceEncoding, storageEncodingOverrides)
resourceEncodingConfig = mergeResourceEncodingConfigs(resourceEncodingConfig, resourceEncodingOverrides)
apiResourceConfig, err := mergeAPIResourceConfigs(defaultAPIResourceConfig, resourceConfigOverrides)
if err != nil {
return nil, err
}
// Merges defaultResourceEncoding with the user specified overrides.
func NewStorageFactory(
storageConfig storagebackend.Config,
defaultMediaType string,
serializer runtime.StorageSerializer,
defaultResourceEncoding *serverstorage.DefaultResourceEncodingConfig,
storageEncodingOverrides map[string]schema.GroupVersion,
resourceEncodingOverrides []schema.GroupVersionResource,
apiResourceConfig *serverstorage.ResourceConfig,
) (*serverstorage.DefaultStorageFactory, error) {
resourceEncodingConfig := resourceconfig.MergeGroupEncodingConfigs(defaultResourceEncoding, storageEncodingOverrides)
resourceEncodingConfig = resourceconfig.MergeResourceEncodingConfigs(resourceEncodingConfig, resourceEncodingOverrides)
return serverstorage.NewDefaultStorageFactory(storageConfig, defaultMediaType, serializer, resourceEncodingConfig, apiResourceConfig, SpecialDefaultResourcePrefixes), nil
}
// Merges the given defaultResourceConfig with specific GroupVersionResource overrides.
func mergeResourceEncodingConfigs(defaultResourceEncoding *serverstorage.DefaultResourceEncodingConfig, resourceEncodingOverrides []schema.GroupVersionResource) *serverstorage.DefaultResourceEncodingConfig {
resourceEncodingConfig := defaultResourceEncoding
for _, gvr := range resourceEncodingOverrides {
resourceEncodingConfig.SetResourceEncoding(gvr.GroupResource(), gvr.GroupVersion(),
schema.GroupVersion{Group: gvr.Group, Version: runtime.APIVersionInternal})
}
return resourceEncodingConfig
}
// Merges the given defaultResourceConfig with specific GroupVersion overrides.
func mergeGroupEncodingConfigs(defaultResourceEncoding *serverstorage.DefaultResourceEncodingConfig, storageEncodingOverrides map[string]schema.GroupVersion) *serverstorage.DefaultResourceEncodingConfig {
resourceEncodingConfig := defaultResourceEncoding
for group, storageEncodingVersion := range storageEncodingOverrides {
resourceEncodingConfig.SetVersionEncoding(group, storageEncodingVersion, schema.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
}
return resourceEncodingConfig
}
// Merges the given defaultAPIResourceConfig with the given resourceConfigOverrides.
func mergeAPIResourceConfigs(defaultAPIResourceConfig *serverstorage.ResourceConfig, resourceConfigOverrides utilflag.ConfigurationMap) (*serverstorage.ResourceConfig, error) {
resourceConfig := defaultAPIResourceConfig
overrides := resourceConfigOverrides
// "api/all=false" allows users to selectively enable specific api versions.
allAPIFlagValue, ok := overrides["api/all"]
if ok {
if allAPIFlagValue == "false" {
// Disable all group versions.
resourceConfig.DisableVersions(legacyscheme.Registry.RegisteredGroupVersions()...)
} else if allAPIFlagValue == "true" {
resourceConfig.EnableVersions(legacyscheme.Registry.RegisteredGroupVersions()...)
}
}
// "api/legacy=false" allows users to disable legacy api versions.
disableLegacyAPIs := false
legacyAPIFlagValue, ok := overrides["api/legacy"]
if ok && legacyAPIFlagValue == "false" {
disableLegacyAPIs = true
}
_ = disableLegacyAPIs // hush the compiler while we don't have legacy APIs to disable.
// "<resourceSpecifier>={true|false} allows users to enable/disable API.
// This takes preference over api/all and api/legacy, if specified.
// Iterate through all group/version overrides specified in runtimeConfig.
for key := range overrides {
if key == "api/all" || key == "api/legacy" {
// Have already handled them above. Can skip them here.
continue
}
tokens := strings.Split(key, "/")
if len(tokens) != 2 {
continue
}
groupVersionString := tokens[0] + "/" + tokens[1]
// HACK: Hack for "v1" legacy group version.
// Remove when we stop supporting the legacy group version.
if groupVersionString == "api/v1" {
groupVersionString = "v1"
}
groupVersion, err := schema.ParseGroupVersion(groupVersionString)
if err != nil {
return nil, fmt.Errorf("invalid key %s", key)
}
// Verify that the groupVersion is legacyscheme.Registry.
if !legacyscheme.Registry.IsRegisteredVersion(groupVersion) {
return nil, fmt.Errorf("group version %s that has not been registered", groupVersion.String())
}
enabled, err := getRuntimeConfigValue(overrides, key, false)
if err != nil {
return nil, err
}
if enabled {
resourceConfig.EnableVersions(groupVersion)
} else {
resourceConfig.DisableVersions(groupVersion)
}
}
// Iterate through all group/version/resource overrides specified in runtimeConfig.
for key := range overrides {
tokens := strings.Split(key, "/")
if len(tokens) != 3 {
continue
}
groupVersionString := tokens[0] + "/" + tokens[1]
// HACK: Hack for "v1" legacy group version.
// Remove when we stop supporting the legacy group version.
if groupVersionString == "api/v1" {
groupVersionString = "v1"
}
groupVersion, err := schema.ParseGroupVersion(groupVersionString)
if err != nil {
return nil, fmt.Errorf("invalid key %s", key)
}
resource := tokens[2]
// Verify that the groupVersion is legacyscheme.Registry.
if !legacyscheme.Registry.IsRegisteredVersion(groupVersion) {
return nil, fmt.Errorf("group version %s that has not been registered", groupVersion.String())
}
if !resourceConfig.AnyResourcesForVersionEnabled(groupVersion) {
return nil, fmt.Errorf("%v is disabled, you cannot configure its resources individually", groupVersion)
}
enabled, err := getRuntimeConfigValue(overrides, key, false)
if err != nil {
return nil, err
}
if enabled {
resourceConfig.EnableResources(groupVersion.WithResource(resource))
} else {
resourceConfig.DisableResources(groupVersion.WithResource(resource))
}
}
return resourceConfig, nil
}
func getRuntimeConfigValue(overrides utilflag.ConfigurationMap, apiKey string, defaultValue bool) (bool, error) {
flagValue, ok := overrides[apiKey]
if ok {
if flagValue == "" {
return true, nil
}
boolValue, err := strconv.ParseBool(flagValue)
if err != nil {
return false, fmt.Errorf("invalid value of %s: %s, err: %v", apiKey, flagValue, err)
}
return boolValue, nil
}
return defaultValue, nil
}

View File

@ -1,210 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
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 kubeapiserver
import (
"reflect"
"testing"
apiv1 "k8s.io/api/core/v1"
extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
_ "k8s.io/kubernetes/pkg/apis/core/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
)
func TestParseRuntimeConfig(t *testing.T) {
extensionsGroupVersion := extensionsapiv1beta1.SchemeGroupVersion
apiv1GroupVersion := apiv1.SchemeGroupVersion
testCases := []struct {
runtimeConfig map[string]string
defaultResourceConfig func() *serverstorage.ResourceConfig
expectedAPIConfig func() *serverstorage.ResourceConfig
err bool
}{
{
// everything default value.
runtimeConfig: map[string]string{},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
err: false,
},
{
// no runtimeConfig override.
runtimeConfig: map[string]string{},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
err: false,
},
{
// version enabled by runtimeConfig override.
runtimeConfig: map[string]string{
"extensions/v1beta1": "",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.EnableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
err: false,
},
{
// disable resource
runtimeConfig: map[string]string{
"api/v1/pods": "false",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.EnableVersions(apiv1GroupVersion)
return config
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.EnableVersions(apiv1GroupVersion)
config.DisableResources(apiv1GroupVersion.WithResource("pods"))
return config
},
err: false,
},
{
// Disable v1.
runtimeConfig: map[string]string{
"api/v1": "false",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.DisableVersions(apiv1GroupVersion)
return config
},
err: false,
},
{
// Enable deployments and disable daemonsets.
runtimeConfig: map[string]string{
"extensions/v1beta1/anything": "true",
"extensions/v1beta1/daemonsets": "false",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.EnableVersions(extensionsGroupVersion)
return config
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.EnableVersions(extensionsGroupVersion)
config.DisableResources(extensionsGroupVersion.WithResource("daemonsets"))
config.EnableResources(extensionsGroupVersion.WithResource("anything"))
return config
},
err: false,
},
{
// invalid runtime config
runtimeConfig: map[string]string{
"invalidgroup/version": "false",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
err: true,
},
{
// cannot disable individual resource when version is not enabled.
runtimeConfig: map[string]string{
"api/v1/pods": "false",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.DisableResources(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"})
return config
},
err: true,
},
{
// enable all
runtimeConfig: map[string]string{
"api/all": "true",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.EnableVersions(legacyscheme.Registry.RegisteredGroupVersions()...)
return config
},
err: false,
},
{
// disable all
runtimeConfig: map[string]string{
"api/all": "false",
},
defaultResourceConfig: func() *serverstorage.ResourceConfig {
return serverstorage.NewResourceConfig()
},
expectedAPIConfig: func() *serverstorage.ResourceConfig {
config := serverstorage.NewResourceConfig()
config.DisableVersions(legacyscheme.Registry.RegisteredGroupVersions()...)
return config
},
err: false,
},
}
for _, test := range testCases {
actualDisablers, err := mergeAPIResourceConfigs(test.defaultResourceConfig(), test.runtimeConfig)
if err == nil && test.err {
t.Fatalf("expected error for test: %v", test)
} else if err != nil && !test.err {
t.Fatalf("unexpected error: %s, for test: %v", err, test)
}
expectedConfig := test.expectedAPIConfig()
if err == nil && !reflect.DeepEqual(actualDisablers, expectedConfig) {
t.Fatalf("%v: unexpected apiResourceDisablers. Actual: %v\n expected: %v", test.runtimeConfig, actualDisablers, expectedConfig)
}
}
}

View File

@ -1,45 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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 options
import (
"github.com/spf13/pflag"
utilflag "k8s.io/apiserver/pkg/util/flag"
)
// APIEnablementOptions contains the options for which resources to turn on and off.
// Given small aggregated API servers, this option isn't required for "normal" API servers
type APIEnablementOptions struct {
RuntimeConfig utilflag.ConfigurationMap
}
func NewAPIEnablementOptions() *APIEnablementOptions {
return &APIEnablementOptions{
RuntimeConfig: make(utilflag.ConfigurationMap),
}
}
// AddFlags adds flags for a specific APIServer to the specified FlagSet
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
fs.Var(&s.RuntimeConfig, "runtime-config", ""+
"A set of key=value pairs that describe runtime configuration that may be passed "+
"to apiserver. <group>/<version> (or <version> for the core group) key can be used to "+
"turn on/off specific api versions. <grouop>/<version>/<resource> (or <version>/<resource> "+
"for the core group) can be used to turn on/off specific resources. api/all and "+
"api/legacy are special keys to control all and legacy api versions respectively.")
}

View File

@ -35,6 +35,7 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"
@ -53,7 +54,7 @@ import (
var (
groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
registry = registered.NewOrDie("")
Registry = registered.NewOrDie("")
Scheme = runtime.NewScheme()
Codecs = serializer.NewCodecFactory(Scheme)
@ -70,7 +71,7 @@ var (
)
func init() {
install.Install(groupFactoryRegistry, registry, Scheme)
install.Install(groupFactoryRegistry, Registry, Scheme)
// we need to add the options to empty v1
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})
@ -131,13 +132,19 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
GenericAPIServer: genericServer,
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)
apiGroupInfo.GroupMeta.GroupVersion = v1beta1.SchemeGroupVersion
customResourceDefintionStorage := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
v1beta1storage := map[string]rest.Storage{}
v1beta1storage["customresourcedefinitions"] = customResourceDefintionStorage
v1beta1storage["customresourcedefinitions/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefintionStorage)
apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = v1beta1storage
apiResourceConfig := c.GenericConfig.MergedResourceConfig
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Registry, Scheme, metav1.ParameterCodec, Codecs)
if apiResourceConfig.AnyResourcesForVersionEnabled(v1beta1.SchemeGroupVersion) {
apiGroupInfo.GroupMeta.GroupVersion = v1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
version := v1beta1.SchemeGroupVersion
if apiResourceConfig.ResourceEnabled(version.WithResource("customresourcedefinitions")) {
customResourceDefintionStorage := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
storage["customresourcedefinitions"] = customResourceDefintionStorage
storage["customresourcedefinitions/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefintionStorage)
}
apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = storage
}
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
return nil, err
@ -211,3 +218,13 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
return s, nil
}
func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
ret := serverstorage.NewResourceConfig()
// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
ret.EnableVersions(
v1beta1.SchemeGroupVersion,
)
return ret
}

View File

@ -36,6 +36,7 @@ const defaultEtcdPathPrefix = "/registry/apiextensions.kubernetes.io"
type CustomResourceDefinitionsServerOptions struct {
RecommendedOptions *genericoptions.RecommendedOptions
APIEnablement *genericoptions.APIEnablementOptions
StdOut io.Writer
StdErr io.Writer
@ -44,6 +45,7 @@ type CustomResourceDefinitionsServerOptions struct {
func NewCustomResourceDefinitionsServerOptions(out, errOut io.Writer) *CustomResourceDefinitionsServerOptions {
o := &CustomResourceDefinitionsServerOptions{
RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion)),
APIEnablement: genericoptions.NewAPIEnablementOptions(),
StdOut: out,
StdErr: errOut,
@ -77,13 +79,14 @@ func NewCommandStartCustomResourceDefinitionsServer(out, errOut io.Writer, stopC
flags := cmd.Flags()
o.RecommendedOptions.AddFlags(flags)
o.APIEnablement.AddFlags(flags)
return cmd
}
func (o CustomResourceDefinitionsServerOptions) Validate(args []string) error {
errors := []error{}
errors = append(errors, o.RecommendedOptions.Validate()...)
errors = append(errors, o.APIEnablement.Validate(apiserver.Registry)...)
return utilerrors.NewAggregate(errors)
}
@ -101,6 +104,9 @@ func (o CustomResourceDefinitionsServerOptions) Config() (*apiserver.Config, err
if err := o.RecommendedOptions.ApplyTo(serverConfig, apiserver.Scheme); err != nil {
return nil, err
}
if err := o.APIEnablement.ApplyTo(&serverConfig.Config, apiserver.DefaultAPIResourceConfigSource(), apiserver.Registry); err != nil {
return nil, err
}
config := &apiserver.Config{
GenericConfig: serverConfig,

View File

@ -62,6 +62,9 @@ func DefaultServerConfig() (*extensionsapiserver.Config, error) {
if err := options.RecommendedOptions.ApplyTo(genericConfig, nil); err != nil {
return nil, err
}
if err := options.APIEnablement.ApplyTo(&genericConfig.Config, extensionsapiserver.DefaultAPIResourceConfigSource(), extensionsapiserver.Registry); err != nil {
return nil, err
}
customResourceDefinitionRESTOptionsGetter := extensionsapiserver.CRDRESTOptionsGetter{
StorageConfig: options.RecommendedOptions.Etcd.StorageConfig,

View File

@ -57,6 +57,7 @@ import (
genericfilters "k8s.io/apiserver/pkg/server/filters"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/routes"
serverstore "k8s.io/apiserver/pkg/server/storage"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
restclient "k8s.io/client-go/rest"
@ -175,6 +176,11 @@ type Config struct {
// if the client requests it via Accept-Encoding
EnableAPIResponseCompression bool
// MergedResourceConfig indicates which groupVersion enabled and its resources enabled/disabled.
// This is composed of genericapiserver defaultAPIResourceConfig and those parsed from flags.
// If not specify any in flags, then genericapiserver will only enable defaultAPIResourceConfig.
MergedResourceConfig *serverstore.ResourceConfig
//===========================================================================
// values below here are targets for removal
//===========================================================================

View File

@ -0,0 +1,111 @@
/*
Copyright 2017 The Kubernetes Authors.
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 options
import (
"fmt"
"strings"
"github.com/spf13/pflag"
"k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/resourceconfig"
serverstore "k8s.io/apiserver/pkg/server/storage"
utilflag "k8s.io/apiserver/pkg/util/flag"
)
// APIEnablementOptions contains the options for which resources to turn on and off.
// Given small aggregated API servers, this option isn't required for "normal" API servers
type APIEnablementOptions struct {
RuntimeConfig utilflag.ConfigurationMap
}
func NewAPIEnablementOptions() *APIEnablementOptions {
return &APIEnablementOptions{
RuntimeConfig: make(utilflag.ConfigurationMap),
}
}
// AddFlags adds flags for a specific APIServer to the specified FlagSet
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
fs.Var(&s.RuntimeConfig, "runtime-config", ""+
"A set of key=value pairs that describe runtime configuration that may be passed "+
"to apiserver. <group>/<version> (or <version> for the core group) key can be used to "+
"turn on/off specific api versions. api/all is special key to control all api versions, "+
"be careful setting it false, unless you know what you do. api/legacy is deprecated, "+
"we will remove it in the future, so stop using it.")
}
// Validate validates RuntimeConfig with a list of registries.
// Usually this list only has one element, the apiserver registry of the process.
// But in the advanced (and usually not recommended) case of delegated apiservers there can be more.
// Validate will filter out the known groups of each registry.
// If anything is left over after that, an error is returned.
func (s *APIEnablementOptions) Validate(registries ...GroupRegisty) []error {
if s == nil {
return nil
}
errors := []error{}
if s.RuntimeConfig["api/all"] == "false" && len(s.RuntimeConfig) == 1 {
// Do not allow only set api/all=false, in such case apiserver startup has no meaning.
return append(errors, fmt.Errorf("invliad key with only api/all=false"))
}
groups, err := resourceconfig.ParseGroups(s.RuntimeConfig)
if err != nil {
return append(errors, err)
}
for _, registry := range registries {
// filter out known groups
groups = unknownGroups(groups, registry)
}
if len(groups) != 0 {
errors = append(errors, fmt.Errorf("unknown api groups %s", strings.Join(groups, ",")))
}
return errors
}
// ApplyTo override MergedResourceConfig with defaults and registry
func (s *APIEnablementOptions) ApplyTo(c *server.Config, defaultResourceConfig *serverstore.ResourceConfig, registry resourceconfig.GroupVersionRegistry) error {
if s == nil {
return nil
}
mergedResourceConfig, err := resourceconfig.MergeAPIResourceConfigs(defaultResourceConfig, s.RuntimeConfig, registry)
c.MergedResourceConfig = mergedResourceConfig
return err
}
func unknownGroups(groups []string, registry GroupRegisty) []string {
unknownGroups := []string{}
for _, group := range groups {
if !registry.IsRegistered(group) {
unknownGroups = append(unknownGroups, group)
}
}
return unknownGroups
}
// GroupRegisty provides a method to check whether given group is registered.
type GroupRegisty interface {
// IsRegistered returns true if given group is registered.
IsRegistered(group string) bool
}

View File

@ -0,0 +1,18 @@
/*
Copyright 2017 The Kubernetes Authors.
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 resourceconfig contains the resource config related helper functions.
package resourceconfig // import "k8s.io/apiserver/pkg/server/resourceconfig"

View File

@ -0,0 +1,164 @@
/*
Copyright 2017 The Kubernetes Authors.
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 resourceconfig
import (
"fmt"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
serverstore "k8s.io/apiserver/pkg/server/storage"
utilflag "k8s.io/apiserver/pkg/util/flag"
)
// GroupVersionRegistry provides access to registered group versions.
type GroupVersionRegistry interface {
// IsRegistered returns true if given group is registered.
IsRegistered(group string) bool
// IsRegisteredVersion returns true if given version is registered.
IsRegisteredVersion(v schema.GroupVersion) bool
// RegisteredGroupVersions returns all registered group versions.
RegisteredGroupVersions() []schema.GroupVersion
}
// MergeResourceEncodingConfigs merges the given defaultResourceConfig with specific GroupVersionResource overrides.
func MergeResourceEncodingConfigs(
defaultResourceEncoding *serverstore.DefaultResourceEncodingConfig,
resourceEncodingOverrides []schema.GroupVersionResource,
) *serverstore.DefaultResourceEncodingConfig {
resourceEncodingConfig := defaultResourceEncoding
for _, gvr := range resourceEncodingOverrides {
resourceEncodingConfig.SetResourceEncoding(gvr.GroupResource(), gvr.GroupVersion(),
schema.GroupVersion{Group: gvr.Group, Version: runtime.APIVersionInternal})
}
return resourceEncodingConfig
}
// MergeGroupEncodingConfigs merges the given defaultResourceConfig with specific GroupVersion overrides.
func MergeGroupEncodingConfigs(
defaultResourceEncoding *serverstore.DefaultResourceEncodingConfig,
storageEncodingOverrides map[string]schema.GroupVersion,
) *serverstore.DefaultResourceEncodingConfig {
resourceEncodingConfig := defaultResourceEncoding
for group, storageEncodingVersion := range storageEncodingOverrides {
resourceEncodingConfig.SetVersionEncoding(group, storageEncodingVersion, schema.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
}
return resourceEncodingConfig
}
// MergeAPIResourceConfigs merges the given defaultAPIResourceConfig with the given resourceConfigOverrides.
// Exclude the groups not registered in registry, and check if version is
// not registered in group, then it will fail.
func MergeAPIResourceConfigs(
defaultAPIResourceConfig *serverstore.ResourceConfig,
resourceConfigOverrides utilflag.ConfigurationMap,
registry GroupVersionRegistry,
) (*serverstore.ResourceConfig, error) {
resourceConfig := defaultAPIResourceConfig
overrides := resourceConfigOverrides
// "api/all=false" allows users to selectively enable specific api versions.
allAPIFlagValue, ok := overrides["api/all"]
if ok {
if allAPIFlagValue == "false" {
// Disable all group versions.
resourceConfig.DisableVersions(registry.RegisteredGroupVersions()...)
} else if allAPIFlagValue == "true" {
resourceConfig.EnableVersions(registry.RegisteredGroupVersions()...)
}
}
// "<resourceSpecifier>={true|false} allows users to enable/disable API.
// This takes preference over api/all, if specified.
// Iterate through all group/version overrides specified in runtimeConfig.
for key := range overrides {
// Have already handled them above. Can skip them here.
if key == "api/all" {
continue
}
tokens := strings.Split(key, "/")
if len(tokens) != 2 {
continue
}
groupVersionString := tokens[0] + "/" + tokens[1]
groupVersion, err := schema.ParseGroupVersion(groupVersionString)
if err != nil {
return nil, fmt.Errorf("invalid key %s", key)
}
// Exclude group not registered into the registry.
if !registry.IsRegistered(groupVersion.Group) {
continue
}
// Verify that the groupVersion is registered into registry.
if !registry.IsRegisteredVersion(groupVersion) {
return nil, fmt.Errorf("group version %s that has not been registered", groupVersion.String())
}
enabled, err := getRuntimeConfigValue(overrides, key, false)
if err != nil {
return nil, err
}
if enabled {
resourceConfig.EnableVersions(groupVersion)
} else {
resourceConfig.DisableVersions(groupVersion)
}
}
return resourceConfig, nil
}
func getRuntimeConfigValue(overrides utilflag.ConfigurationMap, apiKey string, defaultValue bool) (bool, error) {
flagValue, ok := overrides[apiKey]
if ok {
if flagValue == "" {
return true, nil
}
boolValue, err := strconv.ParseBool(flagValue)
if err != nil {
return false, fmt.Errorf("invalid value of %s: %s, err: %v", apiKey, flagValue, err)
}
return boolValue, nil
}
return defaultValue, nil
}
// ParseGroups takes in resourceConfig and returns parsed groups.
func ParseGroups(resourceConfig utilflag.ConfigurationMap) ([]string, error) {
groups := []string{}
for key := range resourceConfig {
if key == "api/all" {
continue
}
tokens := strings.Split(key, "/")
if len(tokens) != 2 && len(tokens) != 3 {
return groups, fmt.Errorf("runtime-config invalid key %s", key)
}
groupVersionString := tokens[0] + "/" + tokens[1]
groupVersion, err := schema.ParseGroupVersion(groupVersionString)
if err != nil {
return nil, fmt.Errorf("runtime-config invalid key %s", key)
}
groups = append(groups, groupVersion.Group)
}
return groups, nil
}

View File

@ -0,0 +1,178 @@
/*
Copyright 2017 The Kubernetes Authors.
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 resourceconfig
import (
"reflect"
"testing"
apiv1 "k8s.io/api/core/v1"
extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/apimachinery"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime/schema"
serverstore "k8s.io/apiserver/pkg/server/storage"
)
func TestParseRuntimeConfig(t *testing.T) {
registry := newFakeRegistry()
apiv1GroupVersion := apiv1.SchemeGroupVersion
testCases := []struct {
runtimeConfig map[string]string
defaultResourceConfig func() *serverstore.ResourceConfig
expectedAPIConfig func() *serverstore.ResourceConfig
err bool
}{
{
// everything default value.
runtimeConfig: map[string]string{},
defaultResourceConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
err: false,
},
{
// no runtimeConfig override.
runtimeConfig: map[string]string{},
defaultResourceConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
err: false,
},
{
// version enabled by runtimeConfig override.
runtimeConfig: map[string]string{
"extensions/v1beta1": "",
},
defaultResourceConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
return config
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
return config
},
err: false,
},
{
// Disable v1.
runtimeConfig: map[string]string{
"/v1": "false",
},
defaultResourceConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
config.DisableVersions(apiv1GroupVersion)
return config
},
err: false,
},
{
// invalid runtime config
runtimeConfig: map[string]string{
"invalidgroup/version": "false",
},
defaultResourceConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
err: false,
},
{
// enable all
runtimeConfig: map[string]string{
"api/all": "true",
},
defaultResourceConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
config.EnableVersions(registry.RegisteredGroupVersions()...)
return config
},
err: false,
},
{
// only enable v1
runtimeConfig: map[string]string{
"api/all": "false",
"/v1": "true",
},
defaultResourceConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
err: false,
},
}
for index, test := range testCases {
actualDisablers, err := MergeAPIResourceConfigs(test.defaultResourceConfig(), test.runtimeConfig, registry)
if err == nil && test.err {
t.Fatalf("expected error for test case: %v", index)
} else if err != nil && !test.err {
t.Fatalf("unexpected error: %s, for test: %v", err, test)
}
expectedConfig := test.expectedAPIConfig()
if err == nil && !reflect.DeepEqual(actualDisablers, expectedConfig) {
t.Fatalf("%v: unexpected apiResourceDisablers. Actual: %v\n expected: %v", test.runtimeConfig, actualDisablers, expectedConfig)
}
}
}
func newFakeAPIResourceConfigSource() *serverstore.ResourceConfig {
ret := serverstore.NewResourceConfig()
// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
ret.EnableVersions(
apiv1.SchemeGroupVersion,
extensionsapiv1beta1.SchemeGroupVersion,
)
return ret
}
func newFakeRegistry() *registered.APIRegistrationManager {
registry := registered.NewOrDie("")
registry.RegisterGroup(apimachinery.GroupMeta{
GroupVersion: apiv1.SchemeGroupVersion,
})
registry.RegisterGroup(apimachinery.GroupMeta{
GroupVersion: extensionsapiv1beta1.SchemeGroupVersion,
})
registry.RegisterVersions([]schema.GroupVersion{apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion})
return registry
}

View File

@ -151,7 +151,14 @@ var _ StorageFactory = &DefaultStorageFactory{}
const AllResources = "*"
func NewDefaultStorageFactory(config storagebackend.Config, defaultMediaType string, defaultSerializer runtime.StorageSerializer, resourceEncodingConfig ResourceEncodingConfig, resourceConfig APIResourceConfigSource, specialDefaultResourcePrefixes map[schema.GroupResource]string) *DefaultStorageFactory {
func NewDefaultStorageFactory(
config storagebackend.Config,
defaultMediaType string,
defaultSerializer runtime.StorageSerializer,
resourceEncodingConfig ResourceEncodingConfig,
resourceConfig APIResourceConfigSource,
specialDefaultResourcePrefixes map[schema.GroupResource]string,
) *DefaultStorageFactory {
config.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
if len(defaultMediaType) == 0 {
defaultMediaType = runtime.ContentTypeJSON

View File

@ -30,6 +30,7 @@ import (
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/client-go/pkg/version"
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
@ -45,13 +46,13 @@ import (
var (
groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
registry = registered.NewOrDie("")
Registry = registered.NewOrDie("")
Scheme = runtime.NewScheme()
Codecs = serializer.NewCodecFactory(Scheme)
)
func init() {
install.Install(groupFactoryRegistry, registry, Scheme)
install.Install(groupFactoryRegistry, Registry, Scheme)
// we need to add the options (like ListOptions) to empty v1
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})
@ -181,13 +182,19 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg
serviceResolver: c.ExtraConfig.ServiceResolver,
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)
apiGroupInfo.GroupMeta.GroupVersion = v1beta1.SchemeGroupVersion
v1beta1storage := map[string]rest.Storage{}
apiServiceREST := apiservicestorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
v1beta1storage["apiservices"] = apiServiceREST
v1beta1storage["apiservices/status"] = apiservicestorage.NewStatusREST(Scheme, apiServiceREST)
apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = v1beta1storage
apiResourceConfig := c.GenericConfig.MergedResourceConfig
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, Registry, Scheme, metav1.ParameterCodec, Codecs)
if apiResourceConfig.AnyResourcesForVersionEnabled(v1beta1.SchemeGroupVersion) {
apiGroupInfo.GroupMeta.GroupVersion = v1beta1.SchemeGroupVersion
storage := map[string]rest.Storage{}
version := v1beta1.SchemeGroupVersion
if apiResourceConfig.ResourceEnabled(version.WithResource("customresourcedefinitions")) {
apiServiceREST := apiservicestorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
storage["apiservices"] = apiServiceREST
storage["apiservices/status"] = apiservicestorage.NewStatusREST(Scheme, apiServiceREST)
}
apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = storage
}
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
return nil, err
@ -330,3 +337,13 @@ func (s *APIAggregator) RemoveAPIService(apiServiceName string) {
// TODO unregister group level discovery when there are no more versions for the group
// We don't need this right away because the handler properly delegates when no versions are present
}
func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
ret := serverstorage.NewResourceConfig()
// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
ret.EnableVersions(
v1beta1.SchemeGroupVersion,
)
return ret
}

View File

@ -37,6 +37,7 @@ const defaultEtcdPathPrefix = "/registry/kube-aggregator.kubernetes.io/"
type AggregatorOptions struct {
RecommendedOptions *genericoptions.RecommendedOptions
APIEnablement *genericoptions.APIEnablementOptions
// ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
// this to confirm the proxy's identity
@ -75,6 +76,7 @@ func NewCommandStartAggregator(out, err io.Writer, stopCh <-chan struct{}) *cobr
// AddFlags is necessary because hyperkube doesn't work using cobra, so we have to have different registration and execution paths
func (o *AggregatorOptions) AddFlags(fs *pflag.FlagSet) {
o.RecommendedOptions.AddFlags(fs)
o.APIEnablement.AddFlags(fs)
fs.StringVar(&o.ProxyClientCertFile, "proxy-client-cert-file", o.ProxyClientCertFile, "client certificate used identify the proxy to the API server")
fs.StringVar(&o.ProxyClientKeyFile, "proxy-client-key-file", o.ProxyClientKeyFile, "client certificate key used identify the proxy to the API server")
}
@ -83,6 +85,7 @@ func (o *AggregatorOptions) AddFlags(fs *pflag.FlagSet) {
func NewDefaultOptions(out, err io.Writer) *AggregatorOptions {
o := &AggregatorOptions{
RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion)),
APIEnablement: genericoptions.NewAPIEnablementOptions(),
StdOut: out,
StdErr: err,
@ -94,6 +97,7 @@ func NewDefaultOptions(out, err io.Writer) *AggregatorOptions {
func (o AggregatorOptions) Validate(args []string) error {
errors := []error{}
errors = append(errors, o.RecommendedOptions.Validate()...)
errors = append(errors, o.APIEnablement.Validate(apiserver.Registry)...)
return utilerrors.NewAggregate(errors)
}
@ -112,6 +116,9 @@ func (o AggregatorOptions) RunAggregator(stopCh <-chan struct{}) error {
if err := o.RecommendedOptions.ApplyTo(serverConfig, apiserver.Scheme); err != nil {
return err
}
if err := o.APIEnablement.ApplyTo(&serverConfig.Config, apiserver.DefaultAPIResourceConfigSource(), apiserver.Registry); err != nil {
return err
}
serverConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck(
sets.NewString("watch", "proxy"),
sets.NewString("attach", "exec", "proxy", "log", "portforward"),