mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
pass APIEnablement through apiserver chain
This commit is contained in:
parent
b7b5bbb20f
commit
2f403b7ad1
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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.")
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
//===========================================================================
|
||||
|
@ -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
|
||||
}
|
@ -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"
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"),
|
||||
|
Loading…
Reference in New Issue
Block a user