diff --git a/cmd/hyperkube/federation-apiserver.go b/cmd/hyperkube/federation-apiserver.go index d6583800911..31261097ceb 100644 --- a/cmd/hyperkube/federation-apiserver.go +++ b/cmd/hyperkube/federation-apiserver.go @@ -24,7 +24,7 @@ import ( // NewFederationAPIServer creates a new hyperkube Server object that includes the // description and flags. func NewFederationAPIServer() *Server { - s := genericoptions.NewServerRunOptions() + s := genericoptions.NewServerRunOptions().WithEtcdOptions() hks := Server{ SimpleUsage: "federation-apiserver", @@ -33,6 +33,7 @@ func NewFederationAPIServer() *Server { return app.Run(s) }, } - s.AddFlags(hks.Flags()) + s.AddUniversalFlags(hks.Flags()) + s.AddEtcdStorageFlags(hks.Flags()) return &hks } diff --git a/cmd/kube-apiserver/app/options/options.go b/cmd/kube-apiserver/app/options/options.go index 91fac22b180..224ba0ccb43 100644 --- a/cmd/kube-apiserver/app/options/options.go +++ b/cmd/kube-apiserver/app/options/options.go @@ -47,7 +47,7 @@ type APIServer struct { // NewAPIServer creates a new APIServer object with default parameters func NewAPIServer() *APIServer { s := APIServer{ - ServerRunOptions: genericoptions.NewServerRunOptions(), + ServerRunOptions: genericoptions.NewServerRunOptions().WithEtcdOptions(), EventTTL: 1 * time.Hour, KubeletConfig: kubeletclient.KubeletClientConfig{ Port: ports.KubeletPort, @@ -62,7 +62,9 @@ func NewAPIServer() *APIServer { // 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) + s.ServerRunOptions.AddUniversalFlags(fs) + //Add etcd specific flags. + s.ServerRunOptions.AddEtcdStorageFlags(fs) // Note: the weird ""+ in below lines seems to be the only way to get gofmt to // arrange these text blocks sensibly. Grrr. diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 2d7dabe034e..3669261ab60 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -46,6 +46,7 @@ import ( "k8s.io/kubernetes/pkg/controller/framework/informers" serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" "k8s.io/kubernetes/pkg/genericapiserver" + genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/registry/cachesize" @@ -81,6 +82,7 @@ cluster's shared state through which all other components interact.`, // Run runs the specified APIServer. This should never exit. func Run(s *options.APIServer) error { + genericvalidation.VerifyEtcdServersList(s.ServerRunOptions) genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions) capabilities.Initialize(capabilities.Capabilities{ diff --git a/examples/apiserver/apiserver.go b/examples/apiserver/apiserver.go index bc3e5ad231c..8b062371892 100644 --- a/examples/apiserver/apiserver.go +++ b/examples/apiserver/apiserver.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/genericapiserver" genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" + genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" "k8s.io/kubernetes/pkg/storage/storagebackend" // Install the testgroup API @@ -51,7 +52,7 @@ func newStorageFactory() genericapiserver.StorageFactory { } func NewServerRunOptions() *genericoptions.ServerRunOptions { - serverOptions := genericoptions.NewServerRunOptions() + serverOptions := genericoptions.NewServerRunOptions().WithEtcdOptions() serverOptions.InsecurePort = InsecurePort return serverOptions } @@ -61,7 +62,8 @@ func Run(serverOptions *genericoptions.ServerRunOptions) error { _, serviceClusterIPRange, _ := net.ParseCIDR("10.0.0.0/24") serverOptions.ServiceClusterIPRange = *serviceClusterIPRange serverOptions.StorageConfig.ServerList = []string{"http://127.0.0.1:4001"} - genericapiserver.ValidateRunOptions(serverOptions) + genericvalidation.ValidateRunOptions(serverOptions) + genericvalidation.VerifyEtcdServersList(serverOptions) config := genericapiserver.NewConfig(serverOptions) config.Serializer = api.Codecs s, err := genericapiserver.New(config) diff --git a/examples/apiserver/server/main.go b/examples/apiserver/server/main.go index dbed6e043c8..a145adba68b 100644 --- a/examples/apiserver/server/main.go +++ b/examples/apiserver/server/main.go @@ -28,7 +28,8 @@ func main() { serverRunOptions := apiserver.NewServerRunOptions() // Parse command line flags. - serverRunOptions.AddFlags(pflag.CommandLine) + serverRunOptions.AddUniversalFlags(pflag.CommandLine) + serverRunOptions.AddEtcdStorageFlags(pflag.CommandLine) flag.InitFlags() if err := apiserver.Run(serverRunOptions); err != nil { diff --git a/federation/cmd/federation-apiserver/apiserver.go b/federation/cmd/federation-apiserver/apiserver.go index 7a65dca8670..1b74cba34f1 100644 --- a/federation/cmd/federation-apiserver/apiserver.go +++ b/federation/cmd/federation-apiserver/apiserver.go @@ -36,8 +36,9 @@ import ( func main() { rand.Seed(time.Now().UTC().UnixNano()) - s := genericoptions.NewServerRunOptions() - s.AddFlags(pflag.CommandLine) + s := genericoptions.NewServerRunOptions().WithEtcdOptions() + s.AddUniversalFlags(pflag.CommandLine) + s.AddEtcdStorageFlags(pflag.CommandLine) flag.InitFlags() util.InitLogs() diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index cc6b80feb9c..bc0390cc72f 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -35,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/controller/framework/informers" "k8s.io/kubernetes/pkg/genericapiserver" genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" + genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" "k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/util/wait" @@ -42,8 +43,9 @@ import ( // NewAPIServerCommand creates a *cobra.Command object with default parameters func NewAPIServerCommand() *cobra.Command { - s := genericoptions.NewServerRunOptions() - s.AddFlags(pflag.CommandLine) + s := genericoptions.NewServerRunOptions().WithEtcdOptions() + s.AddUniversalFlags(pflag.CommandLine) + s.AddEtcdStorageFlags(pflag.CommandLine) cmd := &cobra.Command{ Use: "federation-apiserver", Long: `The Kubernetes federation API server validates and configures data @@ -59,6 +61,7 @@ cluster's shared state through which all other components interact.`, // Run runs the specified APIServer. This should never exit. func Run(s *genericoptions.ServerRunOptions) error { + genericvalidation.VerifyEtcdServersList(s) genericapiserver.DefaultAndValidateRunOptions(s) // TODO: register cluster federation resources here. diff --git a/federation/cmd/federation-apiserver/app/server_test.go b/federation/cmd/federation-apiserver/app/server_test.go index da3dfc0d486..719e6192fdd 100644 --- a/federation/cmd/federation-apiserver/app/server_test.go +++ b/federation/cmd/federation-apiserver/app/server_test.go @@ -36,7 +36,7 @@ import ( ) func TestLongRunningRequestRegexp(t *testing.T) { - regexp := regexp.MustCompile(options.NewServerRunOptions().LongRunningRequestRE) + regexp := regexp.MustCompile(options.NewServerRunOptions().WithEtcdOptions().LongRunningRequestRE) dontMatch := []string{ "/api/v1/watch-namespace/", "/api/v1/namespace-proxy/", @@ -84,7 +84,7 @@ var groupVersions = []unversioned.GroupVersion{ } func TestRun(t *testing.T) { - s := options.NewServerRunOptions() + s := options.NewServerRunOptions().WithEtcdOptions() s.InsecurePort = insecurePort _, ipNet, _ := net.ParseCIDR("10.10.10.0/24") s.ServiceClusterIPRange = *ipNet diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index e34a06ac82d..3693bbded9a 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -42,6 +42,7 @@ import ( "k8s.io/kubernetes/pkg/auth/handlers" "k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/genericapiserver/options" + genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/generic/registry" ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator" @@ -536,17 +537,6 @@ func (s *GenericAPIServer) installGroupsDiscoveryHandler() { }) } -// TODO: Longer term we should read this from some config store, rather than a flag. -func verifyClusterIPFlags(options *options.ServerRunOptions) { - if options.ServiceClusterIPRange.IP == nil { - glog.Fatal("No --service-cluster-ip-range specified") - } - var ones, bits = options.ServiceClusterIPRange.Mask.Size() - if bits-ones > 20 { - glog.Fatal("Specified --service-cluster-ip-range is too large") - } -} - func NewConfig(options *options.ServerRunOptions) *Config { return &Config{ APIGroupPrefix: options.APIGroupPrefix, @@ -570,46 +560,8 @@ func NewConfig(options *options.ServerRunOptions) *Config { } } -func verifyServiceNodePort(options *options.ServerRunOptions) { - if options.KubernetesServiceNodePort < 0 || options.KubernetesServiceNodePort > 65535 { - glog.Fatalf("--kubernetes-service-node-port %v must be between 0 and 65535, inclusive. If 0, the Kubernetes master service will be of type ClusterIP.", options.KubernetesServiceNodePort) - } - - if options.KubernetesServiceNodePort > 0 && !options.ServiceNodePortRange.Contains(options.KubernetesServiceNodePort) { - glog.Fatalf("Kubernetes service port range %v doesn't contain %v", options.ServiceNodePortRange, (options.KubernetesServiceNodePort)) - } -} - -func verifyEtcdServersList(options *options.ServerRunOptions) { - if len(options.StorageConfig.ServerList) == 0 { - glog.Fatalf("--etcd-servers must be specified") - } -} - -func verifySecureAndInsecurePort(options *options.ServerRunOptions) { - if options.SecurePort < 0 || options.SecurePort > 65535 { - glog.Fatalf("--secure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", options.SecurePort) - } - - // TODO: Allow 0 to turn off insecure port. - if options.InsecurePort < 1 || options.InsecurePort > 65535 { - glog.Fatalf("--insecure-port %v must be between 1 and 65535, inclusive.", options.InsecurePort) - } - - if options.SecurePort == options.InsecurePort { - glog.Fatalf("--secure-port and --insecure-port cannot use the same port.") - } -} - -func ValidateRunOptions(options *options.ServerRunOptions) { - verifyClusterIPFlags(options) - verifyServiceNodePort(options) - verifyEtcdServersList(options) - verifySecureAndInsecurePort(options) -} - func DefaultAndValidateRunOptions(options *options.ServerRunOptions) { - ValidateRunOptions(options) + genericvalidation.ValidateRunOptions(options) // If advertise-address is not specified, use bind-address. If bind-address // is not usable (unset, 0.0.0.0, or loopback), we will use the host's default diff --git a/pkg/genericapiserver/options/etcd_options.go b/pkg/genericapiserver/options/etcd_options.go new file mode 100644 index 00000000000..7a7fab76b9e --- /dev/null +++ b/pkg/genericapiserver/options/etcd_options.go @@ -0,0 +1,51 @@ +/* +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 options + +import ( + "github.com/spf13/pflag" +) + +const ( + DefaultEtcdPathPrefix = "/registry" +) + +// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet +func (s *ServerRunOptions) AddEtcdStorageFlags(fs *pflag.FlagSet) { + + fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+ + "Per-resource etcd servers overrides, comma separated. The individual override "+ + "format: group/resource#servers, where servers are http://ip:port, semicolon separated.") + + fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList, + "List of etcd servers to connect with (http://ip:port), comma separated.") + + fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix, + "The prefix for all resource paths in etcd.") + + fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile, + "SSL key file used to secure etcd communication.") + + fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile, + "SSL certification file used to secure etcd communication.") + + fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile, + "SSL Certificate Authority file used to secure etcd communication.") + + fs.BoolVar(&s.StorageConfig.Quorum, "etcd-quorum-read", s.StorageConfig.Quorum, + "If true, enable quorum read.") +} diff --git a/pkg/genericapiserver/options/server_run_options.go b/pkg/genericapiserver/options/server_run_options.go index 3f22fbe1133..84f43b65319 100644 --- a/pkg/genericapiserver/options/server_run_options.go +++ b/pkg/genericapiserver/options/server_run_options.go @@ -39,7 +39,6 @@ import ( ) const ( - DefaultEtcdPathPrefix = "/registry" DefaultDeserializationCacheSize = 50000 // TODO: This can be tightened up. It still matches objects named watch or proxy. @@ -119,10 +118,6 @@ func NewServerRunOptions() *ServerRunOptions { CertDirectory: "/var/run/kubernetes", DefaultStorageMediaType: "application/json", DefaultStorageVersions: registered.AllPreferredGroupVersions(), - StorageConfig: storagebackend.Config{ - Prefix: DefaultEtcdPathPrefix, - DeserializationCacheSize: DefaultDeserializationCacheSize, - }, DeleteCollectionWorkers: 1, EnableLogsSupport: true, EnableProfiling: true, @@ -141,6 +136,14 @@ func NewServerRunOptions() *ServerRunOptions { } } +func (o *ServerRunOptions) WithEtcdOptions() *ServerRunOptions { + o.StorageConfig = storagebackend.Config{ + Prefix: DefaultEtcdPathPrefix, + DeserializationCacheSize: DefaultDeserializationCacheSize, + } + return o +} + // 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) { @@ -212,7 +215,7 @@ func (s *ServerRunOptions) NewSelfClient() (clientset.Interface, error) { } // AddFlags adds flags for a specific APIServer to the specified FlagSet -func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { +func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) { // Note: the weird ""+ in below lines seems to be the only way to get gofmt to // arrange these text blocks sensibly. Grrr. @@ -301,10 +304,6 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { fs.BoolVar(&s.EnableWatchCache, "watch-cache", s.EnableWatchCache, "Enable watch caching in the apiserver") - fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+ - "Per-resource etcd servers overrides, comma separated. The individual override "+ - "format: group/resource#servers, where servers are http://ip:port, semicolon separated.") - fs.IntVar(&s.TargetRAMMB, "target-ram-mb", s.TargetRAMMB, "Memory limit for apiserver in MB (used to configure sizes of caches, etc.)") @@ -405,24 +404,6 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.StorageConfig.Type, "storage-backend", s.StorageConfig.Type, "The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.") - fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList, - "List of etcd servers to connect with (http://ip:port), comma separated.") - - fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix, - "The prefix for all resource paths in etcd.") - - fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile, - "SSL key file used to secure etcd communication.") - - fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile, - "SSL certification file used to secure etcd communication.") - - fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile, - "SSL Certificate Authority file used to secure etcd communication.") - - fs.BoolVar(&s.StorageConfig.Quorum, "etcd-quorum-read", s.StorageConfig.Quorum, - "If true, enable quorum read.") - fs.IntVar(&s.StorageConfig.DeserializationCacheSize, "deserialization-cache-size", s.StorageConfig.DeserializationCacheSize, "Number of deserialized json objects to cache in memory.") diff --git a/pkg/genericapiserver/validation/etcd_validation.go b/pkg/genericapiserver/validation/etcd_validation.go new file mode 100644 index 00000000000..54e4a4fdfb8 --- /dev/null +++ b/pkg/genericapiserver/validation/etcd_validation.go @@ -0,0 +1,28 @@ +/* +Copyright 2014 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 validation + +import ( + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/genericapiserver/options" +) + +func VerifyEtcdServersList(options *options.ServerRunOptions) { + if len(options.StorageConfig.ServerList) == 0 { + glog.Fatalf("--etcd-servers must be specified") + } +} diff --git a/pkg/genericapiserver/validation/universal_validation.go b/pkg/genericapiserver/validation/universal_validation.go new file mode 100644 index 00000000000..f74cd0751cd --- /dev/null +++ b/pkg/genericapiserver/validation/universal_validation.go @@ -0,0 +1,64 @@ +/* +Copyright 2014 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 validation + +import ( + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/genericapiserver/options" +) + +// TODO: Longer term we should read this from some config store, rather than a flag. +func verifyClusterIPFlags(options *options.ServerRunOptions) { + if options.ServiceClusterIPRange.IP == nil { + glog.Fatal("No --service-cluster-ip-range specified") + } + var ones, bits = options.ServiceClusterIPRange.Mask.Size() + if bits-ones > 20 { + glog.Fatal("Specified --service-cluster-ip-range is too large") + } +} + +func verifyServiceNodePort(options *options.ServerRunOptions) { + if options.KubernetesServiceNodePort < 0 || options.KubernetesServiceNodePort > 65535 { + glog.Fatalf("--kubernetes-service-node-port %v must be between 0 and 65535, inclusive. If 0, the Kubernetes master service will be of type ClusterIP.", options.KubernetesServiceNodePort) + } + + if options.KubernetesServiceNodePort > 0 && !options.ServiceNodePortRange.Contains(options.KubernetesServiceNodePort) { + glog.Fatalf("Kubernetes service port range %v doesn't contain %v", options.ServiceNodePortRange, (options.KubernetesServiceNodePort)) + } +} + +func verifySecureAndInsecurePort(options *options.ServerRunOptions) { + if options.SecurePort < 0 || options.SecurePort > 65535 { + glog.Fatalf("--secure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", options.SecurePort) + } + + // TODO: Allow 0 to turn off insecure port. + if options.InsecurePort < 1 || options.InsecurePort > 65535 { + glog.Fatalf("--insecure-port %v must be between 1 and 65535, inclusive.", options.InsecurePort) + } + + if options.SecurePort == options.InsecurePort { + glog.Fatalf("--secure-port and --insecure-port cannot use the same port.") + } +} + +func ValidateRunOptions(options *options.ServerRunOptions) { + verifyClusterIPFlags(options) + verifyServiceNodePort(options) + verifySecureAndInsecurePort(options) +}