cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.29.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/flags.go

126lines · modecode

1package promapi
2
3import (
4 "context"
5 "crypto/sha1"
6 "encoding/json"
7 "fmt"
8 "io"
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 "github.com/rs/zerolog/log"
16)
17
18type FlagsResult struct {
19 URI string
20 Flags v1.FlagsResult
21}
22
23type flagsQuery struct {
24 prom *Prometheus
25 ctx context.Context
26 timestamp time.Time
27}
28
29func (q flagsQuery) Run() queryResult {
30 log.Debug().
31 Str("uri", q.prom.uri).
32 Msg("Getting prometheus flags")
33
34 ctx, cancel := context.WithTimeout(q.ctx, q.prom.timeout)
35 defer cancel()
36
37 qr := queryResult{expires: q.timestamp.Add(cacheExpiry * 2)}
38
39 args := url.Values{}
40 resp, err := q.prom.doRequest(ctx, http.MethodGet, "/api/v1/status/flags", args)
41 if err != nil {
42 qr.err = fmt.Errorf("failed to query Prometheus flags: %w", err)
43 return qr
44 }
45 defer resp.Body.Close()
46
47 if resp.StatusCode/100 != 2 {
48 qr.err = tryDecodingAPIError(resp)
49 return qr
50 }
51
52 qr.value, qr.err = streamFlags(resp.Body)
53 return qr
54}
55
56func (q flagsQuery) Endpoint() string {
57 return "/api/v1/status/flags"
58}
59
60func (q flagsQuery) String() string {
61 return "/api/v1/status/flags"
62}
63
64func (q flagsQuery) CacheKey() string {
65 h := sha1.New()
66 _, _ = io.WriteString(h, q.Endpoint())
67 _, _ = io.WriteString(h, "\n")
68 _, _ = io.WriteString(h, q.timestamp.Round(cacheExpiry).Format(time.RFC3339))
69 return fmt.Sprintf("%x", h.Sum(nil))
70}
71
72func (p *Prometheus) Flags(ctx context.Context) (*FlagsResult, error) {
73 log.Debug().Str("uri", p.uri).Msg("Scheduling Prometheus flags query")
74
75 key := "/api/v1/status/flags"
76 p.locker.lock(key)
77 defer p.locker.unlock(key)
78
79 resultChan := make(chan queryResult)
80 p.queries <- queryRequest{
81 query: flagsQuery{prom: p, ctx: ctx, timestamp: time.Now()},
82 result: resultChan,
83 }
84
85 result := <-resultChan
86 if result.err != nil {
87 return nil, QueryError{err: result.err, msg: decodeError(result.err)}
88 }
89
90 r := FlagsResult{URI: p.uri, Flags: result.value.(v1.FlagsResult)}
91
92 return &r, nil
93}
94
95func streamFlags(r io.Reader) (flags v1.FlagsResult, err error) {
96 defer dummyReadAll(r)
97
98 var status, errType, errText string
99 flags = v1.FlagsResult{}
100 decoder := current.Object(
101 func() {},
102 current.Key("status", current.Text(func(s string) {
103 status = s
104 })),
105 current.Key("error", current.Text(func(s string) {
106 errText = s
107 })),
108 current.Key("errorType", current.Text(func(s string) {
109 errType = s
110 })),
111 current.Key("data", current.Map(func(k, v string) {
112 flags[k] = v
113 })),
114 )
115
116 dec := json.NewDecoder(r)
117 if err = current.Stream(dec, decoder); err != nil {
118 return nil, APIError{Status: status, ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("JSON parse error: %s", err)}
119 }
120
121 if status != "success" {
122 return nil, APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText}
123 }
124
125 return flags, nil
126}
127