1
2
3
4
5
6
7
8
9
10
11
12
13
14 package test
15
16 import (
17 "bytes"
18 "context"
19 "encoding/json"
20 "fmt"
21 "net/http"
22 "net/url"
23 "time"
24
25 "github.com/prometheus/client_golang/api"
26
27 "github.com/prometheus/alertmanager/config"
28 "github.com/prometheus/alertmanager/types"
29 )
30
31 const (
32 apiPrefix = "/api/v1"
33
34 epStatus = apiPrefix + "/status"
35 epSilence = apiPrefix + "/silence/:id"
36 epSilences = apiPrefix + "/silences"
37 epAlerts = apiPrefix + "/alerts"
38
39 statusSuccess = "success"
40 statusError = "error"
41 )
42
43
44 type ServerStatus struct {
45 ConfigYAML string `json:"configYAML"`
46 ConfigJSON *config.Config `json:"configJSON"`
47 VersionInfo map[string]string `json:"versionInfo"`
48 Uptime time.Time `json:"uptime"`
49 ClusterStatus *ClusterStatus `json:"clusterStatus"`
50 }
51
52
53 type PeerStatus struct {
54 Name string `json:"name"`
55 Address string `json:"address"`
56 }
57
58
59 type ClusterStatus struct {
60 Name string `json:"name"`
61 Status string `json:"status"`
62 Peers []PeerStatus `json:"peers"`
63 }
64
65
66
67 type apiClient struct {
68 api.Client
69 }
70
71 type apiResponse struct {
72 Status string `json:"status"`
73 Data json.RawMessage `json:"data,omitempty"`
74 ErrorType string `json:"errorType,omitempty"`
75 Error string `json:"error,omitempty"`
76 }
77
78 type clientError struct {
79 code int
80 msg string
81 }
82
83 func (e *clientError) Error() string {
84 return fmt.Sprintf("%s (code: %d)", e.msg, e.code)
85 }
86
87 func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
88 resp, body, err := c.Client.Do(ctx, req)
89 if err != nil {
90 return resp, body, err
91 }
92
93 code := resp.StatusCode
94
95 var result apiResponse
96 if err = json.Unmarshal(body, &result); err != nil {
97
98
99 return resp, body, &clientError{
100 code: code,
101 msg: string(body),
102 }
103 }
104
105 if (code/100 == 2) && (result.Status != statusSuccess) {
106 return resp, body, &clientError{
107 code: code,
108 msg: "inconsistent body for response code",
109 }
110 }
111
112 if result.Status == statusError {
113 err = &clientError{
114 code: code,
115 msg: result.Error,
116 }
117 }
118
119 return resp, []byte(result.Data), err
120 }
121
122
123 type StatusAPI interface {
124
125 Get(ctx context.Context) (*ServerStatus, error)
126 }
127
128
129 func NewStatusAPI(c api.Client) StatusAPI {
130 return &httpStatusAPI{client: apiClient{c}}
131 }
132
133 type httpStatusAPI struct {
134 client api.Client
135 }
136
137 func (h *httpStatusAPI) Get(ctx context.Context) (*ServerStatus, error) {
138 u := h.client.URL(epStatus, nil)
139
140 req, err := http.NewRequest(http.MethodGet, u.String(), nil)
141 if err != nil {
142 return nil, fmt.Errorf("error creating request: %v", err)
143 }
144
145 _, body, err := h.client.Do(ctx, req)
146 if err != nil {
147 return nil, err
148 }
149
150 var ss *ServerStatus
151 err = json.Unmarshal(body, &ss)
152
153 return ss, err
154 }
155
156
157 type AlertAPI interface {
158
159 List(ctx context.Context, filter, receiver string, silenced, inhibited, active, unprocessed bool) ([]*ExtendedAlert, error)
160
161 Push(ctx context.Context, alerts ...APIV1Alert) error
162 }
163
164
165 type APIV1Alert struct {
166 Labels LabelSet `json:"labels"`
167 Annotations LabelSet `json:"annotations"`
168 StartsAt time.Time `json:"startsAt,omitempty"`
169 EndsAt time.Time `json:"endsAt,omitempty"`
170 GeneratorURL string `json:"generatorURL"`
171 }
172
173
174 type ExtendedAlert struct {
175 APIV1Alert
176 Status types.AlertStatus `json:"status"`
177 Receivers []string `json:"receivers"`
178 Fingerprint string `json:"fingerprint"`
179 }
180
181
182 type LabelSet map[LabelName]LabelValue
183
184
185 type LabelName string
186
187
188 type LabelValue string
189
190
191 func NewAlertAPI(c api.Client) AlertAPI {
192 return &httpAlertAPI{client: apiClient{c}}
193 }
194
195 type httpAlertAPI struct {
196 client api.Client
197 }
198
199 func (h *httpAlertAPI) List(ctx context.Context, filter, receiver string, silenced, inhibited, active, unprocessed bool) ([]*ExtendedAlert, error) {
200 u := h.client.URL(epAlerts, nil)
201 params := url.Values{}
202 if filter != "" {
203 params.Add("filter", filter)
204 }
205 params.Add("silenced", fmt.Sprintf("%t", silenced))
206 params.Add("inhibited", fmt.Sprintf("%t", inhibited))
207 params.Add("active", fmt.Sprintf("%t", active))
208 params.Add("unprocessed", fmt.Sprintf("%t", unprocessed))
209 params.Add("receiver", receiver)
210 u.RawQuery = params.Encode()
211
212 req, err := http.NewRequest(http.MethodGet, u.String(), nil)
213 if err != nil {
214 return nil, fmt.Errorf("error creating request: %v", err)
215 }
216
217 _, body, err := h.client.Do(ctx, req)
218 if err != nil {
219 return nil, err
220 }
221
222 var alts []*ExtendedAlert
223 err = json.Unmarshal(body, &alts)
224
225 return alts, err
226 }
227
228 func (h *httpAlertAPI) Push(ctx context.Context, alerts ...APIV1Alert) error {
229 u := h.client.URL(epAlerts, nil)
230
231 var buf bytes.Buffer
232 if err := json.NewEncoder(&buf).Encode(&alerts); err != nil {
233 return err
234 }
235
236 req, err := http.NewRequest(http.MethodPost, u.String(), &buf)
237 if err != nil {
238 return fmt.Errorf("error creating request: %v", err)
239 }
240
241 _, _, err = h.client.Do(ctx, req)
242 return err
243 }
244
245
246 type SilenceAPI interface {
247
248 Get(ctx context.Context, id string) (*types.Silence, error)
249
250 Set(ctx context.Context, sil types.Silence) (string, error)
251
252 Expire(ctx context.Context, id string) error
253
254 List(ctx context.Context, filter string) ([]*types.Silence, error)
255 }
256
257
258 func NewSilenceAPI(c api.Client) SilenceAPI {
259 return &httpSilenceAPI{client: apiClient{c}}
260 }
261
262 type httpSilenceAPI struct {
263 client api.Client
264 }
265
266 func (h *httpSilenceAPI) Get(ctx context.Context, id string) (*types.Silence, error) {
267 u := h.client.URL(epSilence, map[string]string{
268 "id": id,
269 })
270
271 req, err := http.NewRequest(http.MethodGet, u.String(), nil)
272 if err != nil {
273 return nil, fmt.Errorf("error creating request: %v", err)
274 }
275
276 _, body, err := h.client.Do(ctx, req)
277 if err != nil {
278 return nil, err
279 }
280
281 var sil types.Silence
282 err = json.Unmarshal(body, &sil)
283
284 return &sil, err
285 }
286
287 func (h *httpSilenceAPI) Expire(ctx context.Context, id string) error {
288 u := h.client.URL(epSilence, map[string]string{
289 "id": id,
290 })
291
292 req, err := http.NewRequest(http.MethodDelete, u.String(), nil)
293 if err != nil {
294 return fmt.Errorf("error creating request: %v", err)
295 }
296
297 _, _, err = h.client.Do(ctx, req)
298 return err
299 }
300
301 func (h *httpSilenceAPI) Set(ctx context.Context, sil types.Silence) (string, error) {
302 u := h.client.URL(epSilences, nil)
303
304 var buf bytes.Buffer
305 if err := json.NewEncoder(&buf).Encode(&sil); err != nil {
306 return "", err
307 }
308
309 req, err := http.NewRequest(http.MethodPost, u.String(), &buf)
310 if err != nil {
311 return "", fmt.Errorf("error creating request: %v", err)
312 }
313
314 _, body, err := h.client.Do(ctx, req)
315 if err != nil {
316 return "", err
317 }
318
319 var res struct {
320 SilenceID string `json:"silenceId"`
321 }
322 err = json.Unmarshal(body, &res)
323
324 return res.SilenceID, err
325 }
326
327 func (h *httpSilenceAPI) List(ctx context.Context, filter string) ([]*types.Silence, error) {
328 u := h.client.URL(epSilences, nil)
329 params := url.Values{}
330 if filter != "" {
331 params.Add("filter", filter)
332 }
333 u.RawQuery = params.Encode()
334
335 req, err := http.NewRequest(http.MethodGet, u.String(), nil)
336 if err != nil {
337 return nil, fmt.Errorf("error creating request: %v", err)
338 }
339
340 _, body, err := h.client.Do(ctx, req)
341 if err != nil {
342 return nil, err
343 }
344
345 var sils []*types.Silence
346 err = json.Unmarshal(body, &sils)
347
348 return sils, err
349 }
350
View as plain text