cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.49.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/flags.go

128lines · 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)
16
17type FlagsResult struct {
18 URI string
19 PublicURI 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 slog.Debug("Getting prometheus flags", slog.String("uri", q.prom.safeURI))
31
32 ctx, cancel := q.prom.requestContext(q.ctx)
33 defer cancel()
34
35 var qr queryResult
36
37 args := url.Values{}
38 resp, err := q.prom.doRequest(ctx, http.MethodGet, q.Endpoint(), args)
39 if err != nil {
40 qr.err = fmt.Errorf("failed to query Prometheus flags: %w", err)
41 return qr
42 }
43 defer resp.Body.Close()
44
45 if resp.StatusCode/100 != 2 {
46 qr.err = tryDecodingAPIError(resp)
47 return qr
48 }
49
50 flags, err := streamFlags(resp.Body)
51 qr.value, qr.err = flags, err
52 return qr
53}
54
55func (q flagsQuery) Endpoint() string {
56 return "/api/v1/status/flags"
57}
58
59func (q flagsQuery) String() string {
60 return "/api/v1/status/flags"
61}
62
63func (q flagsQuery) CacheKey() uint64 {
64 return hash(q.prom.unsafeURI, q.Endpoint())
65}
66
67func (q flagsQuery) CacheTTL() time.Duration {
68 return time.Minute * 10
69}
70
71func (p *Prometheus) Flags(ctx context.Context) (*FlagsResult, error) {
72 slog.Debug("Scheduling Prometheus flags query", slog.String("uri", p.safeURI))
73
74 key := "/api/v1/status/flags"
75 p.locker.lock(key)
76 defer p.locker.unlock(key)
77
78 resultChan := make(chan queryResult)
79 p.queries <- queryRequest{
80 query: flagsQuery{prom: p, ctx: ctx, timestamp: time.Now()},
81 result: resultChan,
82 }
83
84 result := <-resultChan
85 if result.err != nil {
86 return nil, QueryError{err: result.err, msg: decodeError(result.err)}
87 }
88
89 r := FlagsResult{
90 URI: p.safeURI,
91 PublicURI: p.publicURI,
92 Flags: result.value.(v1.FlagsResult),
93 }
94
95 return &r, nil
96}
97
98func streamFlags(r io.Reader) (flags v1.FlagsResult, err error) {
99 defer dummyReadAll(r)
100
101 var status, errType, errText string
102 flags = v1.FlagsResult{}
103 decoder := current.Object(
104 current.Key("status", current.Value(func(s string, isNil bool) {
105 status = s
106 })),
107 current.Key("error", current.Value(func(s string, isNil bool) {
108 errText = s
109 })),
110 current.Key("errorType", current.Value(func(s string, isNil bool) {
111 errType = s
112 })),
113 current.Key("data", current.Map(func(k, v string) {
114 flags[k] = v
115 })),
116 )
117
118 dec := json.NewDecoder(r)
119 if err = decoder.Stream(dec); err != nil {
120 return nil, APIError{Status: status, ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("JSON parse error: %s", err)}
121 }
122
123 if status != "success" {
124 return nil, APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText}
125 }
126
127 return flags, nil
128}
129