github.com/m3db/m3@v1.5.0/src/cluster/client/etcd/options.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package etcd 22 23 import ( 24 "crypto/tls" 25 "crypto/x509" 26 "errors" 27 "fmt" 28 "io/ioutil" 29 "math" 30 "os" 31 "time" 32 33 "google.golang.org/grpc" 34 35 "github.com/m3db/m3/src/cluster/services" 36 "github.com/m3db/m3/src/x/instrument" 37 "github.com/m3db/m3/src/x/retry" 38 ) 39 40 const ( 41 defaultAutoSyncInterval = 1 * time.Minute 42 defaultDialTimeout = 15 * time.Second 43 44 defaultKeepAliveEnabled = true 45 defaultKeepAlivePeriod = 20 * time.Second 46 defaultKeepAlivePeriodMaxJitter = 10 * time.Second 47 defaultKeepAliveTimeout = 10 * time.Second 48 49 defaultRequestTimeout = 10 * time.Second 50 defaultWatchChanCheckInterval = 10 * time.Second 51 defaultWatchChanResetInterval = 10 * time.Second 52 defaultWatchChanInitTimeout = 10 * time.Second 53 54 defaultRetryInitialBackoff = 2 * time.Second 55 defaultRetryBackoffFactor = 2.0 56 defaultRetryMaxRetries = 3 57 defaultRetryMaxBackoff = time.Duration(math.MaxInt64) 58 defaultRetryJitter = true 59 60 defaultDirectoryMode = os.FileMode(0755) 61 ) 62 63 type keepAliveOptions struct { 64 keepAliveEnabled bool 65 keepAlivePeriod time.Duration 66 keepAlivePeriodMaxJitter time.Duration 67 keepAliveTimeout time.Duration 68 } 69 70 // NewKeepAliveOptions provide a set of keepAlive options. 71 func NewKeepAliveOptions() KeepAliveOptions { 72 return &keepAliveOptions{ 73 keepAliveEnabled: defaultKeepAliveEnabled, 74 keepAlivePeriod: defaultKeepAlivePeriod, 75 keepAlivePeriodMaxJitter: defaultKeepAlivePeriodMaxJitter, 76 keepAliveTimeout: defaultKeepAliveTimeout, 77 } 78 } 79 80 func (o *keepAliveOptions) KeepAliveEnabled() bool { return o.keepAliveEnabled } 81 82 func (o *keepAliveOptions) SetKeepAliveEnabled(value bool) KeepAliveOptions { 83 opts := *o 84 opts.keepAliveEnabled = value 85 return &opts 86 } 87 88 func (o *keepAliveOptions) KeepAlivePeriod() time.Duration { return o.keepAlivePeriod } 89 90 func (o *keepAliveOptions) SetKeepAlivePeriod(value time.Duration) KeepAliveOptions { 91 opts := *o 92 opts.keepAlivePeriod = value 93 return &opts 94 } 95 96 func (o *keepAliveOptions) KeepAlivePeriodMaxJitter() time.Duration { 97 return o.keepAlivePeriodMaxJitter 98 } 99 100 func (o *keepAliveOptions) SetKeepAlivePeriodMaxJitter(value time.Duration) KeepAliveOptions { 101 opts := *o 102 opts.keepAlivePeriodMaxJitter = value 103 return &opts 104 } 105 106 func (o *keepAliveOptions) KeepAliveTimeout() time.Duration { 107 return o.keepAliveTimeout 108 } 109 110 func (o *keepAliveOptions) SetKeepAliveTimeout(value time.Duration) KeepAliveOptions { 111 opts := *o 112 opts.keepAliveTimeout = value 113 return &opts 114 } 115 116 // NewTLSOptions creates a set of TLS Options. 117 func NewTLSOptions() TLSOptions { 118 return tlsOptions{} 119 } 120 121 type tlsOptions struct { 122 cert string 123 key string 124 ca string 125 } 126 127 func (o tlsOptions) CrtPath() string { 128 return o.cert 129 } 130 131 func (o tlsOptions) SetCrtPath(cert string) TLSOptions { 132 o.cert = cert 133 return o 134 } 135 136 func (o tlsOptions) KeyPath() string { 137 return o.key 138 } 139 func (o tlsOptions) SetKeyPath(key string) TLSOptions { 140 o.key = key 141 return o 142 } 143 144 func (o tlsOptions) CACrtPath() string { 145 return o.ca 146 } 147 func (o tlsOptions) SetCACrtPath(ca string) TLSOptions { 148 o.ca = ca 149 return o 150 } 151 152 func (o tlsOptions) Config() (*tls.Config, error) { 153 if o.cert == "" { 154 // By default we should use nil config instead of empty config. 155 return nil, nil 156 } 157 158 cert, err := tls.LoadX509KeyPair(o.cert, o.key) 159 if err != nil { 160 return nil, err 161 } 162 caCert, err := ioutil.ReadFile(o.ca) 163 if err != nil { 164 return nil, err 165 } 166 caPool := x509.NewCertPool() 167 if ok := caPool.AppendCertsFromPEM(caCert); !ok { 168 return nil, fmt.Errorf("can't read PEM-formatted certificates from file %s as root CA pool", o.ca) 169 } 170 return &tls.Config{ 171 MinVersion: tls.VersionTLS12, 172 InsecureSkipVerify: false, 173 Certificates: []tls.Certificate{cert}, 174 RootCAs: caPool, 175 }, nil 176 } 177 178 // NewOptions creates a set of Options. 179 func NewOptions() Options { 180 return options{ 181 sdOpts: services.NewOptions(), 182 iopts: instrument.NewOptions(), 183 requestTimeout: defaultRequestTimeout, 184 watchChanInitTimeout: defaultWatchChanInitTimeout, 185 watchChanCheckInterval: defaultWatchChanCheckInterval, 186 watchChanResetInterval: defaultWatchChanResetInterval, 187 // NB(r): Set some default retry options so changes to retry 188 // option defaults don't change behavior of this client's retry options 189 retryOpts: retry.NewOptions(). 190 SetInitialBackoff(defaultRetryInitialBackoff). 191 SetBackoffFactor(defaultRetryBackoffFactor). 192 SetMaxBackoff(defaultRetryMaxBackoff). 193 SetMaxRetries(defaultRetryMaxRetries). 194 SetJitter(defaultRetryJitter), 195 newDirectoryMode: defaultDirectoryMode, 196 } 197 } 198 199 type options struct { 200 requestTimeout time.Duration 201 env string 202 zone string 203 service string 204 cacheDir string 205 watchChanCheckInterval time.Duration 206 watchChanResetInterval time.Duration 207 watchChanInitTimeout time.Duration 208 watchWithRevision int64 209 enableFastGets bool 210 sdOpts services.Options 211 clusters map[string]Cluster 212 iopts instrument.Options 213 retryOpts retry.Options 214 newDirectoryMode os.FileMode 215 } 216 217 func (o options) Validate() error { 218 if o.service == "" { 219 return errors.New("invalid options, no service name set") 220 } 221 222 if len(o.clusters) == 0 { 223 return errors.New("invalid options, no etcd clusters set") 224 } 225 226 if o.iopts == nil { 227 return errors.New("invalid options, no instrument options set") 228 } 229 230 if o.watchChanCheckInterval <= 0 { 231 return errors.New("invalid watch channel check interval") 232 } 233 234 if o.watchChanResetInterval <= 0 { 235 return errors.New("invalid watch reset interval") 236 } 237 238 if o.watchChanInitTimeout <= 0 { 239 return errors.New("invalid watch init interval") 240 } 241 242 if o.requestTimeout <= 0 { 243 return errors.New("invalid request timeout") 244 } 245 246 return nil 247 } 248 249 func (o options) Env() string { 250 return o.env 251 } 252 253 func (o options) SetEnv(e string) Options { 254 o.env = e 255 return o 256 } 257 258 func (o options) Zone() string { 259 return o.zone 260 } 261 262 func (o options) SetZone(z string) Options { 263 o.zone = z 264 return o 265 } 266 267 func (o options) ServicesOptions() services.Options { 268 return o.sdOpts 269 } 270 271 func (o options) SetServicesOptions(cfg services.Options) Options { 272 o.sdOpts = cfg 273 return o 274 } 275 276 func (o options) CacheDir() string { 277 return o.cacheDir 278 } 279 280 func (o options) SetCacheDir(dir string) Options { 281 o.cacheDir = dir 282 return o 283 } 284 285 func (o options) Service() string { 286 return o.service 287 } 288 289 func (o options) SetService(id string) Options { 290 o.service = id 291 return o 292 } 293 294 func (o options) Clusters() []Cluster { 295 res := make([]Cluster, 0, len(o.clusters)) 296 for _, c := range o.clusters { 297 res = append(res, c) 298 } 299 return res 300 } 301 302 func (o options) SetClusters(clusters []Cluster) Options { 303 o.clusters = make(map[string]Cluster, len(clusters)) 304 for _, c := range clusters { 305 o.clusters[c.Zone()] = c 306 } 307 return o 308 } 309 310 func (o options) ClusterForZone(z string) (Cluster, bool) { 311 c, ok := o.clusters[z] 312 return c, ok 313 } 314 315 func (o options) InstrumentOptions() instrument.Options { 316 return o.iopts 317 } 318 319 func (o options) SetInstrumentOptions(iopts instrument.Options) Options { 320 o.iopts = iopts 321 return o 322 } 323 324 //nolint:gocritic 325 func (o options) RequestTimeout() time.Duration { 326 return o.requestTimeout 327 } 328 329 //nolint:gocritic 330 func (o options) SetRequestTimeout(t time.Duration) Options { 331 o.requestTimeout = t 332 return o 333 } 334 335 func (o options) RetryOptions() retry.Options { 336 return o.retryOpts 337 } 338 339 func (o options) SetRetryOptions(retryOpts retry.Options) Options { 340 o.retryOpts = retryOpts 341 return o 342 } 343 344 //nolint:gocritic 345 func (o options) WatchChanCheckInterval() time.Duration { 346 return o.watchChanCheckInterval 347 } 348 349 //nolint:gocritic 350 func (o options) SetWatchChanCheckInterval(t time.Duration) Options { 351 o.watchChanCheckInterval = t 352 return o 353 } 354 355 //nolint:gocritic 356 func (o options) WatchChanResetInterval() time.Duration { 357 return o.watchChanResetInterval 358 } 359 360 //nolint:gocritic 361 func (o options) SetWatchChanResetInterval(t time.Duration) Options { 362 o.watchChanResetInterval = t 363 return o 364 } 365 366 //nolint:gocritic 367 func (o options) WatchChanInitTimeout() time.Duration { 368 return o.watchChanInitTimeout 369 } 370 371 //nolint:gocritic 372 func (o options) SetWatchChanInitTimeout(t time.Duration) Options { 373 o.watchChanInitTimeout = t 374 return o 375 } 376 377 func (o options) WatchWithRevision() int64 { 378 return o.watchWithRevision 379 } 380 381 func (o options) SetWatchWithRevision(rev int64) Options { 382 o.watchWithRevision = rev 383 return o 384 } 385 386 func (o options) SetNewDirectoryMode(fm os.FileMode) Options { 387 o.newDirectoryMode = fm 388 return o 389 } 390 391 func (o options) NewDirectoryMode() os.FileMode { 392 return o.newDirectoryMode 393 } 394 395 //nolint:gocritic 396 func (o options) EnableFastGets() bool { 397 return o.enableFastGets 398 } 399 400 //nolint:gocritic 401 func (o options) SetEnableFastGets(enabled bool) Options { 402 o.enableFastGets = enabled 403 return o 404 } 405 406 // NewCluster creates a Cluster. 407 func NewCluster() Cluster { 408 return cluster{ 409 autoSyncInterval: defaultAutoSyncInterval, 410 dialTimeout: defaultDialTimeout, 411 keepAliveOpts: NewKeepAliveOptions(), 412 tlsOpts: NewTLSOptions(), 413 } 414 } 415 416 type cluster struct { 417 zone string 418 endpoints []string 419 keepAliveOpts KeepAliveOptions 420 tlsOpts TLSOptions 421 autoSyncInterval time.Duration 422 dialTimeout time.Duration 423 dialOptions []grpc.DialOption 424 } 425 426 func (c cluster) Zone() string { 427 return c.zone 428 } 429 430 func (c cluster) SetZone(z string) Cluster { 431 c.zone = z 432 return c 433 } 434 435 func (c cluster) Endpoints() []string { 436 return c.endpoints 437 } 438 439 func (c cluster) SetEndpoints(endpoints []string) Cluster { 440 c.endpoints = endpoints 441 return c 442 } 443 444 func (c cluster) KeepAliveOptions() KeepAliveOptions { 445 return c.keepAliveOpts 446 } 447 448 func (c cluster) SetKeepAliveOptions(value KeepAliveOptions) Cluster { 449 c.keepAliveOpts = value 450 return c 451 } 452 453 func (c cluster) TLSOptions() TLSOptions { 454 return c.tlsOpts 455 } 456 457 func (c cluster) SetTLSOptions(opts TLSOptions) Cluster { 458 c.tlsOpts = opts 459 return c 460 } 461 462 func (c cluster) AutoSyncInterval() time.Duration { 463 return c.autoSyncInterval 464 } 465 466 func (c cluster) SetAutoSyncInterval(autoSyncInterval time.Duration) Cluster { 467 c.autoSyncInterval = autoSyncInterval 468 return c 469 } 470 471 //nolint:gocritic 472 func (c cluster) DialTimeout() time.Duration { 473 return c.dialTimeout 474 } 475 476 //nolint:gocritic 477 func (c cluster) SetDialTimeout(dialTimeout time.Duration) Cluster { 478 c.dialTimeout = dialTimeout 479 480 return c 481 } 482 483 func (c cluster) DialOptions() []grpc.DialOption { 484 return c.dialOptions 485 } 486 487 func (c cluster) SetDialOptions(opts []grpc.DialOption) Cluster { 488 c.dialOptions = opts 489 return c 490 }