cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.81.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/buildinfo.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)
15
16const (
17 APIPathBuildInfo = "/api/v1/status/buildinfo"
18)
19
20type PrometheusBuildInfoResponse struct {
21 Data v1.BuildinfoResult `json:"data"`
22 PrometheusResponse
23}
24
25type BuildInfoResult struct {
26 URI string
27 Version string
28}
29
30type buildInfoQuery struct {
31 prom *Prometheus
32 ctx context.Context
33 timestamp time.Time
34}
35
36func (q buildInfoQuery) Run() queryResult {
37 slog.LogAttrs(q.ctx, slog.LevelDebug, "Getting prometheus build info", slog.String("uri", q.prom.safeURI))
38
39 ctx, cancel := q.prom.requestContext(q.ctx)
40 defer cancel()
41
42 var qr queryResult
43
44 args := url.Values{}
45 resp, err := q.prom.doRequest(ctx, http.MethodGet, q.Endpoint(), args)
46 if err != nil {
47 qr.err = fmt.Errorf("failed to query Prometheus build info: %w", err)
48 return qr
49 }
50 defer resp.Body.Close()
51
52 if resp.StatusCode/100 != 2 {
53 qr.err = tryDecodingAPIError(resp)
54 return qr
55 }
56
57 bi, err := parseBuildInfo(resp.Body)
58 qr.value, qr.err = bi, err
59 return qr
60}
61
62func (q buildInfoQuery) Endpoint() string {
63 return APIPathBuildInfo
64}
65
66func (q buildInfoQuery) String() string {
67 return APIPathBuildInfo
68}
69
70func (q buildInfoQuery) CacheKey() uint64 {
71 return hash(q.prom.unsafeURI, q.Endpoint())
72}
73
74func (q buildInfoQuery) CacheTTL() time.Duration {
75 return time.Minute * 10
76}
77
78func (prom *Prometheus) BuildInfo(ctx context.Context) (*BuildInfoResult, error) {
79 slog.LogAttrs(ctx, slog.LevelDebug, "Scheduling Prometheus build info query", slog.String("uri", prom.safeURI))
80
81 prom.locker.lock(APIPathBuildInfo)
82 defer prom.locker.unlock(APIPathBuildInfo)
83
84 resultChan := make(chan queryResult)
85 prom.queries <- queryRequest{
86 query: buildInfoQuery{prom: prom, ctx: ctx, timestamp: time.Now()},
87 result: resultChan,
88 }
89
90 result := <-resultChan
91 if result.err != nil {
92 return nil, QueryError{err: result.err, msg: decodeError(result.err)}
93 }
94
95 bi := result.value.(v1.BuildinfoResult)
96 r := BuildInfoResult{
97 URI: prom.publicURI,
98 Version: bi.Version,
99 }
100
101 return &r, nil
102}
103
104func parseBuildInfo(r io.Reader) (_ v1.BuildinfoResult, err error) {
105 defer dummyReadAll(r)
106
107 var data PrometheusBuildInfoResponse
108 if err = json.NewDecoder(r).Decode(&data); err != nil {
109 return data.Data, APIError{
110 Status: data.Status,
111 ErrorType: v1.ErrBadResponse,
112 Err: "JSON parse error: " + err.Error(),
113 }
114 }
115
116 if data.Status != "success" {
117 if data.Error == "" {
118 data.Error = "empty response object"
119 }
120 return data.Data, APIError{
121 Status: data.Status,
122 ErrorType: decodeErrorType(data.ErrorType),
123 Err: data.Error,
124 }
125 }
126
127 return data.Data, nil
128}
129