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  }