mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
Merge pull request #24787 from nikhiljindal/storageVersion
Automatic merge from submit-queue Moving StorageFactory building logic to genericapiserver Adding a DefaultStorageFactoryBuilder which builds the required StorageFactory. This allows us to remove the duplicated code between `cmd/kube-apiserver` and `federation/cmd/federated-apiserver` cc @deads2k @lavalamp @jianhuiz
This commit is contained in:
commit
bbdbfc8940
@ -23,10 +23,7 @@ import (
|
||||
|
||||
"k8s.io/kubernetes/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
apiutil "k8s.io/kubernetes/pkg/api/util"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
@ -46,7 +43,6 @@ type APIServer struct {
|
||||
BasicAuthFile string
|
||||
DefaultStorageMediaType string
|
||||
DeleteCollectionWorkers int
|
||||
DeprecatedStorageVersion string
|
||||
EtcdServersOverrides []string
|
||||
EventTTL time.Duration
|
||||
KeystoneURL string
|
||||
@ -62,13 +58,8 @@ type APIServer struct {
|
||||
SSHUser string
|
||||
ServiceAccountKeyFile string
|
||||
ServiceAccountLookup bool
|
||||
StorageVersions string
|
||||
// The default values for StorageVersions. StorageVersions overrides
|
||||
// these; you can change this if you want to change the defaults (e.g.,
|
||||
// for testing). This is not actually exposed as a flag.
|
||||
DefaultStorageVersions string
|
||||
TokenAuthFile string
|
||||
WatchCacheSizes []string
|
||||
TokenAuthFile string
|
||||
WatchCacheSizes []string
|
||||
}
|
||||
|
||||
// NewAPIServer creates a new APIServer object with default parameters
|
||||
@ -81,8 +72,6 @@ func NewAPIServer() *APIServer {
|
||||
DeleteCollectionWorkers: 1,
|
||||
EventTTL: 1 * time.Hour,
|
||||
MasterServiceNamespace: api.NamespaceDefault,
|
||||
StorageVersions: registered.AllPreferredGroupVersions(),
|
||||
DefaultStorageVersions: registered.AllPreferredGroupVersions(),
|
||||
KubeletConfig: kubeletclient.KubeletClientConfig{
|
||||
Port: ports.KubeletPort,
|
||||
EnableHttps: true,
|
||||
@ -92,69 +81,12 @@ func NewAPIServer() *APIServer {
|
||||
return &s
|
||||
}
|
||||
|
||||
// dest must be a map of group to groupVersion.
|
||||
func mergeGroupVersionIntoMap(gvList string, dest map[string]unversioned.GroupVersion) error {
|
||||
for _, gvString := range strings.Split(gvList, ",") {
|
||||
if gvString == "" {
|
||||
continue
|
||||
}
|
||||
// We accept two formats. "group/version" OR
|
||||
// "group=group/version". The latter is used when types
|
||||
// move between groups.
|
||||
if !strings.Contains(gvString, "=") {
|
||||
gv, err := unversioned.ParseGroupVersion(gvString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest[gv.Group] = gv
|
||||
|
||||
} else {
|
||||
parts := strings.SplitN(gvString, "=", 2)
|
||||
gv, err := unversioned.ParseGroupVersion(parts[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest[parts[0]] = gv
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StorageGroupsToEncodingVersion returns a map from group name to group version,
|
||||
// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
|
||||
// TODO: can we move the whole storage version concept to the generic apiserver?
|
||||
func (s *APIServer) StorageGroupsToEncodingVersion() (map[string]unversioned.GroupVersion, error) {
|
||||
storageVersionMap := map[string]unversioned.GroupVersion{}
|
||||
if s.DeprecatedStorageVersion != "" {
|
||||
storageVersionMap[""] = unversioned.GroupVersion{Group: apiutil.GetGroup(s.DeprecatedStorageVersion), Version: apiutil.GetVersion(s.DeprecatedStorageVersion)}
|
||||
}
|
||||
|
||||
// First, get the defaults.
|
||||
if err := mergeGroupVersionIntoMap(s.DefaultStorageVersions, storageVersionMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Override any defaults with the user settings.
|
||||
if err := mergeGroupVersionIntoMap(s.StorageVersions, storageVersionMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storageVersionMap, nil
|
||||
}
|
||||
|
||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
// Add the generic flags.
|
||||
s.ServerRunOptions.AddFlags(fs)
|
||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||
// arrange these text blocks sensibly. Grrr.
|
||||
fs.StringVar(&s.DeprecatedStorageVersion, "storage-version", s.DeprecatedStorageVersion, "The version to store the legacy v1 resources with. Defaults to server preferred")
|
||||
fs.MarkDeprecated("storage-version", "--storage-version is deprecated and will be removed when the v1 API is retired. See --storage-versions instead.")
|
||||
fs.StringVar(&s.StorageVersions, "storage-versions", s.StorageVersions, "The per-group version to store resources in. "+
|
||||
"Specified in the format \"group1/version1,group2/version2,...\". "+
|
||||
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
||||
"You only need to pass the groups you wish to change from the defaults. "+
|
||||
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
||||
fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, "The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.")
|
||||
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default 1 hour.")
|
||||
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
||||
|
@ -17,72 +17,11 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
func TestGenerateStorageVersionMap(t *testing.T) {
|
||||
testCases := []struct {
|
||||
legacyVersion string
|
||||
storageVersions string
|
||||
defaultVersions string
|
||||
expectedMap map[string]unversioned.GroupVersion
|
||||
}{
|
||||
{
|
||||
legacyVersion: "v1",
|
||||
storageVersions: "v1,extensions/v1beta1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "extensions/v1beta1,v1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "autoscaling=extensions/v1beta1,v1",
|
||||
defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
autoscaling.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "",
|
||||
expectedMap: map[string]unversioned.GroupVersion{},
|
||||
},
|
||||
}
|
||||
for i, test := range testCases {
|
||||
s := APIServer{
|
||||
DeprecatedStorageVersion: test.legacyVersion,
|
||||
StorageVersions: test.storageVersions,
|
||||
DefaultStorageVersions: test.defaultVersions,
|
||||
}
|
||||
output, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectedMap, output) {
|
||||
t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddFlagsFlag(t *testing.T) {
|
||||
// TODO: This only tests the enable-swagger-ui flag for now.
|
||||
// Expand the test to include other flags as well.
|
||||
|
@ -21,7 +21,6 @@ package app
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@ -35,26 +34,18 @@ import (
|
||||
"k8s.io/kubernetes/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
||||
appsapi "k8s.io/kubernetes/pkg/apis/apps/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
"k8s.io/kubernetes/pkg/apiserver/authenticator"
|
||||
"k8s.io/kubernetes/pkg/capabilities"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||
)
|
||||
|
||||
@ -132,44 +123,17 @@ func Run(s *options.APIServer) error {
|
||||
glog.Fatalf("Failure to start kubelet client: %v", err)
|
||||
}
|
||||
|
||||
apiResourceConfigSource, err := parseRuntimeConfig(s)
|
||||
storageGroupsToEncodingVersion, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
glog.Fatalf("error in parsing runtime-config: %s", err)
|
||||
glog.Fatalf("error generating storage version map: %s", err)
|
||||
}
|
||||
|
||||
clientConfig := &restclient.Config{
|
||||
Host: net.JoinHostPort(s.InsecureBindAddress.String(), strconv.Itoa(s.InsecurePort)),
|
||||
// Increase QPS limits. The client is currently passed to all admission plugins,
|
||||
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
|
||||
// for more details. Once #22422 is fixed, we may want to remove it.
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
}
|
||||
if len(s.DeprecatedStorageVersion) != 0 {
|
||||
gv, err := unversioned.ParseGroupVersion(s.DeprecatedStorageVersion)
|
||||
if err != nil {
|
||||
glog.Fatalf("error in parsing group version: %s", err)
|
||||
}
|
||||
clientConfig.GroupVersion = &gv
|
||||
}
|
||||
|
||||
client, err := clientset.NewForConfig(clientConfig)
|
||||
storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
|
||||
s.StorageConfig, s.DefaultStorageMediaType, api.Codecs,
|
||||
genericapiserver.NewDefaultResourceEncodingConfig(), storageGroupsToEncodingVersion,
|
||||
master.DefaultAPIResourceConfigSource(), s.RuntimeConfig)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create clientset: %v", err)
|
||||
glog.Fatalf("error in initializing storage factory: %s", err)
|
||||
}
|
||||
|
||||
resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
|
||||
groupToEncoding, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
glog.Fatalf("error getting group encoding: %s", err)
|
||||
}
|
||||
for group, storageEncodingVersion := range groupToEncoding {
|
||||
resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
||||
}
|
||||
|
||||
storageFactory := genericapiserver.NewDefaultStorageFactory(s.StorageConfig, s.DefaultStorageMediaType, api.Codecs, resourceEncoding, apiResourceConfigSource)
|
||||
// third party resources are always serialized to storage using JSON
|
||||
storageFactory.SetSerializer(extensions.Resource("thirdpartyresources"), "application/json", nil)
|
||||
storageFactory.AddCohabitatingResources(batch.Resource("jobs"), extensions.Resource("jobs"))
|
||||
storageFactory.AddCohabitatingResources(autoscaling.Resource("horizontalpodautoscalers"), extensions.Resource("horizontalpodautoscalers"))
|
||||
for _, override := range s.EtcdServersOverrides {
|
||||
@ -238,6 +202,10 @@ func Run(s *options.APIServer) error {
|
||||
}
|
||||
|
||||
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
|
||||
client, err := s.NewSelfClient()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create clientset: %v", err)
|
||||
}
|
||||
admissionController := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile)
|
||||
|
||||
genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
|
||||
@ -247,7 +215,7 @@ func Run(s *options.APIServer) error {
|
||||
genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
|
||||
genericConfig.Authorizer = authorizer
|
||||
genericConfig.AdmissionControl = admissionController
|
||||
genericConfig.APIResourceConfigSource = apiResourceConfigSource
|
||||
genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
|
||||
genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
|
||||
genericConfig.ProxyDialer = proxyDialerFn
|
||||
genericConfig.ProxyTLSClientConfig = proxyTLSClientConfig
|
||||
@ -275,85 +243,3 @@ func Run(s *options.APIServer) error {
|
||||
m.Run(s.ServerRunOptions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRuntimeConfigValue(s *options.APIServer, apiKey string, defaultValue bool) bool {
|
||||
flagValue, ok := s.RuntimeConfig[apiKey]
|
||||
if ok {
|
||||
if flagValue == "" {
|
||||
return true
|
||||
}
|
||||
boolValue, err := strconv.ParseBool(flagValue)
|
||||
if err != nil {
|
||||
glog.Fatalf("Invalid value of %s: %s, err: %v", apiKey, flagValue, err)
|
||||
}
|
||||
return boolValue
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Parses the given runtime-config and formats it into genericapiserver.APIResourceConfigSource
|
||||
func parseRuntimeConfig(s *options.APIServer) (genericapiserver.APIResourceConfigSource, error) {
|
||||
v1GroupVersionString := "api/v1"
|
||||
extensionsGroupVersionString := extensionsapiv1beta1.SchemeGroupVersion.String()
|
||||
versionToResourceSpecifier := map[unversioned.GroupVersion]string{
|
||||
apiv1.SchemeGroupVersion: v1GroupVersionString,
|
||||
extensionsapiv1beta1.SchemeGroupVersion: extensionsGroupVersionString,
|
||||
batchapiv1.SchemeGroupVersion: batchapiv1.SchemeGroupVersion.String(),
|
||||
autoscalingapiv1.SchemeGroupVersion: autoscalingapiv1.SchemeGroupVersion.String(),
|
||||
appsapi.SchemeGroupVersion: appsapi.SchemeGroupVersion.String(),
|
||||
}
|
||||
|
||||
resourceConfig := master.DefaultAPIResourceConfigSource()
|
||||
|
||||
// "api/all=false" allows users to selectively enable specific api versions.
|
||||
enableAPIByDefault := true
|
||||
allAPIFlagValue, ok := s.RuntimeConfig["api/all"]
|
||||
if ok && allAPIFlagValue == "false" {
|
||||
enableAPIByDefault = false
|
||||
}
|
||||
|
||||
// "api/legacy=false" allows users to disable legacy api versions.
|
||||
disableLegacyAPIs := false
|
||||
legacyAPIFlagValue, ok := s.RuntimeConfig["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.
|
||||
for version, resourceSpecifier := range versionToResourceSpecifier {
|
||||
enableVersion := getRuntimeConfigValue(s, resourceSpecifier, enableAPIByDefault)
|
||||
if enableVersion {
|
||||
resourceConfig.EnableVersions(version)
|
||||
} else {
|
||||
resourceConfig.DisableVersions(version)
|
||||
}
|
||||
}
|
||||
|
||||
for key := range s.RuntimeConfig {
|
||||
tokens := strings.Split(key, "/")
|
||||
if len(tokens) != 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(key, extensionsGroupVersionString+"/"):
|
||||
if !resourceConfig.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
|
||||
return nil, fmt.Errorf("%v is disabled, you cannot configure its resources individually", extensionsapiv1beta1.SchemeGroupVersion)
|
||||
}
|
||||
|
||||
resource := strings.TrimPrefix(key, extensionsGroupVersionString+"/")
|
||||
if getRuntimeConfigValue(s, key, false) {
|
||||
resourceConfig.EnableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource(resource))
|
||||
} else {
|
||||
resourceConfig.DisableResources(extensionsapiv1beta1.SchemeGroupVersion.WithResource(resource))
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO enable individual resource capability for all GroupVersionResources
|
||||
return nil, fmt.Errorf("%v resources cannot be enabled/disabled individually", key)
|
||||
}
|
||||
}
|
||||
return resourceConfig, nil
|
||||
}
|
||||
|
@ -17,14 +17,10 @@ limitations under the License.
|
||||
package app
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
)
|
||||
|
||||
func TestLongRunningRequestRegexp(t *testing.T) {
|
||||
@ -67,99 +63,3 @@ func TestLongRunningRequestRegexp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRuntimeConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
runtimeConfig map[string]string
|
||||
expectedAPIConfig func() *genericapiserver.ResourceConfig
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
runtimeConfig: map[string]string{},
|
||||
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
|
||||
return master.DefaultAPIResourceConfigSource()
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// Cannot override v1 resources.
|
||||
runtimeConfig: map[string]string{
|
||||
"api/v1/pods": "false",
|
||||
},
|
||||
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
|
||||
return master.DefaultAPIResourceConfigSource()
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
// Disable v1.
|
||||
runtimeConfig: map[string]string{
|
||||
"api/v1": "false",
|
||||
},
|
||||
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
|
||||
config := master.DefaultAPIResourceConfigSource()
|
||||
config.DisableVersions(unversioned.GroupVersion{Group: "", Version: "v1"})
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// Disable extensions.
|
||||
runtimeConfig: map[string]string{
|
||||
"extensions/v1beta1": "false",
|
||||
},
|
||||
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
|
||||
config := master.DefaultAPIResourceConfigSource()
|
||||
config.DisableVersions(unversioned.GroupVersion{Group: "extensions", Version: "v1beta1"})
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// Disable deployments.
|
||||
runtimeConfig: map[string]string{
|
||||
"extensions/v1beta1/deployments": "false",
|
||||
},
|
||||
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
|
||||
config := master.DefaultAPIResourceConfigSource()
|
||||
config.DisableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"})
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// Enable deployments and disable jobs.
|
||||
runtimeConfig: map[string]string{
|
||||
"extensions/v1beta1/anything": "true",
|
||||
"extensions/v1beta1/jobs": "false",
|
||||
},
|
||||
expectedAPIConfig: func() *genericapiserver.ResourceConfig {
|
||||
config := master.DefaultAPIResourceConfigSource()
|
||||
config.DisableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "jobs"})
|
||||
config.EnableResources(unversioned.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "anything"})
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
s := &options.APIServer{
|
||||
ServerRunOptions: &genericapiserver.ServerRunOptions{
|
||||
RuntimeConfig: test.runtimeConfig,
|
||||
},
|
||||
}
|
||||
actualDisablers, err := parseRuntimeConfig(s)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ kube-apiserver
|
||||
--watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled.
|
||||
```
|
||||
|
||||
###### Auto generated by spf13/cobra on 8-May-2016
|
||||
###### Auto generated by spf13/cobra on 9-May-2016
|
||||
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
|
@ -23,8 +23,6 @@ import (
|
||||
|
||||
"k8s.io/kubernetes/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
apiutil "k8s.io/kubernetes/pkg/api/util"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
@ -92,69 +90,12 @@ func NewAPIServer() *APIServer {
|
||||
return &s
|
||||
}
|
||||
|
||||
// dest must be a map of group to groupVersion.
|
||||
func mergeGroupVersionIntoMap(gvList string, dest map[string]unversioned.GroupVersion) error {
|
||||
for _, gvString := range strings.Split(gvList, ",") {
|
||||
if gvString == "" {
|
||||
continue
|
||||
}
|
||||
// We accept two formats. "group/version" OR
|
||||
// "group=group/version". The latter is used when types
|
||||
// move between groups.
|
||||
if !strings.Contains(gvString, "=") {
|
||||
gv, err := unversioned.ParseGroupVersion(gvString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest[gv.Group] = gv
|
||||
|
||||
} else {
|
||||
parts := strings.SplitN(gvString, "=", 2)
|
||||
gv, err := unversioned.ParseGroupVersion(parts[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest[parts[0]] = gv
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StorageGroupsToEncodingVersion returns a map from group name to group version,
|
||||
// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
|
||||
// TODO: can we move the whole storage version concept to the generic apiserver?
|
||||
func (s *APIServer) StorageGroupsToEncodingVersion() (map[string]unversioned.GroupVersion, error) {
|
||||
storageVersionMap := map[string]unversioned.GroupVersion{}
|
||||
if s.DeprecatedStorageVersion != "" {
|
||||
storageVersionMap[""] = unversioned.GroupVersion{Group: apiutil.GetGroup(s.DeprecatedStorageVersion), Version: apiutil.GetVersion(s.DeprecatedStorageVersion)}
|
||||
}
|
||||
|
||||
// First, get the defaults.
|
||||
if err := mergeGroupVersionIntoMap(s.DefaultStorageVersions, storageVersionMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Override any defaults with the user settings.
|
||||
if err := mergeGroupVersionIntoMap(s.StorageVersions, storageVersionMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storageVersionMap, nil
|
||||
}
|
||||
|
||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
// Add the generic flags.
|
||||
s.ServerRunOptions.AddFlags(fs)
|
||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||
// arrange these text blocks sensibly. Grrr.
|
||||
fs.StringVar(&s.DeprecatedStorageVersion, "storage-version", s.DeprecatedStorageVersion, "The version to store the legacy v1 resources with. Defaults to server preferred")
|
||||
fs.MarkDeprecated("storage-version", "--storage-version is deprecated and will be removed when the v1 API is retired. See --storage-versions instead.")
|
||||
fs.StringVar(&s.StorageVersions, "storage-versions", s.StorageVersions, "The per-group version to store resources in. "+
|
||||
"Specified in the format \"group1/version1,group2/version2,...\". "+
|
||||
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
||||
"You only need to pass the groups you wish to change from the defaults. "+
|
||||
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
||||
fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, "The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.")
|
||||
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default 1 hour.")
|
||||
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
||||
|
@ -17,72 +17,11 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
func TestGenerateStorageVersionMap(t *testing.T) {
|
||||
testCases := []struct {
|
||||
legacyVersion string
|
||||
storageVersions string
|
||||
defaultVersions string
|
||||
expectedMap map[string]unversioned.GroupVersion
|
||||
}{
|
||||
{
|
||||
legacyVersion: "v1",
|
||||
storageVersions: "v1,extensions/v1beta1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "extensions/v1beta1,v1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "autoscaling=extensions/v1beta1,v1",
|
||||
defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
autoscaling.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "",
|
||||
expectedMap: map[string]unversioned.GroupVersion{},
|
||||
},
|
||||
}
|
||||
for i, test := range testCases {
|
||||
s := APIServer{
|
||||
DeprecatedStorageVersion: test.legacyVersion,
|
||||
StorageVersions: test.storageVersions,
|
||||
DefaultStorageVersions: test.defaultVersions,
|
||||
}
|
||||
output, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectedMap, output) {
|
||||
t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddFlagsFlag(t *testing.T) {
|
||||
// TODO: This only tests the enable-swagger-ui flag for now.
|
||||
// Expand the test to include other flags as well.
|
||||
|
@ -20,8 +20,6 @@ limitations under the License.
|
||||
package app
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -34,11 +32,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
"k8s.io/kubernetes/pkg/apiserver/authenticator"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// NewAPIServerCommand creates a *cobra.Command object with default parameters
|
||||
@ -62,21 +57,21 @@ cluster's shared state through which all other components interact.`,
|
||||
func Run(s *options.APIServer) error {
|
||||
genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions)
|
||||
|
||||
apiResourceConfigSource, err := parseRuntimeConfig(s)
|
||||
// TODO: register cluster federation resources here.
|
||||
resourceConfig := genericapiserver.NewResourceConfig()
|
||||
|
||||
storageGroupsToEncodingVersion, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
glog.Fatalf("error in parsing runtime-config: %s", err)
|
||||
glog.Fatalf("error generating storage version map: %s", err)
|
||||
}
|
||||
storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
|
||||
s.StorageConfig, s.DefaultStorageMediaType, api.Codecs,
|
||||
genericapiserver.NewDefaultResourceEncodingConfig(), storageGroupsToEncodingVersion,
|
||||
resourceConfig, s.RuntimeConfig)
|
||||
if err != nil {
|
||||
glog.Fatalf("error in initializing storage factory: %s", err)
|
||||
}
|
||||
|
||||
resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
|
||||
groupToEncoding, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
glog.Fatalf("error getting group encoding: %s", err)
|
||||
}
|
||||
for group, storageEncodingVersion := range groupToEncoding {
|
||||
resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
||||
}
|
||||
|
||||
storageFactory := genericapiserver.NewDefaultStorageFactory(s.StorageConfig, s.DefaultStorageMediaType, api.Codecs, resourceEncoding, apiResourceConfigSource)
|
||||
for _, override := range s.EtcdServersOverrides {
|
||||
tokens := strings.Split(override, "#")
|
||||
if len(tokens) != 2 {
|
||||
@ -119,27 +114,10 @@ func Run(s *options.APIServer) error {
|
||||
}
|
||||
|
||||
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
|
||||
clientConfig := &restclient.Config{
|
||||
Host: net.JoinHostPort(s.InsecureBindAddress.String(), strconv.Itoa(s.InsecurePort)),
|
||||
// Increase QPS limits. The client is currently passed to all admission plugins,
|
||||
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
|
||||
// for more details. Once #22422 is fixed, we may want to remove it.
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
}
|
||||
if len(s.DeprecatedStorageVersion) != 0 {
|
||||
gv, err := unversioned.ParseGroupVersion(s.DeprecatedStorageVersion)
|
||||
if err != nil {
|
||||
glog.Fatalf("error in parsing group version: %s", err)
|
||||
}
|
||||
clientConfig.GroupVersion = &gv
|
||||
}
|
||||
|
||||
client, err := clientset.NewForConfig(clientConfig)
|
||||
client, err := s.NewSelfClient()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create clientset: %v", err)
|
||||
}
|
||||
|
||||
admissionController := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile)
|
||||
|
||||
genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
|
||||
@ -149,7 +127,7 @@ func Run(s *options.APIServer) error {
|
||||
genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
|
||||
genericConfig.Authorizer = authorizer
|
||||
genericConfig.AdmissionControl = admissionController
|
||||
genericConfig.APIResourceConfigSource = apiResourceConfigSource
|
||||
genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
|
||||
genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
|
||||
genericConfig.Serializer = api.Codecs
|
||||
|
||||
@ -168,25 +146,3 @@ func Run(s *options.APIServer) error {
|
||||
m.Run(s.ServerRunOptions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRuntimeConfigValue(s *options.APIServer, apiKey string, defaultValue bool) bool {
|
||||
flagValue, ok := s.RuntimeConfig[apiKey]
|
||||
if ok {
|
||||
if flagValue == "" {
|
||||
return true
|
||||
}
|
||||
boolValue, err := strconv.ParseBool(flagValue)
|
||||
if err != nil {
|
||||
glog.Fatalf("Invalid value of %s: %s, err: %v", apiKey, flagValue, err)
|
||||
}
|
||||
return boolValue
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Parses the given runtime-config and formats it into genericapiserver.APIResourceConfigSource
|
||||
func parseRuntimeConfig(s *options.APIServer) (genericapiserver.APIResourceConfigSource, error) {
|
||||
// TODO: parse the relevant group version when we add any.
|
||||
resourceConfig := genericapiserver.NewResourceConfig()
|
||||
return resourceConfig, nil
|
||||
}
|
||||
|
@ -168,6 +168,15 @@ func IsRegisteredVersion(v unversioned.GroupVersion) bool {
|
||||
return found
|
||||
}
|
||||
|
||||
// RegisteredGroupVersions returns all registered group versions.
|
||||
func RegisteredGroupVersions() []unversioned.GroupVersion {
|
||||
ret := []unversioned.GroupVersion{}
|
||||
for groupVersion := range registeredVersions {
|
||||
ret = append(ret, groupVersion)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// IsThirdPartyAPIGroupVersion returns true if the api version is a user-registered group/version.
|
||||
func IsThirdPartyAPIGroupVersion(gv unversioned.GroupVersion) bool {
|
||||
for ix := range thirdPartyGroupVersions {
|
||||
|
167
pkg/genericapiserver/default_storage_factory_builder.go
Normal file
167
pkg/genericapiserver/default_storage_factory_builder.go
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package genericapiserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
||||
"k8s.io/kubernetes/pkg/util/config"
|
||||
)
|
||||
|
||||
// Builds the DefaultStorageFactory.
|
||||
// Merges defaultResourceConfig with the user specified overrides and merges
|
||||
// defaultAPIResourceConfig with the corresponding user specified overrides as well.
|
||||
func BuildDefaultStorageFactory(storageConfig storagebackend.Config, defaultMediaType string, serializer runtime.StorageSerializer,
|
||||
defaultResourceEncoding *DefaultResourceEncodingConfig, storageEncodingOverrides map[string]unversioned.GroupVersion, defaultAPIResourceConfig *ResourceConfig, resourceConfigOverrides config.ConfigurationMap) (*DefaultStorageFactory, error) {
|
||||
|
||||
resourceEncodingConfig := mergeResourceEncodingConfigs(defaultResourceEncoding, storageEncodingOverrides)
|
||||
apiResourceConfig, err := mergeAPIResourceConfigs(defaultAPIResourceConfig, resourceConfigOverrides)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewDefaultStorageFactory(storageConfig, defaultMediaType, serializer, resourceEncodingConfig, apiResourceConfig), nil
|
||||
}
|
||||
|
||||
// Merges the given defaultAPIResourceConfig with the given storageEncodingOverrides.
|
||||
func mergeResourceEncodingConfigs(defaultResourceEncoding *DefaultResourceEncodingConfig, storageEncodingOverrides map[string]unversioned.GroupVersion) *DefaultResourceEncodingConfig {
|
||||
resourceEncodingConfig := defaultResourceEncoding
|
||||
for group, storageEncodingVersion := range storageEncodingOverrides {
|
||||
resourceEncodingConfig.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
|
||||
}
|
||||
return resourceEncodingConfig
|
||||
}
|
||||
|
||||
// Merges the given defaultAPIResourceConfig with the given resourceConfigOverrides.
|
||||
func mergeAPIResourceConfigs(defaultAPIResourceConfig *ResourceConfig, resourceConfigOverrides config.ConfigurationMap) (*ResourceConfig, error) {
|
||||
resourceConfig := defaultAPIResourceConfig
|
||||
overrides := resourceConfigOverrides
|
||||
|
||||
// "api/all=false" allows users to selectively enable specific api versions.
|
||||
allAPIFlagValue, ok := overrides["api/all"]
|
||||
if ok && allAPIFlagValue == "false" {
|
||||
// Disable all group versions.
|
||||
for _, groupVersion := range registered.RegisteredGroupVersions() {
|
||||
if resourceConfig.AnyResourcesForVersionEnabled(groupVersion) {
|
||||
resourceConfig.DisableVersions(groupVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "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 := unversioned.ParseGroupVersion(groupVersionString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid key %s", key)
|
||||
}
|
||||
// Verify that the groupVersion is registered.
|
||||
if !registered.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 := unversioned.ParseGroupVersion(groupVersionString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid key %s", key)
|
||||
}
|
||||
resource := tokens[2]
|
||||
// Verify that the groupVersion is registered.
|
||||
if !registered.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 config.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
|
||||
}
|
177
pkg/genericapiserver/default_storage_factory_builder_test.go
Normal file
177
pkg/genericapiserver/default_storage_factory_builder_test.go
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package genericapiserver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
||||
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
)
|
||||
|
||||
func TestParseRuntimeConfig(t *testing.T) {
|
||||
extensionsGroupVersion := extensionsapiv1beta1.SchemeGroupVersion
|
||||
apiv1GroupVersion := apiv1.SchemeGroupVersion
|
||||
testCases := []struct {
|
||||
runtimeConfig map[string]string
|
||||
defaultResourceConfig func() *ResourceConfig
|
||||
expectedAPIConfig func() *ResourceConfig
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
// everything default value.
|
||||
runtimeConfig: map[string]string{},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// no runtimeConfig override.
|
||||
runtimeConfig: map[string]string{},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
|
||||
return config
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// version enabled by runtimeConfig override.
|
||||
runtimeConfig: map[string]string{
|
||||
"extensions/v1beta1": "",
|
||||
},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
|
||||
return config
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.EnableVersions(extensionsapiv1beta1.SchemeGroupVersion)
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// disable resource
|
||||
runtimeConfig: map[string]string{
|
||||
"api/v1/pods": "false",
|
||||
},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.EnableVersions(apiv1GroupVersion)
|
||||
return config
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.EnableVersions(apiv1GroupVersion)
|
||||
config.DisableResources(apiv1GroupVersion.WithResource("pods"))
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// Disable v1.
|
||||
runtimeConfig: map[string]string{
|
||||
"api/v1": "false",
|
||||
},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.DisableVersions(apiv1GroupVersion)
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// Enable deployments and disable jobs.
|
||||
runtimeConfig: map[string]string{
|
||||
"extensions/v1beta1/anything": "true",
|
||||
"extensions/v1beta1/jobs": "false",
|
||||
},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.EnableVersions(extensionsGroupVersion)
|
||||
return config
|
||||
},
|
||||
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.EnableVersions(extensionsGroupVersion)
|
||||
config.DisableResources(extensionsGroupVersion.WithResource("jobs"))
|
||||
config.EnableResources(extensionsGroupVersion.WithResource("anything"))
|
||||
return config
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
// invalid runtime config
|
||||
runtimeConfig: map[string]string{
|
||||
"invalidgroup/version": "false",
|
||||
},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
// cannot disable individual resource when version is not enabled.
|
||||
runtimeConfig: map[string]string{
|
||||
"api/v1/pods": "false",
|
||||
},
|
||||
defaultResourceConfig: func() *ResourceConfig {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
expectedAPIConfig: func() *ResourceConfig {
|
||||
config := NewResourceConfig()
|
||||
config.DisableResources(unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"})
|
||||
return config
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,11 +18,19 @@ package genericapiserver
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
apiutil "k8s.io/kubernetes/pkg/api/util"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
||||
"k8s.io/kubernetes/pkg/util/config"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
@ -33,15 +41,17 @@ const (
|
||||
|
||||
// ServerRunOptions contains the options while running a generic api server.
|
||||
type ServerRunOptions struct {
|
||||
APIGroupPrefix string
|
||||
APIPrefix string
|
||||
AdvertiseAddress net.IP
|
||||
BindAddress net.IP
|
||||
CertDirectory string
|
||||
ClientCAFile string
|
||||
CloudConfigFile string
|
||||
CloudProvider string
|
||||
CorsAllowedOriginList []string
|
||||
APIGroupPrefix string
|
||||
APIPrefix string
|
||||
AdvertiseAddress net.IP
|
||||
BindAddress net.IP
|
||||
CertDirectory string
|
||||
ClientCAFile string
|
||||
CloudConfigFile string
|
||||
CloudProvider string
|
||||
CorsAllowedOriginList []string
|
||||
// Used to specify the storage version that should be used for the legacy v1 api group.
|
||||
DeprecatedStorageVersion string
|
||||
EnableLogsSupport bool
|
||||
EnableProfiling bool
|
||||
EnableSwaggerUI bool
|
||||
@ -59,16 +69,22 @@ type ServerRunOptions struct {
|
||||
SecurePort int
|
||||
ServiceClusterIPRange net.IPNet // TODO: make this a list
|
||||
ServiceNodePortRange utilnet.PortRange
|
||||
TLSCertFile string
|
||||
TLSPrivateKeyFile string
|
||||
StorageVersions string
|
||||
// The default values for StorageVersions. StorageVersions overrides
|
||||
// these; you can change this if you want to change the defaults (e.g.,
|
||||
// for testing). This is not actually exposed as a flag.
|
||||
DefaultStorageVersions string
|
||||
TLSCertFile string
|
||||
TLSPrivateKeyFile string
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
return &ServerRunOptions{
|
||||
APIGroupPrefix: "/apis",
|
||||
APIPrefix: "/api",
|
||||
BindAddress: net.ParseIP("0.0.0.0"),
|
||||
CertDirectory: "/var/run/kubernetes",
|
||||
APIGroupPrefix: "/apis",
|
||||
APIPrefix: "/api",
|
||||
BindAddress: net.ParseIP("0.0.0.0"),
|
||||
CertDirectory: "/var/run/kubernetes",
|
||||
DefaultStorageVersions: registered.AllPreferredGroupVersions(),
|
||||
StorageConfig: storagebackend.Config{
|
||||
Prefix: DefaultEtcdPathPrefix,
|
||||
DeserializationCacheSize: DefaultDeserializationCacheSize,
|
||||
@ -84,9 +100,80 @@ func NewServerRunOptions() *ServerRunOptions {
|
||||
MinRequestTimeout: 1800,
|
||||
RuntimeConfig: make(config.ConfigurationMap),
|
||||
SecurePort: 6443,
|
||||
StorageVersions: registered.AllPreferredGroupVersions(),
|
||||
}
|
||||
}
|
||||
|
||||
// StorageGroupsToEncodingVersion returns a map from group name to group version,
|
||||
// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
|
||||
func (s *ServerRunOptions) StorageGroupsToEncodingVersion() (map[string]unversioned.GroupVersion, error) {
|
||||
storageVersionMap := map[string]unversioned.GroupVersion{}
|
||||
if s.DeprecatedStorageVersion != "" {
|
||||
storageVersionMap[""] = unversioned.GroupVersion{Group: apiutil.GetGroup(s.DeprecatedStorageVersion), Version: apiutil.GetVersion(s.DeprecatedStorageVersion)}
|
||||
}
|
||||
|
||||
// First, get the defaults.
|
||||
if err := mergeGroupVersionIntoMap(s.DefaultStorageVersions, storageVersionMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Override any defaults with the user settings.
|
||||
if err := mergeGroupVersionIntoMap(s.StorageVersions, storageVersionMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storageVersionMap, nil
|
||||
}
|
||||
|
||||
// dest must be a map of group to groupVersion.
|
||||
func mergeGroupVersionIntoMap(gvList string, dest map[string]unversioned.GroupVersion) error {
|
||||
for _, gvString := range strings.Split(gvList, ",") {
|
||||
if gvString == "" {
|
||||
continue
|
||||
}
|
||||
// We accept two formats. "group/version" OR
|
||||
// "group=group/version". The latter is used when types
|
||||
// move between groups.
|
||||
if !strings.Contains(gvString, "=") {
|
||||
gv, err := unversioned.ParseGroupVersion(gvString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest[gv.Group] = gv
|
||||
|
||||
} else {
|
||||
parts := strings.SplitN(gvString, "=", 2)
|
||||
gv, err := unversioned.ParseGroupVersion(parts[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest[parts[0]] = gv
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a clientset which can be used to talk to this apiserver.
|
||||
func (s *ServerRunOptions) NewSelfClient() (clientset.Interface, error) {
|
||||
clientConfig := &restclient.Config{
|
||||
Host: net.JoinHostPort(s.InsecureBindAddress.String(), strconv.Itoa(s.InsecurePort)),
|
||||
// Increase QPS limits. The client is currently passed to all admission plugins,
|
||||
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
|
||||
// for more details. Once #22422 is fixed, we may want to remove it.
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
}
|
||||
if len(s.DeprecatedStorageVersion) != 0 {
|
||||
gv, err := unversioned.ParseGroupVersion(s.DeprecatedStorageVersion)
|
||||
if err != nil {
|
||||
glog.Fatalf("error in parsing group version: %s", err)
|
||||
}
|
||||
clientConfig.GroupVersion = &gv
|
||||
}
|
||||
|
||||
return clientset.NewForConfig(clientConfig)
|
||||
}
|
||||
|
||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||
@ -171,6 +258,14 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.Var(&s.ServiceNodePortRange, "service-node-ports", "Deprecated: see --service-node-port-range instead.")
|
||||
fs.MarkDeprecated("service-node-ports", "see --service-node-port-range instead.")
|
||||
|
||||
fs.StringVar(&s.DeprecatedStorageVersion, "storage-version", s.DeprecatedStorageVersion, "The version to store the legacy v1 resources with. Defaults to server preferred")
|
||||
fs.MarkDeprecated("storage-version", "--storage-version is deprecated and will be removed when the v1 API is retired. See --storage-versions instead.")
|
||||
fs.StringVar(&s.StorageVersions, "storage-versions", s.StorageVersions, "The per-group version to store resources in. "+
|
||||
"Specified in the format \"group1/version1,group2/version2,...\". "+
|
||||
"In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+
|
||||
"You only need to pass the groups you wish to change from the defaults. "+
|
||||
"It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.")
|
||||
|
||||
fs.StringVar(&s.TLSCertFile, "tls-cert-file", s.TLSCertFile, ""+
|
||||
"File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). "+
|
||||
"If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, "+
|
||||
|
82
pkg/genericapiserver/server_run_options_test.go
Normal file
82
pkg/genericapiserver/server_run_options_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package genericapiserver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
func TestGenerateStorageVersionMap(t *testing.T) {
|
||||
testCases := []struct {
|
||||
legacyVersion string
|
||||
storageVersions string
|
||||
defaultVersions string
|
||||
expectedMap map[string]unversioned.GroupVersion
|
||||
}{
|
||||
{
|
||||
legacyVersion: "v1",
|
||||
storageVersions: "v1,extensions/v1beta1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "extensions/v1beta1,v1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "autoscaling=extensions/v1beta1,v1",
|
||||
defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
|
||||
expectedMap: map[string]unversioned.GroupVersion{
|
||||
api.GroupName: {Version: "v1"},
|
||||
autoscaling.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
legacyVersion: "",
|
||||
storageVersions: "",
|
||||
expectedMap: map[string]unversioned.GroupVersion{},
|
||||
},
|
||||
}
|
||||
for i, test := range testCases {
|
||||
s := ServerRunOptions{
|
||||
DeprecatedStorageVersion: test.legacyVersion,
|
||||
StorageVersions: test.storageVersions,
|
||||
DefaultStorageVersions: test.defaultVersions,
|
||||
}
|
||||
output, err := s.StorageGroupsToEncodingVersion()
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectedMap, output) {
|
||||
t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user