github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/configs/client/client.go (about) 1 package client 2 3 import ( 4 "context" 5 "crypto/tls" 6 "encoding/json" 7 "errors" 8 "flag" 9 "fmt" 10 "net/http" 11 "net/url" 12 "time" 13 14 "github.com/go-kit/log/level" 15 dstls "github.com/grafana/dskit/crypto/tls" 16 "github.com/grafana/dskit/flagext" 17 "github.com/prometheus/client_golang/prometheus" 18 "github.com/prometheus/client_golang/prometheus/promauto" 19 "github.com/prometheus/common/version" 20 "github.com/weaveworks/common/instrument" 21 22 "github.com/grafana/loki/pkg/configs/userconfig" 23 util_log "github.com/grafana/loki/pkg/util/log" 24 ) 25 26 var ( 27 errBadURL = errors.New("configs_api_url is not set or valid") 28 ) 29 30 // Config says where we can find the ruler userconfig. 31 type Config struct { 32 ConfigsAPIURL flagext.URLValue `yaml:"configs_api_url"` 33 ClientTimeout time.Duration `yaml:"client_timeout"` // HTTP timeout duration for requests made to the Weave Cloud configs service. 34 TLS dstls.ClientConfig `yaml:",inline"` 35 } 36 37 // RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet 38 func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 39 f.Var(&cfg.ConfigsAPIURL, prefix+"configs.url", "URL of configs API server.") 40 f.DurationVar(&cfg.ClientTimeout, prefix+"configs.client-timeout", 5*time.Second, "Timeout for requests to Weave Cloud configs service.") 41 cfg.TLS.RegisterFlagsWithPrefix(prefix+"configs", f) 42 } 43 44 var configsRequestDuration = instrument.NewHistogramCollector(promauto.NewHistogramVec(prometheus.HistogramOpts{ 45 Namespace: "cortex", 46 Name: "configs_request_duration_seconds", 47 Help: "Time spent requesting userconfig.", 48 Buckets: prometheus.DefBuckets, 49 }, []string{"operation", "status_code"})) 50 51 // Client is what the ruler and altermanger needs from a config store to process rules. 52 type Client interface { 53 // GetRules returns all Cortex configurations from a configs API server 54 // that have been updated after the given userconfig.ID was last updated. 55 GetRules(ctx context.Context, since userconfig.ID) (map[string]userconfig.VersionedRulesConfig, error) 56 57 // GetAlerts fetches all the alerts that have changes since since. 58 GetAlerts(ctx context.Context, since userconfig.ID) (*ConfigsResponse, error) 59 } 60 61 // New creates a new ConfigClient. 62 func New(cfg Config) (*ConfigDBClient, error) { 63 64 if cfg.ConfigsAPIURL.URL == nil { 65 return nil, errBadURL 66 } 67 68 client := &ConfigDBClient{ 69 URL: cfg.ConfigsAPIURL.URL, 70 Timeout: cfg.ClientTimeout, 71 } 72 73 tlsConfig, err := cfg.TLS.GetTLSConfig() 74 if err != nil { 75 return nil, err 76 } 77 78 if tlsConfig != nil { 79 client.TLSConfig = tlsConfig 80 } 81 return client, nil 82 } 83 84 // ConfigDBClient allows retrieving recording and alerting rules from the configs server. 85 type ConfigDBClient struct { 86 URL *url.URL 87 Timeout time.Duration 88 TLSConfig *tls.Config 89 } 90 91 // GetRules implements Client 92 func (c ConfigDBClient) GetRules(ctx context.Context, since userconfig.ID) (map[string]userconfig.VersionedRulesConfig, error) { 93 suffix := "" 94 if since != 0 { 95 suffix = fmt.Sprintf("?since=%d", since) 96 } 97 endpoint := fmt.Sprintf("%s/private/api/prom/configs/rules%s", c.URL.String(), suffix) 98 var response *ConfigsResponse 99 err := instrument.CollectedRequest(ctx, "GetRules", configsRequestDuration, instrument.ErrorCode, func(ctx context.Context) error { 100 var err error 101 response, err = doRequest(endpoint, c.Timeout, c.TLSConfig, since) 102 return err 103 }) 104 if err != nil { 105 return nil, err 106 } 107 configs := map[string]userconfig.VersionedRulesConfig{} 108 for id, view := range response.Configs { 109 cfg := view.GetVersionedRulesConfig() 110 if cfg != nil { 111 configs[id] = *cfg 112 } 113 } 114 return configs, nil 115 } 116 117 // GetAlerts implements Client. 118 func (c ConfigDBClient) GetAlerts(ctx context.Context, since userconfig.ID) (*ConfigsResponse, error) { 119 suffix := "" 120 if since != 0 { 121 suffix = fmt.Sprintf("?since=%d", since) 122 } 123 endpoint := fmt.Sprintf("%s/private/api/prom/configs/alertmanager%s", c.URL.String(), suffix) 124 var response *ConfigsResponse 125 err := instrument.CollectedRequest(ctx, "GetAlerts", configsRequestDuration, instrument.ErrorCode, func(ctx context.Context) error { 126 var err error 127 response, err = doRequest(endpoint, c.Timeout, c.TLSConfig, since) 128 return err 129 }) 130 return response, err 131 } 132 133 func doRequest(endpoint string, timeout time.Duration, tlsConfig *tls.Config, since userconfig.ID) (*ConfigsResponse, error) { 134 req, err := http.NewRequest("GET", endpoint, nil) 135 if err != nil { 136 return nil, err 137 } 138 139 client := &http.Client{Timeout: timeout} 140 if tlsConfig != nil { 141 client.Transport = &http.Transport{TLSClientConfig: tlsConfig} 142 } 143 144 req.Header.Set("User-Agent", fmt.Sprintf("Cortex/%s", version.Version)) 145 146 resp, err := client.Do(req) 147 if err != nil { 148 return nil, err 149 } 150 defer resp.Body.Close() 151 152 if resp.StatusCode != http.StatusOK { 153 return nil, fmt.Errorf("Invalid response from configs server: %v", resp.StatusCode) 154 } 155 156 var config ConfigsResponse 157 if err := json.NewDecoder(resp.Body).Decode(&config); err != nil { 158 level.Error(util_log.Logger).Log("msg", "configs: couldn't decode JSON body", "err", err) 159 return nil, err 160 } 161 162 config.since = since 163 return &config, nil 164 } 165 166 // ConfigsResponse is a response from server for Getuserconfig. 167 type ConfigsResponse struct { 168 // The version since which these configs were changed 169 since userconfig.ID 170 171 // Configs maps user ID to their latest userconfig.View. 172 Configs map[string]userconfig.View `json:"configs"` 173 } 174 175 // GetLatestConfigID returns the last config ID from a set of userconfig. 176 func (c ConfigsResponse) GetLatestConfigID() userconfig.ID { 177 latest := c.since 178 for _, config := range c.Configs { 179 if config.ID > latest { 180 latest = config.ID 181 } 182 } 183 return latest 184 }