github.com/m3db/m3@v1.5.0/src/query/storage/promremote/options.go (about)

     1  // Copyright (c) 2021  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 promremote
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"strings"
    27  
    28  	"github.com/uber-go/tally"
    29  	"go.uber.org/zap"
    30  
    31  	"github.com/m3db/m3/src/cmd/services/m3query/config"
    32  	"github.com/m3db/m3/src/query/storage/m3"
    33  	"github.com/m3db/m3/src/query/storage/m3/storagemetadata"
    34  	xhttp "github.com/m3db/m3/src/x/net/http"
    35  )
    36  
    37  // NewOptions constructs Options based on the given config.
    38  func NewOptions(
    39  	cfg *config.PrometheusRemoteBackendConfiguration,
    40  	scope tally.Scope,
    41  	logger *zap.Logger,
    42  ) (Options, error) {
    43  	err := validateBackendConfiguration(cfg)
    44  	if err != nil {
    45  		return Options{}, err
    46  	}
    47  	endpoints := make([]EndpointOptions, 0, len(cfg.Endpoints))
    48  
    49  	for _, endpoint := range cfg.Endpoints {
    50  		var (
    51  			attr = storagemetadata.Attributes{
    52  				MetricsType: storagemetadata.UnaggregatedMetricsType,
    53  			}
    54  			downsampleOptions *m3.ClusterNamespaceDownsampleOptions
    55  		)
    56  		if endpoint.StoragePolicy != nil {
    57  			attr.MetricsType = storagemetadata.AggregatedMetricsType
    58  			attr.Resolution = endpoint.StoragePolicy.Resolution
    59  			attr.Retention = endpoint.StoragePolicy.Retention
    60  			downsampleOptions = &m3.DefaultClusterNamespaceDownsampleOptions
    61  			if downsample := endpoint.StoragePolicy.Downsample; downsample != nil {
    62  				downsampleOptions = &m3.ClusterNamespaceDownsampleOptions{
    63  					All: downsample.All,
    64  				}
    65  			}
    66  		}
    67  		endpoints = append(endpoints, EndpointOptions{
    68  			name:              endpoint.Name,
    69  			address:           endpoint.Address,
    70  			attributes:        attr,
    71  			downsampleOptions: downsampleOptions,
    72  		})
    73  	}
    74  	clientOpts := xhttp.DefaultHTTPClientOptions()
    75  	if cfg.RequestTimeout != nil {
    76  		clientOpts.RequestTimeout = *cfg.RequestTimeout
    77  	}
    78  	if cfg.ConnectTimeout != nil {
    79  		clientOpts.ConnectTimeout = *cfg.ConnectTimeout
    80  	}
    81  	if cfg.KeepAlive != nil {
    82  		clientOpts.KeepAlive = *cfg.KeepAlive
    83  	}
    84  	if cfg.IdleConnTimeout != nil {
    85  		clientOpts.IdleConnTimeout = *cfg.IdleConnTimeout
    86  	}
    87  	if cfg.MaxIdleConns != nil {
    88  		clientOpts.MaxIdleConns = *cfg.MaxIdleConns
    89  	}
    90  
    91  	clientOpts.DisableCompression = true // Already snappy compressed.
    92  
    93  	return Options{
    94  		endpoints:   endpoints,
    95  		httpOptions: clientOpts,
    96  		scope:       scope,
    97  		logger:      logger,
    98  	}, nil
    99  }
   100  
   101  func validateBackendConfiguration(cfg *config.PrometheusRemoteBackendConfiguration) error {
   102  	if cfg == nil {
   103  		return fmt.Errorf("prometheusRemoteBackend configuration is required")
   104  	}
   105  	if len(cfg.Endpoints) == 0 {
   106  		return fmt.Errorf(
   107  			"at least one endpoint must be configured when using %s backend type",
   108  			config.PromRemoteStorageType,
   109  		)
   110  	}
   111  	if cfg.MaxIdleConns != nil && *cfg.MaxIdleConns < 0 {
   112  		return errors.New("maxIdleConns can't be negative")
   113  	}
   114  	if cfg.KeepAlive != nil && *cfg.KeepAlive < 0 {
   115  		return errors.New("keepAlive can't be negative")
   116  	}
   117  	if cfg.IdleConnTimeout != nil && *cfg.IdleConnTimeout < 0 {
   118  		return errors.New("idleConnTimeout can't be negative")
   119  	}
   120  	if cfg.RequestTimeout != nil && *cfg.RequestTimeout < 0 {
   121  		return errors.New("requestTimeout can't be negative")
   122  	}
   123  	if cfg.ConnectTimeout != nil && *cfg.ConnectTimeout < 0 {
   124  		return errors.New("connectTimeout can't be negative")
   125  	}
   126  
   127  	seenNames := map[string]struct{}{}
   128  	for _, endpoint := range cfg.Endpoints {
   129  		if err := validateEndpointConfiguration(endpoint); err != nil {
   130  			return err
   131  		}
   132  		if _, ok := seenNames[endpoint.Name]; ok {
   133  			return fmt.Errorf("endpoint name %s is not unique, ensure all endpoint names are unique", endpoint.Name)
   134  		}
   135  		seenNames[endpoint.Name] = struct{}{}
   136  	}
   137  	return nil
   138  }
   139  
   140  func validateEndpointConfiguration(endpoint config.PrometheusRemoteBackendEndpointConfiguration) error {
   141  	if endpoint.StoragePolicy != nil {
   142  		if endpoint.StoragePolicy.Resolution <= 0 {
   143  			return errors.New("endpoint resolution must be positive")
   144  		}
   145  		if endpoint.StoragePolicy.Retention <= 0 {
   146  			return errors.New("endpoint retention must be positive")
   147  		}
   148  	}
   149  	if strings.TrimSpace(endpoint.Address) == "" {
   150  		return errors.New("endpoint address must be set")
   151  	}
   152  	if strings.TrimSpace(endpoint.Name) == "" {
   153  		return errors.New("endpoint name must be set")
   154  	}
   155  	return nil
   156  }