cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.42.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/flags.go

125lines · modecode

1package promapi
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io"
8 "net/http"
9 "net/url"
10 "time"
11
12 v1 "github.com/prometheus/client_golang/api/prometheus/v1"
13 "github.com/prymitive/current"
14 "github.com/rs/zerolog/log"
15)
16
17type FlagsResult struct {
18 URI string
19 Flags v1.FlagsResult
20}
21
22type flagsQuery struct {
23 prom *Prometheus
24 ctx context.Context
25 timestamp time.Time
26}
27
28func (q flagsQuery) Run() queryResult {
29 log.Debug().
30 Str("uri", q.prom.safeURI).
31 Msg("Getting prometheus flags")
32
33 ctx, cancel := context.WithTimeout(q.ctx, q.prom.timeout)
34 defer cancel()
35
36 var qr queryResult
37
38 args := url.Values{}
39 resp, err := q.prom.doRequest(ctx, http.MethodGet, q.Endpoint(), args)
40 if err != nil {
41 qr.err = fmt.Errorf("failed to query Prometheus flags: %w", err)
42 return qr
43 }
44 defer resp.Body.Close()
45
46 if resp.StatusCode/100 != 2 {
47 qr.err = tryDecodingAPIError(resp)
48 return qr
49 }
50
51 flags, err := streamFlags(resp.Body)
52 qr.value, qr.err = flags, err
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() uint64 {
65 return hash(q.prom.unsafeURI, q.Endpoint())
66}
67
68func (q flagsQuery) CacheTTL() time.Duration {
69 return time.Minute * 10
70}
71
72func (p *Prometheus) Flags(ctx context.Context) (*FlagsResult, error) {
73 log.Debug().Str("uri", p.safeURI).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.safeURI, 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 current.Key("status", current.Value(func(s string, isNil bool) {
102 status = s
103 })),
104 current.Key("error", current.Value(func(s string, isNil bool) {
105 errText = s
106 })),
107 current.Key("errorType", current.Value(func(s string, isNil bool) {
108 errType = s
109 })),
110 current.Key("data", current.Map(func(k, v string) {
111 flags[k] = v
112 })),
113 )
114
115 dec := json.NewDecoder(r)
116 if err = decoder.Stream(dec); err != nil {
117 return nil, APIError{Status: status, ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("JSON parse error: %s", err)}
118 }
119
120 if status != "success" {
121 return nil, APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText}
122 }
123
124 return flags, nil
125}
126