cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.59.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/promapi/config.go

164lines · modecode

1package promapi
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io"
8 "log/slog"
9 "net/http"
10 "net/url"
11 "time"
12
13 v1 "github.com/prometheus/client_golang/api/prometheus/v1"
14 "github.com/prymitive/current"
15 "gopkg.in/yaml.v3"
16)
17
18type ConfigSectionGlobal struct {
19 ExternalLabels map[string]string `yaml:"external_labels"`
20 ScrapeInterval time.Duration `yaml:"scrape_interval"`
21 ScrapeTimeout time.Duration `yaml:"scrape_timeout"`
22 EvaluationInterval time.Duration `yaml:"evaluation_interval"`
23}
24
25type PrometheusConfig struct {
26 RuleFiles []string `yaml:"rule_files"`
27 Global ConfigSectionGlobal `yaml:"global"`
28}
29
30type ConfigResult struct {
31 URI string
32 PublicURI string
33 Config PrometheusConfig
34}
35
36type configQuery struct {
37 prom *Prometheus
38 ctx context.Context
39 timestamp time.Time
40 cacheTTL time.Duration
41}
42
43func (q configQuery) Run() queryResult {
44 slog.Debug("Getting prometheus configuration", slog.String("uri", q.prom.safeURI))
45
46 ctx, cancel := q.prom.requestContext(q.ctx)
47 defer cancel()
48
49 var qr queryResult
50
51 args := url.Values{}
52 resp, err := q.prom.doRequest(ctx, http.MethodGet, q.Endpoint(), args)
53 if err != nil {
54 qr.err = fmt.Errorf("failed to query Prometheus config: %w", err)
55 return qr
56 }
57 defer resp.Body.Close()
58
59 if resp.StatusCode/100 != 2 {
60 qr.err = tryDecodingAPIError(resp)
61 return qr
62 }
63
64 qr.value, err = streamConfig(resp.Body)
65 if err != nil {
66 prometheusQueryErrorsTotal.WithLabelValues(q.prom.name, "/api/v1/status/config", errReason(err)).Inc()
67 qr.err = fmt.Errorf("failed to decode config data in %s response: %w", q.prom.safeURI, err)
68 }
69 return qr
70}
71
72func (q configQuery) Endpoint() string {
73 return "/api/v1/status/config"
74}
75
76func (q configQuery) String() string {
77 return "/api/v1/status/config"
78}
79
80func (q configQuery) CacheKey() uint64 {
81 return hash(q.prom.unsafeURI, q.Endpoint())
82}
83
84func (q configQuery) CacheTTL() time.Duration {
85 return q.cacheTTL
86}
87
88func (p *Prometheus) Config(ctx context.Context, cacheTTL time.Duration) (*ConfigResult, error) {
89 slog.Debug("Scheduling Prometheus configuration query", slog.String("uri", p.safeURI))
90
91 key := "/api/v1/status/config"
92 p.locker.lock(key)
93 defer p.locker.unlock(key)
94
95 if cacheTTL == 0 {
96 cacheTTL = time.Minute
97 }
98
99 resultChan := make(chan queryResult)
100 p.queries <- queryRequest{
101 query: configQuery{prom: p, ctx: ctx, timestamp: time.Now(), cacheTTL: cacheTTL},
102 result: resultChan,
103 }
104
105 result := <-resultChan
106 if result.err != nil {
107 return nil, QueryError{err: result.err, msg: decodeError(result.err)}
108 }
109
110 r := ConfigResult{
111 URI: p.safeURI,
112 PublicURI: p.publicURI,
113 Config: result.value.(PrometheusConfig),
114 }
115
116 return &r, nil
117}
118
119func streamConfig(r io.Reader) (cfg PrometheusConfig, err error) {
120 defer dummyReadAll(r)
121
122 var yamlBody, status, errType, errText string
123 decoder := current.Object(
124 current.Key("status", current.Value(func(s string, _ bool) {
125 status = s
126 })),
127 current.Key("error", current.Value(func(s string, _ bool) {
128 errText = s
129 })),
130 current.Key("errorType", current.Value(func(s string, _ bool) {
131 errType = s
132 })),
133 current.Key("data", current.Object(
134 current.Key("yaml", current.Value(func(s string, _ bool) {
135 yamlBody = s
136 })),
137 )),
138 )
139
140 dec := json.NewDecoder(r)
141 if err = decoder.Stream(dec); err != nil {
142 return cfg, APIError{Status: status, ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("JSON parse error: %s", err)}
143 }
144
145 if status != "success" {
146 return cfg, APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText}
147 }
148
149 if err = yaml.Unmarshal([]byte(yamlBody), &cfg); err != nil {
150 return cfg, err
151 }
152
153 if cfg.Global.ScrapeInterval == 0 {
154 cfg.Global.ScrapeInterval = time.Minute
155 }
156 if cfg.Global.ScrapeTimeout == 0 {
157 cfg.Global.ScrapeTimeout = time.Second * 10
158 }
159 if cfg.Global.EvaluationInterval == 0 {
160 cfg.Global.EvaluationInterval = time.Minute
161 }
162
163 return cfg, nil
164}
165