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 }