cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.83.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/buildinfo.go

129lines · modecode

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