2019-01-12 04:58:27 +00:00
/ *
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 (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/healthz"
2019-09-27 21:51:53 +00:00
"k8s.io/apiserver/pkg/server/options/encryptionconfig"
2019-01-12 04:58:27 +00:00
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiserver/pkg/storage/storagebackend"
storagefactory "k8s.io/apiserver/pkg/storage/storagebackend/factory"
)
type EtcdOptions struct {
// The value of Paging on StorageConfig will be overridden by the
// calculated feature gate value.
StorageConfig storagebackend . Config
EncryptionProviderConfigFilepath string
EtcdServersOverrides [ ] string
// To enable protobuf as storage format, it is enough
// to set it to "application/vnd.kubernetes.protobuf".
DefaultStorageMediaType string
DeleteCollectionWorkers int
EnableGarbageCollection bool
// Set EnableWatchCache to false to disable all watch caches
EnableWatchCache bool
// Set DefaultWatchCacheSize to zero to disable watch caches for those resources that have no explicit cache size set
DefaultWatchCacheSize int
// WatchCacheSizes represents override to a given resource
WatchCacheSizes [ ] string
}
var storageTypes = sets . NewString (
storagebackend . StorageTypeETCD3 ,
)
func NewEtcdOptions ( backendConfig * storagebackend . Config ) * EtcdOptions {
options := & EtcdOptions {
StorageConfig : * backendConfig ,
DefaultStorageMediaType : "application/json" ,
DeleteCollectionWorkers : 1 ,
EnableGarbageCollection : true ,
EnableWatchCache : true ,
DefaultWatchCacheSize : 100 ,
}
options . StorageConfig . CountMetricPollPeriod = time . Minute
return options
}
func ( s * EtcdOptions ) Validate ( ) [ ] error {
2019-08-30 18:33:25 +00:00
if s == nil {
return nil
}
allErrors := [ ] error { }
if len ( s . StorageConfig . Transport . ServerList ) == 0 {
allErrors = append ( allErrors , fmt . Errorf ( "--etcd-servers must be specified" ) )
}
if s . StorageConfig . Type != storagebackend . StorageTypeUnset && ! storageTypes . Has ( s . StorageConfig . Type ) {
allErrors = append ( allErrors , fmt . Errorf ( "--storage-backend invalid, allowed values: %s. If not specified, it will default to 'etcd3'" , strings . Join ( storageTypes . List ( ) , ", " ) ) )
}
for _ , override := range s . EtcdServersOverrides {
tokens := strings . Split ( override , "#" )
if len ( tokens ) != 2 {
allErrors = append ( allErrors , fmt . Errorf ( "--etcd-servers-overrides invalid, must be of format: group/resource#servers, where servers are URLs, semicolon separated" ) )
continue
}
apiresource := strings . Split ( tokens [ 0 ] , "/" )
if len ( apiresource ) != 2 {
allErrors = append ( allErrors , fmt . Errorf ( "--etcd-servers-overrides invalid, must be of format: group/resource#servers, where servers are URLs, semicolon separated" ) )
continue
}
}
return allErrors
2019-01-12 04:58:27 +00:00
}
// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet
func ( s * EtcdOptions ) AddFlags ( fs * pflag . FlagSet ) {
if s == nil {
return
}
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 URLs, semicolon separated." )
fs . StringVar ( & s . DefaultStorageMediaType , "storage-media-type" , s . DefaultStorageMediaType , "" +
"The media type to use to store objects in storage. " +
"Some resources or storage backends may only support a specific media type and will ignore this setting." )
fs . IntVar ( & s . DeleteCollectionWorkers , "delete-collection-workers" , s . DeleteCollectionWorkers ,
"Number of workers spawned for DeleteCollection call. These are used to speed up namespace cleanup." )
fs . BoolVar ( & s . EnableGarbageCollection , "enable-garbage-collector" , s . EnableGarbageCollection , "" +
"Enables the generic garbage collector. MUST be synced with the corresponding flag " +
"of the kube-controller-manager." )
fs . BoolVar ( & s . EnableWatchCache , "watch-cache" , s . EnableWatchCache ,
"Enable watch caching in the apiserver" )
fs . IntVar ( & s . DefaultWatchCacheSize , "default-watch-cache-size" , s . DefaultWatchCacheSize ,
"Default watch cache size. If zero, watch cache will be disabled for resources that do not have a default watch size set." )
fs . StringSliceVar ( & s . WatchCacheSizes , "watch-cache-sizes" , s . WatchCacheSizes , "" +
2019-04-07 17:07:55 +00:00
"Watch cache size settings for some resources (pods, nodes, etc.), comma separated. " +
"The individual setting format: resource[.group]#size, where resource is lowercase plural (no version), " +
"group is omitted for resources of apiVersion v1 (the legacy core API) and included for others, " +
"and size is a number. It takes effect when watch-cache is enabled. " +
2019-01-12 04:58:27 +00:00
"Some resources (replicationcontrollers, endpoints, nodes, pods, services, apiservices.apiregistration.k8s.io) " +
"have system defaults set by heuristics, others default to default-watch-cache-size" )
fs . StringVar ( & s . StorageConfig . Type , "storage-backend" , s . StorageConfig . Type ,
"The storage backend for persistence. Options: 'etcd3' (default)." )
dummyCacheSize := 0
fs . IntVar ( & dummyCacheSize , "deserialization-cache-size" , 0 , "Number of deserialized json objects to cache in memory." )
fs . MarkDeprecated ( "deserialization-cache-size" , "the deserialization cache was dropped in 1.13 with support for etcd2" )
2019-04-07 17:07:55 +00:00
fs . StringSliceVar ( & s . StorageConfig . Transport . ServerList , "etcd-servers" , s . StorageConfig . Transport . ServerList ,
2019-01-12 04:58:27 +00:00
"List of etcd servers to connect with (scheme://ip:port), comma separated." )
fs . StringVar ( & s . StorageConfig . Prefix , "etcd-prefix" , s . StorageConfig . Prefix ,
"The prefix to prepend to all resource paths in etcd." )
2019-04-07 17:07:55 +00:00
fs . StringVar ( & s . StorageConfig . Transport . KeyFile , "etcd-keyfile" , s . StorageConfig . Transport . KeyFile ,
2019-01-12 04:58:27 +00:00
"SSL key file used to secure etcd communication." )
2019-04-07 17:07:55 +00:00
fs . StringVar ( & s . StorageConfig . Transport . CertFile , "etcd-certfile" , s . StorageConfig . Transport . CertFile ,
2019-01-12 04:58:27 +00:00
"SSL certification file used to secure etcd communication." )
2019-12-12 01:27:03 +00:00
fs . StringVar ( & s . StorageConfig . Transport . TrustedCAFile , "etcd-cafile" , s . StorageConfig . Transport . TrustedCAFile ,
2019-01-12 04:58:27 +00:00
"SSL Certificate Authority file used to secure etcd communication." )
fs . StringVar ( & s . EncryptionProviderConfigFilepath , "experimental-encryption-provider-config" , s . EncryptionProviderConfigFilepath ,
"The file containing configuration for encryption providers to be used for storing secrets in etcd" )
fs . MarkDeprecated ( "experimental-encryption-provider-config" , "use --encryption-provider-config." )
fs . StringVar ( & s . EncryptionProviderConfigFilepath , "encryption-provider-config" , s . EncryptionProviderConfigFilepath ,
"The file containing configuration for encryption providers to be used for storing secrets in etcd" )
fs . DurationVar ( & s . StorageConfig . CompactionInterval , "etcd-compaction-interval" , s . StorageConfig . CompactionInterval ,
"The interval of compaction requests. If 0, the compaction request from apiserver is disabled." )
fs . DurationVar ( & s . StorageConfig . CountMetricPollPeriod , "etcd-count-metric-poll-period" , s . StorageConfig . CountMetricPollPeriod , "" +
"Frequency of polling etcd for number of resources per type. 0 disables the metric collection." )
}
func ( s * EtcdOptions ) ApplyTo ( c * server . Config ) error {
if s == nil {
return nil
}
if err := s . addEtcdHealthEndpoint ( c ) ; err != nil {
return err
}
c . RESTOptionsGetter = & SimpleRestOptionsFactory { Options : * s }
return nil
}
func ( s * EtcdOptions ) ApplyWithStorageFactoryTo ( factory serverstorage . StorageFactory , c * server . Config ) error {
if err := s . addEtcdHealthEndpoint ( c ) ; err != nil {
return err
}
c . RESTOptionsGetter = & StorageFactoryRestOptionsFactory { Options : * s , StorageFactory : factory }
return nil
}
func ( s * EtcdOptions ) addEtcdHealthEndpoint ( c * server . Config ) error {
healthCheck , err := storagefactory . CreateHealthCheck ( s . StorageConfig )
if err != nil {
return err
}
2019-10-16 05:42:28 +00:00
c . AddHealthChecks ( healthz . NamedCheck ( "etcd" , func ( r * http . Request ) error {
2019-01-12 04:58:27 +00:00
return healthCheck ( )
} ) )
2019-09-27 21:51:53 +00:00
if s . EncryptionProviderConfigFilepath != "" {
kmsPluginHealthzChecks , err := encryptionconfig . GetKMSPluginHealthzCheckers ( s . EncryptionProviderConfigFilepath )
if err != nil {
return err
}
2019-10-16 05:42:28 +00:00
c . AddHealthChecks ( kmsPluginHealthzChecks ... )
2019-09-27 21:51:53 +00:00
}
2019-01-12 04:58:27 +00:00
return nil
}
type SimpleRestOptionsFactory struct {
Options EtcdOptions
}
func ( f * SimpleRestOptionsFactory ) GetRESTOptions ( resource schema . GroupResource ) ( generic . RESTOptions , error ) {
ret := generic . RESTOptions {
StorageConfig : & f . Options . StorageConfig ,
Decorator : generic . UndecoratedStorage ,
EnableGarbageCollection : f . Options . EnableGarbageCollection ,
DeleteCollectionWorkers : f . Options . DeleteCollectionWorkers ,
ResourcePrefix : resource . Group + "/" + resource . Resource ,
CountMetricPollPeriod : f . Options . StorageConfig . CountMetricPollPeriod ,
}
if f . Options . EnableWatchCache {
sizes , err := ParseWatchCacheSizes ( f . Options . WatchCacheSizes )
if err != nil {
return generic . RESTOptions { } , err
}
cacheSize , ok := sizes [ resource ]
if ! ok {
cacheSize = f . Options . DefaultWatchCacheSize
}
2019-08-30 18:33:25 +00:00
// depending on cache size this might return an undecorated storage
2019-01-12 04:58:27 +00:00
ret . Decorator = genericregistry . StorageWithCacher ( cacheSize )
}
return ret , nil
}
type StorageFactoryRestOptionsFactory struct {
Options EtcdOptions
StorageFactory serverstorage . StorageFactory
}
func ( f * StorageFactoryRestOptionsFactory ) GetRESTOptions ( resource schema . GroupResource ) ( generic . RESTOptions , error ) {
storageConfig , err := f . StorageFactory . NewConfig ( resource )
if err != nil {
return generic . RESTOptions { } , fmt . Errorf ( "unable to find storage destination for %v, due to %v" , resource , err . Error ( ) )
}
ret := generic . RESTOptions {
StorageConfig : storageConfig ,
Decorator : generic . UndecoratedStorage ,
DeleteCollectionWorkers : f . Options . DeleteCollectionWorkers ,
EnableGarbageCollection : f . Options . EnableGarbageCollection ,
ResourcePrefix : f . StorageFactory . ResourcePrefix ( resource ) ,
CountMetricPollPeriod : f . Options . StorageConfig . CountMetricPollPeriod ,
}
if f . Options . EnableWatchCache {
sizes , err := ParseWatchCacheSizes ( f . Options . WatchCacheSizes )
if err != nil {
return generic . RESTOptions { } , err
}
cacheSize , ok := sizes [ resource ]
if ! ok {
cacheSize = f . Options . DefaultWatchCacheSize
}
2019-08-30 18:33:25 +00:00
// depending on cache size this might return an undecorated storage
2019-01-12 04:58:27 +00:00
ret . Decorator = genericregistry . StorageWithCacher ( cacheSize )
}
return ret , nil
}
// ParseWatchCacheSizes turns a list of cache size values into a map of group resources
// to requested sizes.
func ParseWatchCacheSizes ( cacheSizes [ ] string ) ( map [ schema . GroupResource ] int , error ) {
watchCacheSizes := make ( map [ schema . GroupResource ] int )
for _ , c := range cacheSizes {
tokens := strings . Split ( c , "#" )
if len ( tokens ) != 2 {
return nil , fmt . Errorf ( "invalid value of watch cache size: %s" , c )
}
size , err := strconv . Atoi ( tokens [ 1 ] )
if err != nil {
return nil , fmt . Errorf ( "invalid size of watch cache size: %s" , c )
}
if size < 0 {
return nil , fmt . Errorf ( "watch cache size cannot be negative: %s" , c )
}
watchCacheSizes [ schema . ParseGroupResource ( tokens [ 0 ] ) ] = size
}
return watchCacheSizes , nil
}
// WriteWatchCacheSizes turns a map of cache size values into a list of string specifications.
func WriteWatchCacheSizes ( watchCacheSizes map [ schema . GroupResource ] int ) ( [ ] string , error ) {
var cacheSizes [ ] string
for resource , size := range watchCacheSizes {
if size < 0 {
return nil , fmt . Errorf ( "watch cache size cannot be negative for resource %s" , resource )
}
cacheSizes = append ( cacheSizes , fmt . Sprintf ( "%s#%d" , resource . String ( ) , size ) )
}
return cacheSizes , nil
}