Merge ~ubuntu-docker-images/ubuntu-docker-images/+git/prometheus-alertmanager:update-20.04-use-snap into ~ubuntu-docker-images/ubuntu-docker-images/+git/prometheus-alertmanager:0.21-20.04

Proposed by Sergio Durigan Junior
Status: Merged
Merged at revision: d700675a3c1f0983a511287ed5b0178c0bee5b3c
Proposed branch: ~ubuntu-docker-images/ubuntu-docker-images/+git/prometheus-alertmanager:update-20.04-use-snap
Merge into: ~ubuntu-docker-images/ubuntu-docker-images/+git/prometheus-alertmanager:0.21-20.04
Diff against target: 626670 lines (+214/-732)
8 files modified
.gitignore (+1/-4)
Dockerfile (+32/-13)
Makefile (+26/-56)
README.md (+49/-366)
data/prometheus-alertmanager.yaml (+40/-0)
dev/null (+0/-293)
examples/alertmanager-deployment.yml (+65/-0)
promu/promu-0.5.0.linux-amd64/LICENSE (+1/-0)
Reviewer Review Type Date Requested Status
Bryce Harrington Approve
Lucas Kanashiro Pending
Canonical Server Pending
Review via email: mp+399991@code.launchpad.net

Description of the change

Now that the prometheus-alertmanager snap has the 20.04 track, we can use it to build the OCI image.

This MP seems very big, but all it's doing is:

- Remove everything (except the "oci/" directory);
- Move everything inside "oci/" to the root dir;
- Update Dockerfile to use the snap (like we did for 21.04);
- Update the data YAML file to reflect the path change.

Tested by rebuilding and re-running the unit tests.

To post a comment you must log in.
Revision history for this message
Sergio Durigan Junior (sergiodj) wrote :

Asking for a review from Lucas because he reviewed the 21.04 related change.

Revision history for this message
Bryce Harrington (bryce) wrote :

LGTM, +1

You're right this is a sizeable diff, but I focused on visual review of the Dockerfile, Makefile, README.md, and data/prometheus-alertmanager.yaml. All looked pretty standard and consistent with the others. (I did have one question/comment inline below about curl but no changes to suggest.)

I verified locally the branch's Dockerfile builds without issue for me.

review: Approve
Revision history for this message
Sergio Durigan Junior (sergiodj) wrote :

Thanks, Bryce.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.circleci/config.yml b/.circleci/config.yml
2deleted file mode 100644
3index 8a626c5..0000000
4--- a/.circleci/config.yml
5+++ /dev/null
6@@ -1,138 +0,0 @@
7----
8-version: 2.1
9-
10-orbs:
11- prometheus: prometheus/prometheus@0.8.0
12- go: circleci/go@0.2.0
13-
14-jobs:
15- test_frontend:
16- # We need to use a machine executor because the front-end validation runs
17- # containers with mounted volumes which isn't supported with the docker
18- # executor (even with setup_remote_docker).
19- machine: true
20- steps:
21- - checkout
22- - run: sudo service docker restart
23- - run:
24- name: Remove existing Go installation
25- command: sudo rm -rf /usr/local/go
26- # Whenever the Go version is updated here, .promu.yml should also be updated.
27- - go/install:
28- version: "1.14"
29- - run:
30- name: Remove generated code
31- command: make clean
32- - run:
33- name: Generate front-end code
34- command: make all
35- working_directory: ~/project/ui/app
36- environment:
37- JUNIT_DIR: ~/test-results
38- - run:
39- name: Generate assets
40- command: make assets
41- - run:
42- name: Generate API v2 code
43- command: make apiv2
44- - run: git diff --exit-code
45- - store_test_results:
46- path: ~/test-results
47-
48- test:
49- docker:
50- # Whenever the Go version is updated here, .promu.yml should also be updated.
51- - image: circleci/golang:1.14
52- # maildev containers are for running the email tests against a "real" SMTP server.
53- # See notify/email_test.go for details.
54- - image: djfarrelly/maildev@sha256:624e0ec781e11c3531da83d9448f5861f258ee008c1b2da63b3248bfd680acfa
55- name: maildev-noauth
56- entrypoint: bin/maildev
57- command:
58- - -v
59- - image: djfarrelly/maildev@sha256:624e0ec781e11c3531da83d9448f5861f258ee008c1b2da63b3248bfd680acfa
60- name: maildev-auth
61- entrypoint: bin/maildev
62- command:
63- - -v
64- - --incoming-user
65- - user
66- - --incoming-pass
67- - pass
68-
69- environment:
70- EMAIL_NO_AUTH_CONFIG: /tmp/smtp_no_auth.yml
71- EMAIL_AUTH_CONFIG: /tmp/smtp_auth.yml
72-
73- steps:
74- - prometheus/setup_environment
75- - go/load-cache:
76- key: v1-go-mod
77- - run:
78- command: |
79- cat \<<EOF > $EMAIL_NO_AUTH_CONFIG
80- smarthost: maildev-noauth:1025
81- server: http://maildev-noauth:1080/
82- EOF
83- cat \<<EOF > $EMAIL_AUTH_CONFIG
84- smarthost: maildev-auth:1025
85- server: http://maildev-auth:1080/
86- username: user
87- password: pass
88- EOF
89- - run:
90- command: make
91- environment:
92- # By default Go uses GOMAXPROCS but a Circle CI executor has many
93- # cores (> 30) while the CPU and RAM resources are throttled. If we
94- # don't limit this to the number of allocated cores, the job is
95- # likely to get OOMed and killed.
96- GOOPTS: "-p 2"
97- - prometheus/check_proto:
98- version: "3.11.4"
99- - prometheus/store_artifact:
100- file: alertmanager
101- - prometheus/store_artifact:
102- file: amtool
103- - go/save-cache:
104- key: v1-go-mod
105- - store_test_results:
106- path: test-results
107-
108-workflows:
109- version: 2
110- alertmanager:
111- jobs:
112- - test_frontend:
113- filters:
114- tags:
115- only: /.*/
116- - test:
117- filters:
118- tags:
119- only: /.*/
120- - prometheus/build:
121- name: build
122- filters:
123- tags:
124- only: /.*/
125- - prometheus/publish_master:
126- context: org-context
127- requires:
128- - test_frontend
129- - test
130- - build
131- filters:
132- branches:
133- only: master
134- - prometheus/publish_release:
135- context: org-context
136- requires:
137- - test_frontend
138- - test
139- - build
140- filters:
141- tags:
142- only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
143- branches:
144- ignore: /.*/
145diff --git a/.dockerignore b/.dockerignore
146deleted file mode 100644
147index 6e6480b..0000000
148--- a/.dockerignore
149+++ /dev/null
150@@ -1,8 +0,0 @@
151-.build/
152-.tarballs/
153-
154-!.build/linux-amd64/
155-!.build/linux-armv7/
156-!.build/linux-arm64/
157-!.build/linux-ppc64le/
158-!.build/linux-s390x/
159diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
160deleted file mode 100644
161index ce79ffa..0000000
162--- a/.github/ISSUE_TEMPLATE.md
163+++ /dev/null
164@@ -1,47 +0,0 @@
165-<!--
166-
167- Please do *NOT* ask usage questions in Github issues.
168-
169- If your issue is not a feature request or bug report use:
170- https://groups.google.com/forum/#!forum/prometheus-users. If
171- you are unsure whether you hit a bug, search and ask in the
172- mailing list first.
173-
174- You can find more information at: https://prometheus.io/community/
175-
176--->
177-
178-**What did you do?**
179-
180-**What did you expect to see?**
181-
182-**What did you see instead? Under which circumstances?**
183-
184-**Environment**
185-
186-* System information:
187-
188- insert output of `uname -srm` here
189-
190-* Alertmanager version:
191-
192- insert output of `alertmanager --version` here
193-
194-* Prometheus version:
195-
196- insert output of `prometheus --version` here (if relevant to the issue)
197-
198-* Alertmanager configuration file:
199-```
200-insert configuration here
201-```
202-
203-* Prometheus configuration file:
204-```
205-insert configuration here (if relevant to the issue)
206-```
207-
208-* Logs:
209-```
210-insert Prometheus and Alertmanager logs relevant to the issue here
211-```
212diff --git a/.github/stale.yml b/.github/stale.yml
213deleted file mode 100644
214index 66a72af..0000000
215--- a/.github/stale.yml
216+++ /dev/null
217@@ -1,56 +0,0 @@
218-# Configuration for probot-stale - https://github.com/probot/stale
219-
220-# Number of days of inactivity before an Issue or Pull Request becomes stale
221-daysUntilStale: 60
222-
223-# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
224-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
225-daysUntilClose: false
226-
227-# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
228-onlyLabels: []
229-
230-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
231-exemptLabels:
232- - keepalive
233-
234-# Set to true to ignore issues in a project (defaults to false)
235-exemptProjects: false
236-
237-# Set to true to ignore issues in a milestone (defaults to false)
238-exemptMilestones: false
239-
240-# Set to true to ignore issues with an assignee (defaults to false)
241-exemptAssignees: false
242-
243-# Label to use when marking as stale
244-staleLabel: stale
245-
246-# Comment to post when marking as stale. Set to `false` to disable
247-markComment: false
248-
249-# Comment to post when removing the stale label.
250-# unmarkComment: >
251-# Your comment here.
252-
253-# Comment to post when closing a stale Issue or Pull Request.
254-# closeComment: >
255-# Your comment here.
256-
257-# Limit the number of actions per hour, from 1-30. Default is 30
258-limitPerRun: 30
259-
260-# Limit to only `issues` or `pulls`
261-only: pulls
262-
263-# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
264-# pulls:
265-# daysUntilStale: 30
266-# markComment: >
267-# This pull request has been automatically marked as stale because it has not had
268-# recent activity. It will be closed if no further activity occurs. Thank you
269-# for your contributions.
270-
271-# issues:
272-# exemptLabels:
273-# - confirmed
274diff --git a/.gitignore b/.gitignore
275index 205826a..0350652 100644
276--- a/.gitignore
277+++ b/.gitignore
278@@ -1,4 +1 @@
279-/*.snap
280-/parts/
281-/prime/
282-/stage/
283+templates/
284diff --git a/.golangci.yml b/.golangci.yml
285deleted file mode 100644
286index 1a05236..0000000
287--- a/.golangci.yml
288+++ /dev/null
289@@ -1,13 +0,0 @@
290-run:
291- modules-download-mode: vendor
292- deadline: 5m
293-
294-issues:
295- exclude-rules:
296- - path: _test.go
297- linters:
298- - errcheck
299-
300-linters-settings:
301- errcheck:
302- exclude: scripts/errcheck_excludes.txt
303diff --git a/.promu.yml b/.promu.yml
304deleted file mode 100644
305index 1c417c7..0000000
306--- a/.promu.yml
307+++ /dev/null
308@@ -1,48 +0,0 @@
309-go:
310- # Whenever the Go version is updated here, .travis.yml and
311- # .circle/config.yml should also be updated.
312- version: 1.14
313-repository:
314- path: github.com/prometheus/alertmanager
315-build:
316- binaries:
317- - name: alertmanager
318- path: ./cmd/alertmanager
319- - name: amtool
320- path: ./cmd/amtool
321- flags: -mod vendor -a -tags netgo
322- ldflags: |
323- -X github.com/prometheus/common/version.Version={{.Version}}
324- -X github.com/prometheus/common/version.Revision={{.Revision}}
325- -X github.com/prometheus/common/version.Branch={{.Branch}}
326- -X github.com/prometheus/common/version.BuildUser={{user}}@{{host}}
327- -X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}}
328-tarball:
329- files:
330- - examples/ha/alertmanager.yml
331- - LICENSE
332- - NOTICE
333-crossbuild:
334- platforms:
335- - linux/amd64
336- - linux/386
337- - darwin/amd64
338- - darwin/386
339- - windows/amd64
340- - windows/386
341- - freebsd/amd64
342- - freebsd/386
343- - openbsd/amd64
344- - openbsd/386
345- - netbsd/amd64
346- - netbsd/386
347- - dragonfly/amd64
348- - linux/arm
349- - linux/arm64
350- - freebsd/arm
351- - netbsd/arm
352- - linux/ppc64
353- - linux/ppc64le
354- - linux/mips64
355- - linux/mips64le
356- - linux/s390x
357diff --git a/CHANGELOG.md b/CHANGELOG.md
358deleted file mode 100644
359index 93dc0af..0000000
360--- a/CHANGELOG.md
361+++ /dev/null
362@@ -1,564 +0,0 @@
363-## 0.21.0 / 2020-06-16
364-
365-This release removes the HipChat integration as it is discontinued by Atlassian on June 30th 2020.
366-
367-* [CHANGE] [HipChat] Remove HipChat integration as it is end-of-life. #2282
368-* [CHANGE] [amtool] Remove default assignment of environment variables. #2161
369-* [CHANGE] [PagerDuty] Enforce 512KB event size limit. #2225
370-* [ENHANCEMENT] [amtool] Add `cluster` command to show cluster and peer statuses. #2256
371-* [ENHANCEMENT] Add redirection from `/` to the routes prefix when it isn't empty. #2235
372-* [ENHANCEMENT] [Webhook] Add `max_alerts` option to limit the number of alerts included in the payload. #2274
373-* [ENHANCEMENT] Improve logs for API v2, notifications and clustering. #2177 #2188 #2260 #2261 #2273
374-* [BUGFIX] Fix child routes not inheriting their parent route's grouping when `group_by: [...]`. #2154
375-* [BUGFIX] [UI] Fix the receiver selector in the Alerts page when the receiver name contains regular expression metacharacters such as `+`. #2090
376-* [BUGFIX] Fix error message about start and end time validation. #2173
377-* [BUGFIX] Fix a potential race condition in dispatcher. #2208
378-* [BUGFIX] [API v2] Return an empty array of peers when the clustering is disabled. #2203
379-* [BUGFIX] Fix the registration of `alertmanager_dispatcher_aggregation_groups` and `alertmanager_dispatcher_alert_processing_duration_seconds` metrics. #2200
380-* [BUGFIX] Always retry notifications with back-off. #2290
381-
382-## 0.20.0 / 2019-12-11
383-
384-* [CHANGE] Check that at least one silence matcher matches a non-empty string. #2081
385-* [ENHANCEMENT] [pagerduty] Check that PagerDuty keys aren't empty. #2085
386-* [ENHANCEMENT] [template] Add the `stringSlice` function. #2101
387-* [ENHANCEMENT] Add `alertmanager_dispatcher_aggregation_groups` and `alertmanager_dispatcher_alert_processing_duration_seconds` metrics. #2113
388-* [ENHANCEMENT] Log unused receivers. #2114
389-* [ENHANCEMENT] Add `alertmanager_receivers` metric. #2114
390-* [ENHANCEMENT] Add `alertmanager_integrations` metric. #2117
391-* [ENHANCEMENT] [email] Add Message-Id Header to outgoing emails. #2057
392-* [BUGFIX] Don't garbage-collect alerts from the store. #2040
393-* [BUGFIX] [ui] Disable the grammarly plugin on all textareas. #2061
394-* [BUGFIX] [config] Forbid nil regexp matchers. #2083
395-* [BUGFIX] [ui] Fix Silences UI when several filters are applied. #2075
396-
397-Contributors:
398-
399-* @CharlesJUDITH
400-* @NotAFile
401-* @Pger-Y
402-* @TheMeier
403-* @johncming
404-* @n33pm
405-* @ntk148v
406-* @oddlittlebird
407-* @perlun
408-* @qoops-1
409-* @roidelapluie
410-* @simonpasquier
411-* @stephenreddek
412-* @sylr
413-* @vrischmann
414-
415-## 0.19.0 / 2019-09-03
416-
417-* [CHANGE] Reject invalid external URLs at startup. #1960
418-* [CHANGE] Add Fingerprint to template data. #1945
419-* [CHANGE] Check Smarthost validity at config loading. #1957
420-* [ENHANCEMENT] Improve error messages for email receiver. #1953
421-* [ENHANCEMENT] Log error messages from OpsGenie API. #1965
422-* [ENHANCEMENT] Add the ability to configure Slack markdown field. #1967
423-* [ENHANCEMENT] Log warning when repeat_interval > retention. #1993
424-* [ENHANCEMENT] Add `alertmanager_cluster_enabled` metric. #1973
425-* [ENHANCEMENT] [ui] Recreate silence with previous comment. #1927
426-* [BUGFIX] [ui] Fix /api/v2/alerts/groups endpoint with similar alert groups. #1964
427-* [BUGFIX] Allow slashes in receivers. #2011
428-* [BUGFIX] [ui] Fix expand/collapse button with identical alert groups. #2012
429-
430-## 0.18.0 / 2019-07-08
431-
432-* [CHANGE] Remove quantile labels from Summary metrics. #1921
433-* [CHANGE] [OpsGenie] Move from the deprecated `teams` field in the configuration to `responders`. #1863
434-* [CHANGE] [ui] Collapse alert groups on the initial view. #1876
435-* [CHANGE] [Wechat] Set the default API secret to blank. #1888
436-* [CHANGE/BUGFIX] [PagerDuty] Fix embedding of images, the `text` field in the configuration has been renamed to `href`. #1931
437-* [ENHANCEMENT] Use persistent HTTP clients. #1904
438-* [ENHANCEMENT] Add `alertmanager_cluster_alive_messages_total`, `alertmanager_cluster_peer_info` and `alertmanager_cluster_pings_seconds` metrics. #1941
439-* [ENHANCEMENT] [api] Add missing metrics for API v2. #1902
440-* [ENHANCEMENT] [Slack] Log error message on retry errors. #1655
441-* [ENHANCEMENT] [ui] Allow to create silences from the alerts filter bar. #1911
442-* [ENHANCEMENT] [ui] Enable auto resize the textarea fields. #1893
443-* [BUGFIX] [amtool] Use scheme, authentication and base path from the URL if present. #1892 #1940
444-* [BUGFIX] [amtool] Support filtering alerts by receiver. #1915
445-* [BUGFIX] [api] Fix /api/v2/alerts with multiple receivers. #1948
446-* [BUGFIX] [PagerDuty] Truncate description to 1024 chars for PagerDuty v1. #1922
447-* [BUGFIX] [ui] Add filtering based off of "active" query param. #1879
448-
449-
450-## 0.17.0 / 2019-05-02
451-
452-This release includes changes to amtool which are not fully backwards
453-compatible with the previous amtool version (#1798) related to backup and
454-import of silences. If a backup of silences is created using a previous
455-version of amtool (v0.16.1 or earlier), it is possible that not all silences
456-can be correctly imported using a later version of amtool.
457-
458-Additionally, the groups endpoint that was dropped from api v1 has been added
459-to api v2. The default for viewing alerts in the UI now consumes from this
460-endpoint and displays alerts grouped according to the groups defined in the
461-running configuration. Custom grouping is still supported.
462-
463-This release has added two new flags that may need to be tweaked. For people
464-running with a lot of concurrent requests, consider increasing the value of
465-`--web.get-concurrency`. An increase in 503 errors indicates that the request
466-rate is exceeding the number of currently available workers. The other new
467-flag, --web.timeout, limits the time a request is allowed to run. The default
468-behavior is to not use a timeout.
469-
470-* [CHANGE] Modify the self-inhibition prevention semantics (#1873)
471-* [CHANGE] Make api/v2/status.cluster.{name,peers} properties optional for Alertmanager with disabled clustering (#1728)
472-* [FEATURE] Add groups endpoint to v2 api (#1791)
473-* [FEATURE] Optional timeout for HTTP requests (#1743)
474-* [ENHANCEMENT] Set HTTP headers to prevent asset caching (#1817)
475-* [ENHANCEMENT] API returns current silenced/inhibited state of alerts (#1733)
476-* [ENHANCEMENT] Configurable concurrency limit for GET requests (#1743)
477-* [ENHANCEMENT] Pushover notifier: support HTML, URL title and custom sounds (#1634)
478-* [ENHANCEMENT] Support adding custom fields to VictorOps notifications (#1420)
479-* [ENHANCEMENT] Migrate amtool CLI to API v2 (#1798)
480-* [ENHANCEMENT][ui] Default alert list view grouped by configured alert groups (#1864)
481-* [ENHANCEMENT][ui] Remove superfluous inhibited/silenced text, show inhibited status (#1698, #1862)
482-* [ENHANCEMENT][ui] Silence preview now shows already-muted alerts (#1776)
483-* [ENHANCEMENT][ui] Sort silences from api/v2 similarly to api/v1 (#1786)
484-* [BUGFIX] Trim PagerDuty message summary to 1024 chars (#1701)
485-* [BUGFIX] Add fix for race causing alerts to be dropped (#1843)
486-* [BUGFIX][ui] Correctly construct filter query string for api (#1869)
487-* [BUGFIX][ui] Do not display GroupByAll and GroupBy in marshaled config (#1665)
488-* [BUGFIX][ui] Respect regex setting when creating silences (#1697)
489-
490-## 0.16.2 / 2019-04-03
491-
492-Updating to v0.16.2 is recommended for all users using the Slack, Pagerduty,
493-Hipchat, Wechat, VictorOps and Pushover notifier, as connection errors could
494-leak secrets embedded in the notifier's URL to stdout.
495-
496-* [BUGFIX] Redact notifier URL from logs to not leak secrets embedded in the URL (#1822, #1825)
497-* [BUGFIX] Allow sending of unauthenticated SMTP requests when `smtp_auth_username` is not supplied (#1739)
498-
499-## 0.16.1 / 2019-01-31
500-
501-* [BUGFIX] Do not populate cluster info if clustering is disabled in API v2 (#1726)
502-
503-## 0.16.0 / 2019-01-17
504-
505-This release introduces a new API v2, fully generated via the OpenAPI project
506-[1]. At the same time with this release the previous API v1 is being
507-deprecated. API v1 will be removed with Alertmanager release v0.18.0.
508-
509-* [CHANGE] Deprecate API v1
510-* [CHANGE] Remove `api/v1/alerts/groups` GET endpoint (#1508 & #1525)
511-* [CHANGE] Revert Alertmanager working directory changes in Docker image back to `/alertmanager` (#1435)
512-* [CHANGE] Using the recommended label syntax for maintainer in Dockerfile (#1533)
513-* [CHANGE] Change `alertmanager_notifications_total` to count attempted notifications, not only successful ones (#1578)
514-* [CHANGE] Run as nobody inside container (#1586)
515-* [CHANGE] Support `w` for weeks when creating silences, remove `y` for year (#1620)
516-* [FEATURE] Introduce OpenAPI generated API v2 (#1352)
517-* [FEATURE] Lookup parts in strings using regexp.MatchString in templates (#1452)
518-* [FEATURE] Support image/thumb url in attachment in Slack notifier (#1506)
519-* [FEATURE] Support custom TLS certificates for the email notifier (#1528)
520-* [FEATURE] Add support for images and links in the PagerDuty notification config (#1559)
521-* [FEATURE] Add support for grouping by all labels (#1588)
522-* [FEATURE] [amtool] Add timeout support to amtool commands (#1471)
523-* [FEATURE] [amtool] Added `config routes` tools for visualization and testing routes (#1511)
524-* [FEATURE] [amtool] Support adding alerts using amtool (#1461)
525-* [ENHANCEMENT] Add support for --log.format (#1658)
526-* [ENHANCEMENT] Add CORS support to API v2 (#1667)
527-* [ENHANCEMENT] Support HTML, URL title and custom sounds for Pushover (#1634)
528-* [ENHANCEMENT] Update Alert compact view (#1698)
529-* [ENHANCEMENT] Support adding custom fields to VictorOps notifications (#1420)
530-* [ENHANCEMENT] Add help link in UI to Alertmanager documentation (#1522)
531-* [ENHANCEMENT] Enforce HTTP or HTTPS URLs in Alertmanager config (#1567)
532-* [ENHANCEMENT] Make OpsGenie API Key a templated string (#1594)
533-* [ENHANCEMENT] Add name, value and SlackConfirmationField to action in Slack notifier (#1557)
534-* [ENHANCEMENT] Show more alert information on silence form and silence view pages (#1601)
535-* [ENHANCEMENT] Add cluster peers DNS refresh job (#1428)
536-* [BUGFIX] Fix unmarshaling of secret URLs in config (#1663)
537-* [BUGFIX] Do not write groupbyall and groupby when marshaling config (#1665)
538-* [BUGFIX] Make a copy of firing alerts with EndsAt=0 when flushing (#1686)
539-* [BUGFIX] Respect regex matchers when recreating silences in UI (#1697)
540-* [BUGFIX] Change DefaultGlobalConfig to a function in Alertmanager configuration (#1656)
541-* [BUGFIX] Fix email template typo in alert-warning style (#1421)
542-* [BUGFIX] Fix silence redirect on silence creation UI page (#1548)
543-* [BUGFIX] Add missing `callback_id` parameter in Slack notifier (#1592)
544-* [BUGFIX] Throw error if no auth mechanism matches in email notifier (#1608)
545-* [BUGFIX] Use quoted-printable transfer encoding for the email notifier (#1609)
546-* [BUGFIX] Do not merge expired gossip messages (#1631)
547-* [BUGFIX] Fix "PLAIN" auth during notification via smtp-over-tls on port 465 (#1591)
548-* [BUGFIX] [amtool] Support for assuming first label is alertname in silence add and query (#1693)
549-* [BUGFIX] [amtool] Support assuming first label is alertname in alert query with matchers (#1575)
550-* [BUGFIX] [amtool] Fix config path check in amtool (#1538)
551-* [BUGFIX] [amtool] Fix rfc3339 example texts (#1526)
552-* [BUGFIX] [amtool] Fixed issue with loading path of a default configs (#1529)
553-
554-[1] https://github.com/prometheus/alertmanager#api
555-
556-## 0.15.3 / 2018-11-09
557-
558-* [BUGFIX] Fix alert merging supporting both empty and set EndsAt property for firing alerts send by Prometheus (#1611)
559-
560-## 0.15.2 / 2018-08-14
561-
562-* [ENHANCEMENT] [amtool] Add support for stdin to check-config (#1431)
563-* [ENHANCEMENT] Log PagerDuty v1 response on BadRequest (#1481)
564-* [BUGFIX] Correctly encode query strings in notifiers (#1516)
565-* [BUGFIX] Add cache control headers to the API responses to avoid IE caching (#1500)
566-* [BUGFIX] Avoid listener blocking on unsubscribe (#1482)
567-* [BUGFIX] Fix a bunch of unhandled errors (#1501)
568-* [BUGFIX] Update PagerDuty API V2 to send full details on resolve (#1483)
569-* [BUGFIX] Validate URLs at config load time (#1468)
570-* [BUGFIX] Fix Settle() interval (#1478)
571-* [BUGFIX] Fix email to be green if only none firing (#1475)
572-* [BUGFIX] Handle errors in notify (#1474)
573-* [BUGFIX] Fix templating of hipchat room id (#1463)
574-
575-## 0.15.1 / 2018-07-10
576-
577-* [BUGFIX] Fix email template typo in alert-warning style (#1421)
578-* [BUGFIX] Fix regression in Pager Duty config (#1455)
579-* [BUGFIX] Catch templating errors in Wechat Notify (#1436)
580-* [BUGFIX] Fail when no private address can be found for cluster (#1437)
581-* [BUGFIX] Make sure we don't miss the first pushPull when joining cluster (#1456)
582-* [BUGFIX] Fix concurrent read and write group error in dispatch (#1447)
583-
584-## 0.15.0 / 2018-06-22
585-
586-* [CHANGE] [amtool] Update silence add and update flags (#1298)
587-* [CHANGE] Replace deprecated InstrumentHandler() (#1302)
588-* [CHANGE] Validate Slack field config and only allow the necessary input (#1334)
589-* [CHANGE] Remove legacy alert ingest endpoint (#1362)
590-* [CHANGE] Move to memberlist as underlying gossip protocol including cluster flag changes from --mesh.xxx to --cluster.xxx (#1232)
591-* [CHANGE] Move Alertmanager working directory in Docker image to /etc/alertmanager (#1313)
592-* [BUGFIX/CHANGE] The default group by is no labels. (#1287)
593-* [FEATURE] [amtool] Filter alerts by receiver (#1402)
594-* [FEATURE] Wait for mesh to settle before sending alerts (#1209)
595-* [FEATURE] [amtool] Support basic auth in alertmanager url (#1279)
596-* [FEATURE] Make HTTP clients used for integrations configurable
597-* [ENHANCEMENT] Support receiving alerts with end time and zero start time
598-* [ENHANCEMENT] Sort dispatched alerts by job+instance (#1234)
599-* [ENHANCEMENT] Support alert query filters `active` and `unprocessed` (#1366)
600-* [ENHANCEMENT] [amtool] Expose alert query flags --active and --unprocessed (#1370)
601-* [ENHANCEMENT] Add Slack actions to notifications (#1355)
602-* [BUGFIX] Register nflog snapShotSize metric
603-* [BUGFIX] Sort alerts in correct order before flushing to notifiers (#1349)
604-* [BUGFIX] Don't reset initial wait timer if flush is in-progress (#1301)
605-* [BUGFIX] Fix resolved alerts still inhibiting (#1331)
606-* [BUGFIX] Template wechat config fields (#1356)
607-* [BUGFIX] Notify resolved alerts properly (#1408)
608-* [BUGFIX] Fix parsing for label values with commas (#1395)
609-* [BUGFIX] Hide sensitive Wechat configuration (#1253)
610-* [BUGFIX] Prepopulate matchers when recreating a silence (#1270)
611-* [BUGFIX] Fix wechat panic (#1293)
612-* [BUGFIX] Allow empty matchers in silences/filtering (#1289)
613-* [BUGFIX] Properly configure HTTP client for Wechat integration
614-
615-## 0.14.0 / 2018-02-12
616-
617-* [ENHANCEMENT] [amtool] Silence update support dwy suffixes to expire flag (#1197)
618-* [ENHANCEMENT] Allow templating PagerDuty receiver severity (#1214)
619-* [ENHANCEMENT] Include receiver name in failed notifications log messages (#1207)
620-* [ENHANCEMENT] Allow global opsgenie api key (#1208)
621-* [ENHANCEMENT] Add mesh metrics (#1225)
622-* [ENHANCEMENT] Add Class field to PagerDuty; add templating to PagerDuty-CEF fields (#1231)
623-* [BUGFIX] Don't notify of resolved alerts if none were reported firing (#1198)
624-* [BUGFIX] Notify only when new firing alerts are added (#1205)
625-* [BUGFIX] [mesh] Fix pending connections never set to established (#1204)
626-* [BUGFIX] Allow OpsGenie notifier to have empty team fields (#1224)
627-* [BUGFIX] Don't count alerts with EndTime in the future as resolved (#1233)
628-* [BUGFIX] Speed up re-rendering of Silence UI (#1235)
629-* [BUGFIX] Forbid 0 value for group_interval and repeat_interval (#1230)
630-* [BUGFIX] Fix WeChat agentid issue (#1229)
631-
632-## 0.13.0 / 2018-01-12
633-
634-* [CHANGE] Switch cmd/alertmanager to kingpin (#974)
635-* [CHANGE] [amtool] Switch amtool to kingpin (#976)
636-* [CHANGE] [amtool] silence query: --expired flag only shows expired silences (#1190)
637-* [CHANGE] Return config reload result from reload endpoint (#1180)
638-* [FEATURE] UI silence form is populated from location bar (#1148)
639-* [FEATURE] Add /-/healthy endpoint (#1159)
640-* [ENHANCEMENT] Instrument and log snapshot sizes on maintenance (#1155)
641-* [ENHANCEMENT] Make alertGC interval configurable (#1151)
642-* [ENHANCEMENT] Display mesh connections in the Status page (#1164)
643-* [BUGFIX] Template service keys for pagerduty notifier (#1182)
644-* [BUGFIX] Fix expire buttons on the silences page (#1171)
645-* [BUGFIX] Fix JavaScript error in MSIE due to endswith() usage (#1172)
646-* [BUGFIX] Correctly format UI error output (#1167)
647-
648-## 0.12.0 / 2017-12-15
649-
650-* [FEATURE] package amtool in docker container (#1127)
651-* [FEATURE] Add notify support for Chinese User wechat (#1059)
652-* [FEATURE] [amtool] Add a new `silence import` command (#1082)
653-* [FEATURE] [amtool] Add new command to update silence (#1123)
654-* [FEATURE] [amtool] Add ability to query for silences that will expire soon (#1120)
655-* [ENHANCEMENT] Template source field in PagerDuty alert payload (#1117)
656-* [ENHANCEMENT] Add footer field for slack messages (#1141)
657-* [ENHANCEMENT] Add Slack additional "fields" to notifications (#1135)
658-* [ENHANCEMENT] Adding check for webhook's URL formatting (#1129)
659-* [ENHANCEMENT] Let the browser remember the creator of a silence (#1112)
660-* [BUGFIX] Fix race in stopping inhibitor (#1118)
661-* [BUGFIX] Fix browser UI when entering negative duration (#1132)
662-
663-## 0.11.0 / 2017-11-16
664-
665-* [CHANGE] Make silence negative filtering consistent with alert filtering (#1095)
666-* [CHANGE] Change HipChat and OpsGenie api config names (#1087)
667-* [ENHANCEMENT] amtool: Allow 'd', 'w', 'y' time suffixes when creating silence (#1091)
668-* [ENHANCEMENT] Support OpsGenie Priority field (#1094)
669-* [BUGFIX] Fix UI when no silences are present (#1090)
670-* [BUGFIX] Fix OpsGenie Teams field (#1101)
671-* [BUGFIX] Fix OpsGenie Tags field (#1108)
672-
673-## 0.10.0 / 2017-11-09
674-
675-* [CHANGE] Prevent inhibiting alerts in the source of the inhibition (#1017)
676-* [ENHANCEMENT] Improve amtool check-config use and description text (#1016)
677-* [ENHANCEMENT] Add metrics about current silences and alerts (#998)
678-* [ENHANCEMENT] Sorted silences based on current status (#1015)
679-* [ENHANCEMENT] Add metric of alertmanager position in mesh (#1024)
680-* [ENHANCEMENT] Initialise notifications_total and notifications_failed_total (#1011)
681-* [ENHANCEMENT] Allow selectable matchers on silence view (#1030)
682-* [ENHANCEMENT] Allow template in victorops message_type field (#1038)
683-* [ENHANCEMENT] Optionally hide inhibited alerts in API response (#1039)
684-* [ENHANCEMENT] Toggle silenced and inhibited alerts in UI (#1049)
685-* [ENHANCEMENT] Fix pushover limits (title, message, url) (#1055)
686-* [ENHANCEMENT] Add limit to OpsGenie message (#1045)
687-* [ENHANCEMENT] Upgrade OpsGenie notifier to v2 API. (#1061)
688-* [ENHANCEMENT] Allow template in victorops routing_key field (#1083)
689-* [ENHANCEMENT] Add support for PagerDuty API v2 (#1054)
690-* [BUGFIX] Fix inhibit race (#1032)
691-* [BUGFIX] Fix segfault on amtool (#1031)
692-* [BUGFIX] Remove .WasInhibited and .WasSilenced fields of Alert type (#1026)
693-* [BUGFIX] nflog: Fix Log() crash when gossip is nil (#1064)
694-* [BUGFIX] Fix notifications for flapping alerts (#1071)
695-* [BUGFIX] Fix shutdown crash with nil mesh router (#1077)
696-* [BUGFIX] Fix negative matchers filtering (#1077)
697-
698-## 0.9.1 / 2017-09-29
699-* [BUGFIX] Fix -web.external-url regression in ui (#1008)
700-* [BUGFIX] Fix multipart email implementation (#1009)
701-
702-## 0.9.0 / 2017-09-28
703-* [ENHANCEMENT] Add current time to webhook message (#909)
704-* [ENHANCEMENT] Add link_names to slack notifier (#912)
705-* [ENHANCEMENT] Make ui labels selectable/highlightable (#932)
706-* [ENHANCEMENT] Make links in ui annotations selectable (#946)
707-* [ENHANCEMENT] Expose the alert's "fingerprint" (unique identifier) through API (#786)
708-* [ENHANCEMENT] Add README information for amtool (#939)
709-* [ENHANCEMENT] Use user-set logging option consistently throughout alertmanager (#968)
710-* [ENHANCEMENT] Sort alerts returned from API by their fingerprint (#969)
711-* [ENHANCEMENT] Add edit/delete silence buttons on silence page view (#970)
712-* [ENHANCEMENT] Add check-config subcommand to amtool (#978)
713-* [ENHANCEMENT] Add email notification text content support (#934)
714-* [ENHANCEMENT] Support passing binary name to make build target (#990)
715-* [ENHANCEMENT] Show total no. of silenced alerts in preview (#994)
716-* [ENHANCEMENT] Added confirmation dialog when expiring silences (#993)
717-* [BUGFIX] Fix crash when no mesh router is configured (#919)
718-* [BUGFIX] Render status page without mesh (#920)
719-* [BUGFIX] Exit amtool subcommands with non-zero error code (#938)
720-* [BUGFIX] Change mktemp invocation in makefile to work for macOS (#971)
721-* [BUGFIX] Add a mutex to silences.go:gossipData (#984)
722-* [BUGFIX] silences: avoid deadlock (#995)
723-* [BUGFIX] Ignore expired silences OnGossip (#999)
724-
725-## 0.8.0 / 2017-07-20
726-
727-* [FEATURE] Add ability to filter alerts by receiver in the UI (#890)
728-* [FEATURE] Add User-Agent for webhook requests (#893)
729-* [ENHANCEMENT] Add possibility to have a global victorops api_key (#897)
730-* [ENHANCEMENT] Add EntityDisplayName and improve StateMessage for Victorops
731- (#769)
732-* [ENHANCEMENT] Omit empty config fields and show regex upon re-marshaling to
733- elide secrets (#864)
734-* [ENHANCEMENT] Parse API error messages in UI (#866)
735-* [ENHANCEMENT] Enable sending mail via smtp port 465 (#704)
736-* [BUGFIX] Prevent duplicate notifications by sorting matchers (#882)
737-* [BUGFIX] Remove timeout for UI requests (#890)
738-* [BUGFIX] Update config file location of CLI in flag usage text (#895)
739-
740-## 0.7.1 / 2017-06-09
741-
742-* [BUGFIX] Fix filtering by label on Alert list and Silence list page
743-
744-## 0.7.0 / 2017-06-08
745-
746-* [CHANGE] Rewrite UI from scratch improving UX
747-* [CHANGE] Rename `config` to `configYAML` on `api/v1/status`
748-* [FEATURE] Add ability to update a silence on `api/v1/silences` POST endpoint (See #765)
749-* [FEATURE] Return alert status on `api/v1/alerts` GET endpoint
750-* [FEATURE] Serve silence state on `api/v1/silences` GET endpoint
751-* [FEATURE] Add ability to specify a route prefix
752-* [FEATURE] Add option to disable AM listening on mesh port
753-* [ENHANCEMENT] Add ability to specify `filter` string and `silenced` flag on `api/v1/alerts` GET endpoint
754-* [ENHANCEMENT] Update `cache-control` to prevent caching for web assets in general.
755-* [ENHANCEMENT] Serve web assets by alertmanager instead of external CDN (See #846)
756-* [ENHANCEMENT] Elide secrets in alertmanager config (See #840)
757-* [ENHANCEMENT] AMTool: Move config file to a more consistent location (See #843)
758-* [BUGFIX] Enable builds for Solaris/Illumos
759-* [BUGFIX] Load web assets based on url path (See #323)
760-
761-## 0.6.2 / 2017-05-09
762-
763-* [BUGFIX] Correctly link to silences from alert again
764-* [BUGFIX] Correctly hide silenced/show active alerts in UI again
765-* [BUGFIX] Fix regression of alerts not being displayed until first processing
766-* [BUGFIX] Fix internal usage of wrong lock for silence markers
767-* [BUGFIX] Adapt amtool's API parsing to recent API changes
768-* [BUGFIX] Correctly marshal regexes in config JSON response
769-* [CHANGE] Anchor silence regex matchers to be consistent with Prometheus
770-* [ENHANCEMENT] Error if root route is using `continue` keyword
771-
772-## 0.6.1 / 2017-04-28
773-
774-* [BUGFIX] Fix incorrectly serialized hash for notification providers.
775-* [ENHANCEMENT] Add processing status field to alerts.
776-* [FEATURE] Add config hash metric.
777-
778-## 0.6.0 / 2017-04-25
779-
780-* [BUGFIX] Add `groupKey` to `alerts/groups` endpoint https://github.com/prometheus/alertmanager/pull/576
781-* [BUGFIX] Only notify on firing alerts https://github.com/prometheus/alertmanager/pull/595
782-* [BUGFIX] Correctly marshal regex's in config for routing tree https://github.com/prometheus/alertmanager/pull/602
783-* [BUGFIX] Prevent panic when failing to load config https://github.com/prometheus/alertmanager/pull/607
784-* [BUGFIX] Prevent panic when alertmanager is started with an empty `-mesh.peer` https://github.com/prometheus/alertmanager/pull/726
785-* [CHANGE] Rename VictorOps config variables https://github.com/prometheus/alertmanager/pull/667
786-* [CHANGE] No longer generate releases for openbsd/arm https://github.com/prometheus/alertmanager/pull/732
787-* [ENHANCEMENT] Add `DELETE` as accepted CORS method https://github.com/prometheus/alertmanager/commit/0ecc59076ca6b4cbb63252fa7720a3d89d1c81d3
788-* [ENHANCEMENT] Switch to using `gogoproto` for protobuf https://github.com/prometheus/alertmanager/pull/715
789-* [ENHANCEMENT] Include notifier type in logs and errors https://github.com/prometheus/alertmanager/pull/702
790-* [FEATURE] Expose mesh peers on status page https://github.com/prometheus/alertmanager/pull/644
791-* [FEATURE] Add `reReplaceAll` template function https://github.com/prometheus/alertmanager/pull/639
792-* [FEATURE] Allow label-based filtering alerts/silences through API https://github.com/prometheus/alertmanager/pull/633
793-* [FEATURE] Add commandline tool for interacting with alertmanager https://github.com/prometheus/alertmanager/pull/636
794-
795-## 0.5.1 / 2016-11-24
796-
797-* [BUGFIX] Fix crash caused by race condition in silencing
798-* [ENHANCEMENT] Improve logging of API errors
799-* [ENHANCEMENT] Add metrics for the notification log
800-
801-## 0.5.0 / 2016-11-01
802-
803-This release requires a storage wipe. It contains fundamental internal
804-changes that came with implementing the high availability mode.
805-
806-* [FEATURE] Alertmanager clustering for high availability
807-* [FEATURE] Garbage collection of old silences and notification logs
808-* [CHANGE] New storage format
809-* [CHANGE] Stricter silence semantics for consistent historical view
810-
811-## 0.4.2 / 2016-09-02
812-
813-* [BUGFIX] Fix broken regex checkbox in silence form
814-* [BUGFIX] Simplify inconsistent silence update behavior
815-
816-## 0.4.1 / 2016-08-31
817-
818-* [BUGFIX] Wait for silence query to finish instead of showing error
819-* [BUGFIX] Fix sorting of silences
820-* [BUGFIX] Provide visual feedback after creating a silence
821-* [BUGFIX] Fix styling of silences
822-* [ENHANCEMENT] Provide cleaner API silence interface
823-
824-## 0.4.0 / 2016-08-23
825-
826-* [FEATURE] Silences are now paginated in the web ui
827-* [CHANGE] Failure to start on unparsed flags
828-
829-## 0.3.0 / 2016-07-07
830-
831-* [CHANGE] Alerts are purely in memory and no longer persistent across restarts
832-* [FEATURE] Add SMTP LOGIN authentication mechanism
833-
834-## 0.2.1 / 2016-06-23
835-
836-* [ENHANCEMENT] Allow inheritance of route receiver
837-* [ENHANCEMENT] Add silence cache to silence provider
838-* [BUGFIX] Fix HipChat room number in integration URL
839-
840-## 0.2.0 / 2016-06-17
841-
842-This release uses a new storage backend based on BoltDB. You have to backup
843-and wipe your former storage path to run it.
844-
845-* [CHANGE] Use BoltDB as data store.
846-* [CHANGE] Move SMTP authentication to configuration file
847-* [FEATURE] add /-/reload HTTP endpoint
848-* [FEATURE] Filter silenced alerts in web UI
849-* [ENHANCEMENT] reduce inhibition computation complexity
850-* [ENHANCEMENT] Add support for teams and tags in OpsGenie integration
851-* [BUGFIX] Handle OpsGenie responses correctly
852-* [BUGFIX] Fix Pushover queue length issue
853-* [BUGFIX] STARTTLS before querying auth mechanism in email integration
854-
855-## 0.1.1 / 2016-03-15
856-* [BUGFIX] Fix global database lock issue
857-* [ENHANCEMENT] Improve SQLite alerts index
858-* [ENHANCEMENT] Enable debug endpoint
859-
860-## 0.1.0 / 2016-02-23
861-This version is a full rewrite of the Alertmanager with a very different
862-feature set. Thus, there is no meaningful changelog.
863-
864-Changes with respect to 0.1.0-beta2:
865-* [CHANGE] Expose same data structure to templates and webhook
866-* [ENHANCEMENT] Show generator URL in default templates and web UI
867-* [ENHANCEMENT] Support for Slack icon_emoji field
868-* [ENHANCEMENT] Expose incident key to templates and webhook data
869-* [ENHANCEMENT] Allow markdown in Slack 'text' field
870-* [BUGFIX] Fixed database locking issue
871-
872-## 0.1.0-beta2 / 2016-02-03
873-* [BUGFIX] Properly set timeout for incoming alerts with fixed start time
874-* [ENHANCEMENT] Send source field in OpsGenie integration
875-* [ENHANCEMENT] Improved routing configuration validation
876-* [FEATURE] Basic instrumentation added
877-
878-## 0.1.0-beta1 / 2016-01-08
879-* [BUGFIX] Send full alert group state on each update. Fixes erroneous resolved notifications.
880-* [FEATURE] HipChat integration
881-* [CHANGE] Slack integration no longer sends resolved notifications by default
882-
883-## 0.1.0-beta0 / 2015-12-23
884-This version is a full rewrite of the Alertmanager with a very different
885-feature set. Thus, there is no meaningful changelog.
886-
887-## 0.0.4 / 2015-09-09
888-* [BUGFIX] Fix version info string in startup message.
889-* [BUGFIX] Fix Pushover notifications by setting the right priority level, as
890- well as required retry and expiry intervals.
891-* [FEATURE] Make it possible to link to individual alerts in the UI.
892-* [FEATURE] Rearrange alert columns in UI and allow expanding more alert details.
893-* [FEATURE] Add Amazon SNS notifications.
894-* [FEATURE] Add OpsGenie Webhook notifications.
895-* [FEATURE] Add `-web.external-url` flag to control the externally visible
896- Alertmanager URL.
897-* [FEATURE] Add runbook and alertmanager URLs to PagerDuty and email notifications.
898-* [FEATURE] Add a GET API to /api/alerts which pulls JSON formatted
899- AlertAggregates.
900-* [ENHANCEMENT] Sort alerts consistently in web UI.
901-* [ENHANCEMENT] Suggest to use email address as silence creator.
902-* [ENHANCEMENT] Make Slack timeout configurable.
903-* [ENHANCEMENT] Add channel name to error logging about Slack notifications.
904-* [ENHANCEMENT] Refactoring and tests for Flowdock notifications.
905-* [ENHANCEMENT] New Dockerfile using alpine-golang-make-onbuild base image.
906-* [CLEANUP] Add Docker instructions and other cleanups in README.md.
907-* [CLEANUP] Update Makefile.COMMON from prometheus/utils.
908-
909-## 0.0.3 / 2015-06-10
910-* [BUGFIX] Fix email template body writer being called with parameters in wrong order.
911-
912-## 0.0.2 / 2015-06-09
913-
914-* [BUGFIX] Fixed silences.json permissions in Docker image.
915-* [CHANGE] Changed case of API JSON properties to initial lower letter.
916-* [CHANGE] Migrated logging to use http://github.com/prometheus/log.
917-* [FEATURE] Flowdock notification support.
918-* [FEATURE] Slack notification support.
919-* [FEATURE] Generic webhook notification support.
920-* [FEATURE] Support for "@"-mentions in HipChat notifications.
921-* [FEATURE] Path prefix option to support reverse proxies.
922-* [ENHANCEMENT] Improved web redirection and 404 behavior.
923-* [CLEANUP] Updated compiled web assets from source.
924-* [CLEANUP] Updated fsnotify package to its new source location.
925-* [CLEANUP] Updates to README.md and AUTHORS.md.
926-* [CLEANUP] Various smaller cleanups and improvements.
927diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
928deleted file mode 100644
929index af2570c..0000000
930--- a/COPYRIGHT.txt
931+++ /dev/null
932@@ -1,12 +0,0 @@
933-Copyright Prometheus Team
934-Licensed under the Apache License, Version 2.0 (the "License");
935-you may not use this file except in compliance with the License.
936-You may obtain a copy of the License at
937-
938-http://www.apache.org/licenses/LICENSE-2.0
939-
940-Unless required by applicable law or agreed to in writing, software
941-distributed under the License is distributed on an "AS IS" BASIS,
942-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
943-See the License for the specific language governing permissions and
944-limitations under the License.
945diff --git a/Dockerfile b/Dockerfile
946index 7d98d53..94e2ab5 100644
947--- a/Dockerfile
948+++ b/Dockerfile
949@@ -1,21 +1,40 @@
950-ARG ARCH="amd64"
951-ARG OS="linux"
952-FROM quay.io/prometheus/busybox-${OS}-${ARCH}:latest
953-LABEL maintainer="The Prometheus Authors <prometheus-developers@googlegroups.com>"
954+FROM ubuntu:focal AS snap-installer
955
956-ARG ARCH="amd64"
957-ARG OS="linux"
958-COPY .build/${OS}-${ARCH}/amtool /bin/amtool
959-COPY .build/${OS}-${ARCH}/alertmanager /bin/alertmanager
960-COPY examples/ha/alertmanager.yml /etc/alertmanager/alertmanager.yml
961+RUN set -eux; \
962+ apt-get update; \
963+ DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y; \
964+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
965+ jq curl ca-certificates squashfs-tools; \
966+# taken from https://snapcraft.io/docs/build-on-docker
967+# Alternatively, we can install snapd, and issue `snap download prometheus-alertmanager`
968+ curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/prometheus-alertmanager?channel=20.04/edge' | jq '.download_url' -r) --output prometheus-alertmanager.snap; \
969+ mkdir -p /snap; \
970+ unsquashfs -d /snap/prometheus-alertmanager prometheus-alertmanager.snap
971
972-RUN mkdir -p /alertmanager && \
973- chown -R nobody:nogroup etc/alertmanager /alertmanager
974+FROM ubuntu:focal
975
976-USER nobody
977+ENV TZ UTC
978+
979+RUN set -eux; \
980+ apt-get update; \
981+ DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y; \
982+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
983+ tzdata; \
984+ rm -rf /var/lib/apt/lists/*; \
985+ mkdir -p /alertmanager
986+
987+COPY --from=snap-installer /snap/prometheus-alertmanager/bin/alertmanager /usr/bin/alertmanager
988+COPY --from=snap-installer /snap/prometheus-alertmanager/bin/amtool /usr/bin/amtool
989+COPY --from=snap-installer /snap/prometheus-alertmanager/etc/prometheus/alertmanager.yml.example /etc/alertmanager/alertmanager.yml
990+
991+# Copy the manifest files from the snap
992+COPY --from=snap-installer /snap/prometheus-alertmanager/snap/snapcraft.yaml /usr/share/rocks/
993+COPY --from=snap-installer /snap/prometheus-alertmanager/snap/manifest.yaml /usr/share/rocks/
994+
995+# Expose port, configure volume and define the entrypoint
996 EXPOSE 9093
997 VOLUME [ "/alertmanager" ]
998 WORKDIR /alertmanager
999-ENTRYPOINT [ "/bin/alertmanager" ]
1000+ENTRYPOINT [ "/usr/bin/alertmanager" ]
1001 CMD [ "--config.file=/etc/alertmanager/alertmanager.yml", \
1002 "--storage.path=/alertmanager" ]
1003diff --git a/oci/HACKING.md b/HACKING.md
1004similarity index 100%
1005rename from oci/HACKING.md
1006rename to HACKING.md
1007diff --git a/MAINTAINERS.md b/MAINTAINERS.md
1008deleted file mode 100644
1009index e45f5e2..0000000
1010--- a/MAINTAINERS.md
1011+++ /dev/null
1012@@ -1 +0,0 @@
1013-* Simon Pasquier <pasquier.simon@gmail.com>
1014diff --git a/Makefile b/Makefile
1015index 3611908..b0bede0 100644
1016--- a/Makefile
1017+++ b/Makefile
1018@@ -1,56 +1,26 @@
1019-# Copyright 2015 The Prometheus Authors
1020-# Licensed under the Apache License, Version 2.0 (the "License");
1021-# you may not use this file except in compliance with the License.
1022-# You may obtain a copy of the License at
1023-#
1024-# http://www.apache.org/licenses/LICENSE-2.0
1025-#
1026-# Unless required by applicable law or agreed to in writing, software
1027-# distributed under the License is distributed on an "AS IS" BASIS,
1028-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1029-# See the License for the specific language governing permissions and
1030-# limitations under the License.
1031-
1032-# Needs to be defined before including Makefile.common to auto-generate targets
1033-DOCKER_ARCHS ?= amd64 armv7 arm64 ppc64le s390x
1034-
1035-include Makefile.common
1036-
1037-FRONTEND_DIR = $(BIN_DIR)/ui/app
1038-DOCKER_IMAGE_NAME ?= alertmanager
1039-
1040-STATICCHECK_IGNORE =
1041-
1042-.PHONY: build-all
1043-# Will build both the front-end as well as the back-end
1044-build-all: assets apiv2 build
1045-
1046-.PHONY: assets
1047-assets: asset/assets_vfsdata.go
1048-
1049-asset/assets_vfsdata.go: ui/app/script.js ui/app/index.html ui/app/lib template/default.tmpl
1050- GO111MODULE=$(GO111MODULE) $(GO) generate $(GOOPTS) ./asset
1051- @$(GOFMT) -w ./asset
1052-
1053-ui/app/script.js: $(shell find ui/app/src -iname *.elm) api/v2/openapi.yaml
1054- cd $(FRONTEND_DIR) && $(MAKE) script.js
1055-
1056-.PHONY: apiv2
1057-apiv2: api/v2/models api/v2/restapi api/v2/client
1058-
1059-SWAGGER = docker run \
1060- --user=$(shell id -u $(USER)):$(shell id -g $(USER)) \
1061- --rm \
1062- -v $(shell pwd):/go/src/github.com/prometheus/alertmanager \
1063- -w /go/src/github.com/prometheus/alertmanager quay.io/goswagger/swagger:v0.20.1
1064-
1065-api/v2/models api/v2/restapi api/v2/client: api/v2/openapi.yaml
1066- -rm -r api/v2/{client,models,restapi}
1067- $(SWAGGER) generate server -f api/v2/openapi.yaml --copyright-file=COPYRIGHT.txt --exclude-main -A alertmanager --target api/v2/
1068- $(SWAGGER) generate client -f api/v2/openapi.yaml --copyright-file=COPYRIGHT.txt --skip-models --target api/v2
1069-
1070-.PHONY: clean
1071-clean:
1072- - @rm -rf asset/assets_vfsdata.go \
1073- api/v2/models api/v2/restapi api/v2/client
1074- - @cd $(FRONTEND_DIR) && $(MAKE) clean
1075+RENDERDOWN ?= ../RenderDown/renderdown.py
1076+PYTHON ?= python3
1077+
1078+README_TEMPLATE = templates/README_DOCKERHUB.md
1079+
1080+HACKING_TEMPLATE = templates/HACKING_upstream.md
1081+
1082+all: all-doc
1083+
1084+all-doc: clean-doc readme
1085+
1086+get-templates:
1087+ wget -nv -e "robots=off" -nd -r -np -P templates https://git.launchpad.net/~canonical-server/ubuntu-docker-images/+git/templates/plain/templates/
1088+
1089+readme: get-templates
1090+ mv -v $(README_TEMPLATE) templates/README.md
1091+ mv -v $(HACKING_TEMPLATE) templates/HACKING.md
1092+ $(PYTHON) $(RENDERDOWN) templates/README.md > README.md
1093+ $(PYTHON) $(RENDERDOWN) templates/HACKING.md > HACKING.md
1094+
1095+clean: clean-doc
1096+
1097+clean-doc:
1098+ rm -frv templates/ README.md
1099+
1100+.PHONY: readme clean clean-doc all all-doc get-templates
1101diff --git a/Makefile.common b/Makefile.common
1102deleted file mode 100644
1103index b1dcf89..0000000
1104--- a/Makefile.common
1105+++ /dev/null
1106@@ -1,296 +0,0 @@
1107-# Copyright 2018 The Prometheus Authors
1108-# Licensed under the Apache License, Version 2.0 (the "License");
1109-# you may not use this file except in compliance with the License.
1110-# You may obtain a copy of the License at
1111-#
1112-# http://www.apache.org/licenses/LICENSE-2.0
1113-#
1114-# Unless required by applicable law or agreed to in writing, software
1115-# distributed under the License is distributed on an "AS IS" BASIS,
1116-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1117-# See the License for the specific language governing permissions and
1118-# limitations under the License.
1119-
1120-
1121-# A common Makefile that includes rules to be reused in different prometheus projects.
1122-# !!! Open PRs only against the prometheus/prometheus/Makefile.common repository!
1123-
1124-# Example usage :
1125-# Create the main Makefile in the root project directory.
1126-# include Makefile.common
1127-# customTarget:
1128-# @echo ">> Running customTarget"
1129-#
1130-
1131-# Ensure GOBIN is not set during build so that promu is installed to the correct path
1132-unexport GOBIN
1133-
1134-GO ?= go
1135-GOFMT ?= $(GO)fmt
1136-FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
1137-GOOPTS ?=
1138-GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
1139-GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
1140-
1141-GO_VERSION ?= $(shell $(GO) version)
1142-GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
1143-PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.')
1144-
1145-GOVENDOR :=
1146-GO111MODULE :=
1147-ifeq (, $(PRE_GO_111))
1148- ifneq (,$(wildcard go.mod))
1149- # Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI).
1150- GO111MODULE := on
1151-
1152- ifneq (,$(wildcard vendor))
1153- # Always use the local vendor/ directory to satisfy the dependencies.
1154- GOOPTS := $(GOOPTS) -mod=vendor
1155- endif
1156- endif
1157-else
1158- ifneq (,$(wildcard go.mod))
1159- ifneq (,$(wildcard vendor))
1160-$(warning This repository requires Go >= 1.11 because of Go modules)
1161-$(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)')
1162- endif
1163- else
1164- # This repository isn't using Go modules (yet).
1165- GOVENDOR := $(FIRST_GOPATH)/bin/govendor
1166- endif
1167-endif
1168-PROMU := $(FIRST_GOPATH)/bin/promu
1169-pkgs = ./...
1170-
1171-ifeq (arm, $(GOHOSTARCH))
1172- GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM)
1173- GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM)
1174-else
1175- GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)
1176-endif
1177-
1178-GOTEST := $(GO) test
1179-GOTEST_DIR :=
1180-ifneq ($(CIRCLE_JOB),)
1181-ifneq ($(shell which gotestsum),)
1182- GOTEST_DIR := test-results
1183- GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml --
1184-endif
1185-endif
1186-
1187-PROMU_VERSION ?= 0.5.0
1188-
1189-GOLANGCI_LINT :=
1190-GOLANGCI_LINT_OPTS ?=
1191-GOLANGCI_LINT_VERSION ?= v1.18.0
1192-# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
1193-# windows isn't included here because of the path separator being different.
1194-ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
1195- ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386))
1196- GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint
1197- endif
1198-endif
1199-
1200-PREFIX ?= $(shell pwd)
1201-BIN_DIR ?= $(shell pwd)
1202-DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
1203-DOCKERFILE_PATH ?= ./Dockerfile
1204-DOCKERBUILD_CONTEXT ?= ./
1205-DOCKER_REPO ?= prom
1206-
1207-DOCKER_ARCHS ?= amd64
1208-
1209-BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS))
1210-PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS))
1211-TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS))
1212-
1213-ifeq ($(GOHOSTARCH),amd64)
1214- ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows))
1215- # Only supported on amd64
1216- test-flags := -race
1217- endif
1218-endif
1219-
1220-# This rule is used to forward a target like "build" to "common-build". This
1221-# allows a new "build" target to be defined in a Makefile which includes this
1222-# one and override "common-build" without override warnings.
1223-%: common-% ;
1224-
1225-.PHONY: common-all
1226-common-all: precheck style check_license lint unused build test
1227-
1228-.PHONY: common-style
1229-common-style:
1230- @echo ">> checking code style"
1231- @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \
1232- if [ -n "$${fmtRes}" ]; then \
1233- echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \
1234- echo "Please ensure you are using $$($(GO) version) for formatting code."; \
1235- exit 1; \
1236- fi
1237-
1238-.PHONY: common-check_license
1239-common-check_license:
1240- @echo ">> checking license header"
1241- @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \
1242- awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \
1243- done); \
1244- if [ -n "$${licRes}" ]; then \
1245- echo "license header checking failed:"; echo "$${licRes}"; \
1246- exit 1; \
1247- fi
1248-
1249-.PHONY: common-deps
1250-common-deps:
1251- @echo ">> getting dependencies"
1252-ifdef GO111MODULE
1253- GO111MODULE=$(GO111MODULE) $(GO) mod download
1254-else
1255- $(GO) get $(GOOPTS) -t ./...
1256-endif
1257-
1258-.PHONY: update-go-deps
1259-update-go-deps:
1260- @echo ">> updating Go dependencies"
1261- @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
1262- $(GO) get $$m; \
1263- done
1264- GO111MODULE=$(GO111MODULE) $(GO) mod tidy
1265-ifneq (,$(wildcard vendor))
1266- GO111MODULE=$(GO111MODULE) $(GO) mod vendor
1267-endif
1268-
1269-.PHONY: common-test-short
1270-common-test-short: $(GOTEST_DIR)
1271- @echo ">> running short tests"
1272- GO111MODULE=$(GO111MODULE) $(GOTEST) -short $(GOOPTS) $(pkgs)
1273-
1274-.PHONY: common-test
1275-common-test: $(GOTEST_DIR)
1276- @echo ">> running all tests"
1277- GO111MODULE=$(GO111MODULE) $(GOTEST) $(test-flags) $(GOOPTS) $(pkgs)
1278-
1279-$(GOTEST_DIR):
1280- @mkdir -p $@
1281-
1282-.PHONY: common-format
1283-common-format:
1284- @echo ">> formatting code"
1285- GO111MODULE=$(GO111MODULE) $(GO) fmt $(pkgs)
1286-
1287-.PHONY: common-vet
1288-common-vet:
1289- @echo ">> vetting code"
1290- GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs)
1291-
1292-.PHONY: common-lint
1293-common-lint: $(GOLANGCI_LINT)
1294-ifdef GOLANGCI_LINT
1295- @echo ">> running golangci-lint"
1296-ifdef GO111MODULE
1297-# 'go list' needs to be executed before staticcheck to prepopulate the modules cache.
1298-# Otherwise staticcheck might fail randomly for some reason not yet explained.
1299- GO111MODULE=$(GO111MODULE) $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null
1300- GO111MODULE=$(GO111MODULE) $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs)
1301-else
1302- $(GOLANGCI_LINT) run $(pkgs)
1303-endif
1304-endif
1305-
1306-# For backward-compatibility.
1307-.PHONY: common-staticcheck
1308-common-staticcheck: lint
1309-
1310-.PHONY: common-unused
1311-common-unused: $(GOVENDOR)
1312-ifdef GOVENDOR
1313- @echo ">> running check for unused packages"
1314- @$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages'
1315-else
1316-ifdef GO111MODULE
1317- @echo ">> running check for unused/missing packages in go.mod"
1318- GO111MODULE=$(GO111MODULE) $(GO) mod tidy
1319-ifeq (,$(wildcard vendor))
1320- @git diff --exit-code -- go.sum go.mod
1321-else
1322- @echo ">> running check for unused packages in vendor/"
1323- GO111MODULE=$(GO111MODULE) $(GO) mod vendor
1324- @git diff --exit-code -- go.sum go.mod vendor/
1325-endif
1326-endif
1327-endif
1328-
1329-.PHONY: common-build
1330-common-build: promu
1331- @echo ">> building binaries"
1332- GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX) $(PROMU_BINARIES)
1333-
1334-.PHONY: common-tarball
1335-common-tarball: promu
1336- @echo ">> building release tarball"
1337- $(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR)
1338-
1339-.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
1340-common-docker: $(BUILD_DOCKER_ARCHS)
1341-$(BUILD_DOCKER_ARCHS): common-docker-%:
1342- docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \
1343- -f $(DOCKERFILE_PATH) \
1344- --build-arg ARCH="$*" \
1345- --build-arg OS="linux" \
1346- $(DOCKERBUILD_CONTEXT)
1347-
1348-.PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS)
1349-common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
1350-$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
1351- docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)"
1352-
1353-.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
1354-common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
1355-$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
1356- docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
1357-
1358-.PHONY: common-docker-manifest
1359-common-docker-manifest:
1360- DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG))
1361- DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"
1362-
1363-.PHONY: promu
1364-promu: $(PROMU)
1365-
1366-$(PROMU):
1367- mkdir -p $(FIRST_GOPATH)/bin
1368- cp oci/promu/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
1369-
1370-.PHONY: proto
1371-proto:
1372- @echo ">> generating code from proto files"
1373- @./scripts/genproto.sh
1374-
1375-ifdef GOLANGCI_LINT
1376-$(GOLANGCI_LINT):
1377- mkdir -p $(FIRST_GOPATH)/bin
1378- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_LINT_VERSION)/install.sh \
1379- | sed -e '/install -d/d' \
1380- | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION)
1381-endif
1382-
1383-ifdef GOVENDOR
1384-.PHONY: $(GOVENDOR)
1385-$(GOVENDOR):
1386- GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor
1387-endif
1388-
1389-.PHONY: precheck
1390-precheck::
1391-
1392-define PRECHECK_COMMAND_template =
1393-precheck:: $(1)_precheck
1394-
1395-PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1)))
1396-.PHONY: $(1)_precheck
1397-$(1)_precheck:
1398- @if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \
1399- echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \
1400- exit 1; \
1401- fi
1402-endef
1403diff --git a/NOTICE b/NOTICE
1404deleted file mode 100644
1405index f5d0bbb..0000000
1406--- a/NOTICE
1407+++ /dev/null
1408@@ -1,18 +0,0 @@
1409-Prometheus Alertmanager
1410-Copyright 2013-2015 The Prometheus Authors
1411-
1412-This product includes software developed at
1413-SoundCloud Ltd. (http://soundcloud.com/).
1414-
1415-
1416-The following components are included in this product:
1417-
1418-Bootstrap
1419-http://getbootstrap.com
1420-Copyright 2011-2014 Twitter, Inc.
1421-Licensed under the MIT License
1422-
1423-bootstrap-datetimepicker.js
1424-http://www.eyecon.ro/bootstrap-datepicker
1425-Copyright 2012 Stefan Petre
1426-Licensed under the Apache License, Version 2.0
1427diff --git a/Procfile b/Procfile
1428deleted file mode 100644
1429index ab15cfc..0000000
1430--- a/Procfile
1431+++ /dev/null
1432@@ -1,5 +0,0 @@
1433-a1: ./alertmanager --log.level=debug --storage.path=$TMPDIR/a1 --web.listen-address=:9093 --cluster.listen-address=127.0.0.1:8001 --config.file=examples/ha/alertmanager.yml
1434-a2: ./alertmanager --log.level=debug --storage.path=$TMPDIR/a2 --web.listen-address=:9094 --cluster.listen-address=127.0.0.1:8002 --cluster.peer=127.0.0.1:8001 --config.file=examples/ha/alertmanager.yml
1435-a3: ./alertmanager --log.level=debug --storage.path=$TMPDIR/a3 --web.listen-address=:9095 --cluster.listen-address=127.0.0.1:8003 --cluster.peer=127.0.0.1:8001 --config.file=examples/ha/alertmanager.yml
1436-wh: go run ./examples/webhook/echo.go
1437-
1438diff --git a/README.md b/README.md
1439index c7a3683..6b2c643 100644
1440--- a/README.md
1441+++ b/README.md
1442@@ -1,406 +1,89 @@
1443-# Alertmanager [![CircleCI](https://circleci.com/gh/prometheus/alertmanager/tree/master.svg?style=shield)][circleci]
1444+# Prometheus Alertmanager | Ubuntu
1445
1446-[![Docker Repository on Quay](https://quay.io/repository/prometheus/alertmanager/status "Docker Repository on Quay")][quay]
1447-[![Docker Pulls](https://img.shields.io/docker/pulls/prom/alertmanager.svg?maxAge=604800)][hub]
1448+Current Prometheus Alertmanager Docker Image from Ubuntu. Receives security updates and rolls to newer Prometheus Alertmanager or Ubuntu LTS. This repository is exempted from per-user rate limits. For [LTS Docker Image](https://ubuntu.com/security/docker-images) versions of this image, see `lts/prometheus-alertmanager`.
1449
1450-The Alertmanager handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integrations such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts.
1451
1452-* [Documentation](http://prometheus.io/docs/alerting/alertmanager/)
1453+## About Prometheus Alertmanager
1454
1455-## Install
1456+The Alertmanager handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts. Read more on the [Prometheus Alertmanager website](https://prometheus.io/docs/alerting/latest/alertmanager/).
1457
1458-There are various ways of installing Alertmanager.
1459
1460-### Precompiled binaries
1461+## Tags and Architectures
1462+![LTS](https://assets.ubuntu.com/v1/0a5ff561-LTS%402x.png?h=17)
1463+Up to 5 years free security maintenance `from lts/prometheus-alertmanager`.
1464
1465-Precompiled binaries for released versions are available in the
1466-[*download* section](https://prometheus.io/download/)
1467-on [prometheus.io](https://prometheus.io). Using the latest production release binary
1468-is the recommended way of installing Alertmanager.
1469+![ESM](https://assets.ubuntu.com/v1/572f3fbd-ESM%402x.png?h=17)
1470+Up to 10 years customer security maintenance `from store/canonical/prometheus-alertmanager`.
1471
1472-### Docker images
1473+_Tags in italics are not available in ubuntu/prometheus-alertmanager but are shown here for completeness._
1474
1475-Docker images are available on [Quay.io](https://quay.io/repository/prometheus/alertmanager).
1476+| Channel Tag | | | Currently | Architectures |
1477+|---|---|---|---|---|
1478+| **`0.21-20.04_beta`** &nbsp;&nbsp; | | | Prometheus Alertmanager 0.21.0 on Ubuntu 20.04 LTS | `amd64`, `arm64`, `ppc64el`, `s390x` |
1479+| _`track_risk`_ |
1480
1481-### Compiling the binary
1482+Channel tag shows the most stable channel for that track ordered `stable`, `candidate`, `beta`, `edge`. More risky channels are always implicitly available. So if `beta` is listed, you can also pull `edge`. If `candidate` is listed, you can pull `beta` and `edge`. When `stable` is listed, all four are available. Images are guaranteed to progress through the sequence `edge`, `beta`, `candidate` before `stable`.
1483
1484-You can either `go get` it:
1485
1486-```
1487-$ GO15VENDOREXPERIMENT=1 go get github.com/prometheus/alertmanager/cmd/...
1488-# cd $GOPATH/src/github.com/prometheus/alertmanager
1489-$ alertmanager --config.file=<your_file>
1490-```
1491-
1492-Or clone the repository and build manually:
1493-
1494-```
1495-$ mkdir -p $GOPATH/src/github.com/prometheus
1496-$ cd $GOPATH/src/github.com/prometheus
1497-$ git clone https://github.com/prometheus/alertmanager.git
1498-$ cd alertmanager
1499-$ make build
1500-$ ./alertmanager --config.file=<your_file>
1501-```
1502-
1503-You can also build just one of the binaries in this repo by passing a name to the build function:
1504-```
1505-$ make build BINARIES=amtool
1506-```
1507-
1508-## Example
1509-
1510-This is an example configuration that should cover most relevant aspects of the new YAML configuration format. The full documentation of the configuration can be found [here](https://prometheus.io/docs/alerting/configuration/).
1511-
1512-```yaml
1513-global:
1514- # The smarthost and SMTP sender used for mail notifications.
1515- smtp_smarthost: 'localhost:25'
1516- smtp_from: 'alertmanager@example.org'
1517-
1518-# The root route on which each incoming alert enters.
1519-route:
1520- # The root route must not have any matchers as it is the entry point for
1521- # all alerts. It needs to have a receiver configured so alerts that do not
1522- # match any of the sub-routes are sent to someone.
1523- receiver: 'team-X-mails'
1524-
1525- # The labels by which incoming alerts are grouped together. For example,
1526- # multiple alerts coming in for cluster=A and alertname=LatencyHigh would
1527- # be batched into a single group.
1528- #
1529- # To aggregate by all possible labels use '...' as the sole label name.
1530- # This effectively disables aggregation entirely, passing through all
1531- # alerts as-is. This is unlikely to be what you want, unless you have
1532- # a very low alert volume or your upstream notification system performs
1533- # its own grouping. Example: group_by: [...]
1534- group_by: ['alertname', 'cluster']
1535-
1536- # When a new group of alerts is created by an incoming alert, wait at
1537- # least 'group_wait' to send the initial notification.
1538- # This way ensures that you get multiple alerts for the same group that start
1539- # firing shortly after another are batched together on the first
1540- # notification.
1541- group_wait: 30s
1542-
1543- # When the first notification was sent, wait 'group_interval' to send a batch
1544- # of new alerts that started firing for that group.
1545- group_interval: 5m
1546-
1547- # If an alert has successfully been sent, wait 'repeat_interval' to
1548- # resend them.
1549- repeat_interval: 3h
1550-
1551- # All the above attributes are inherited by all child routes and can
1552- # overwritten on each.
1553-
1554- # The child route trees.
1555- routes:
1556- # This routes performs a regular expression match on alert labels to
1557- # catch alerts that are related to a list of services.
1558- - match_re:
1559- service: ^(foo1|foo2|baz)$
1560- receiver: team-X-mails
1561-
1562- # The service has a sub-route for critical alerts, any alerts
1563- # that do not match, i.e. severity != critical, fall-back to the
1564- # parent node and are sent to 'team-X-mails'
1565- routes:
1566- - match:
1567- severity: critical
1568- receiver: team-X-pager
1569-
1570- - match:
1571- service: files
1572- receiver: team-Y-mails
1573-
1574- routes:
1575- - match:
1576- severity: critical
1577- receiver: team-Y-pager
1578-
1579- # This route handles all alerts coming from a database service. If there's
1580- # no team to handle it, it defaults to the DB team.
1581- - match:
1582- service: database
1583-
1584- receiver: team-DB-pager
1585- # Also group alerts by affected database.
1586- group_by: [alertname, cluster, database]
1587-
1588- routes:
1589- - match:
1590- owner: team-X
1591- receiver: team-X-pager
1592-
1593- - match:
1594- owner: team-Y
1595- receiver: team-Y-pager
1596-
1597-
1598-# Inhibition rules allow to mute a set of alerts given that another alert is
1599-# firing.
1600-# We use this to mute any warning-level notifications if the same alert is
1601-# already critical.
1602-inhibit_rules:
1603-- source_match:
1604- severity: 'critical'
1605- target_match:
1606- severity: 'warning'
1607- # Apply inhibition if the alertname is the same.
1608- # CAUTION:
1609- # If all label names listed in `equal` are missing
1610- # from both the source and target alerts,
1611- # the inhibition rule will apply!
1612- equal: ['alertname']
1613-
1614-
1615-receivers:
1616-- name: 'team-X-mails'
1617- email_configs:
1618- - to: 'team-X+alerts@example.org, team-Y+alerts@example.org'
1619-
1620-- name: 'team-X-pager'
1621- email_configs:
1622- - to: 'team-X+alerts-critical@example.org'
1623- pagerduty_configs:
1624- - routing_key: <team-X-key>
1625-
1626-- name: 'team-Y-mails'
1627- email_configs:
1628- - to: 'team-Y+alerts@example.org'
1629-
1630-- name: 'team-Y-pager'
1631- pagerduty_configs:
1632- - routing_key: <team-Y-key>
1633-
1634-- name: 'team-DB-pager'
1635- pagerduty_configs:
1636- - routing_key: <team-DB-key>
1637-```
1638-
1639-## API
1640-
1641-The current Alertmanager API is version 2. This API is fully generated via the
1642-[OpenAPI project](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md)
1643-and [Go Swagger](https://github.com/go-swagger/go-swagger/) with the exception
1644-of the HTTP handlers themselves. The API specification can be found in
1645-[api/v2/openapi.yaml](api/v2/openapi.yaml). A HTML rendered version can be
1646-accessed [here](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/prometheus/alertmanager/master/api/v2/openapi.yaml).
1647-Clients can be easily generated via any OpenAPI generator for all major languages.
1648+## Usage
1649
1650-With the default config, endpoints are accessed under a `/api/v1` or `/api/v2` prefix.
1651-The v2 `/status` endpoint would be `/api/v2/status`. If `--web.route-prefix` is set then API routes are
1652-prefixed with that as well, so `--web.route-prefix=/alertmanager/` would
1653-relate to `/alertmanager/api/v2/status`.
1654-
1655-_API v2 is still under heavy development and thereby subject to change._
1656-
1657-## amtool
1658-
1659-`amtool` is a cli tool for interacting with the Alertmanager API. It is bundled with all releases of Alertmanager.
1660-
1661-### Install
1662-
1663-Alternatively you can install with:
1664-```
1665-go get github.com/prometheus/alertmanager/cmd/amtool
1666-```
1667-
1668-### Examples
1669-
1670-View all currently firing alerts:
1671-```
1672-$ amtool alert
1673-Alertname Starts At Summary
1674-Test_Alert 2017-08-02 18:30:18 UTC This is a testing alert!
1675-Test_Alert 2017-08-02 18:30:18 UTC This is a testing alert!
1676-Check_Foo_Fails 2017-08-02 18:30:18 UTC This is a testing alert!
1677-Check_Foo_Fails 2017-08-02 18:30:18 UTC This is a testing alert!
1678-```
1679-
1680-View all currently firing alerts with extended output:
1681-```
1682-$ amtool -o extended alert
1683-Labels Annotations Starts At Ends At Generator URL
1684-alertname="Test_Alert" instance="node0" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1685-alertname="Test_Alert" instance="node1" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1686-alertname="Check_Foo_Fails" instance="node0" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1687-alertname="Check_Foo_Fails" instance="node1" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1688-```
1689+Launch this image locally:
1690
1691-In addition to viewing alerts, you can use the rich query syntax provided by Alertmanager:
1692-```
1693-$ amtool -o extended alert query alertname="Test_Alert"
1694-Labels Annotations Starts At Ends At Generator URL
1695-alertname="Test_Alert" instance="node0" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1696-alertname="Test_Alert" instance="node1" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1697-
1698-$ amtool -o extended alert query instance=~".+1"
1699-Labels Annotations Starts At Ends At Generator URL
1700-alertname="Test_Alert" instance="node1" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1701-alertname="Check_Foo_Fails" instance="node1" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1702-
1703-$ amtool -o extended alert query alertname=~"Test.*" instance=~".+1"
1704-Labels Annotations Starts At Ends At Generator URL
1705-alertname="Test_Alert" instance="node1" link="https://example.com" summary="This is a testing alert!" 2017-08-02 18:31:24 UTC 0001-01-01 00:00:00 UTC http://my.testing.script.local
1706+```sh
1707+docker run -d --name prometheus-alertmanager-container -e TZ=UTC -p 30093:9093 ubuntu/prometheus-alertmanager:0.21-20.04_beta
1708 ```
1709+Access your Prometheus Alertmanager server at `localhost:30093`.
1710
1711-Silence an alert:
1712-```
1713-$ amtool silence add alertname=Test_Alert
1714-b3ede22e-ca14-4aa0-932c-ca2f3445f926
1715+#### Parameters
1716
1717-$ amtool silence add alertname="Test_Alert" instance=~".+0"
1718-e48cb58a-0b17-49ba-b734-3585139b1d25
1719-```
1720+| Parameter | Description |
1721+|---|---|
1722+| `-e TZ=UTC` | Timezone. |
1723+| `-p 30093:9093` | Expose Prometheus Alertmanager on `localhost:30093`. |
1724+| `-v /path/to/alertmanager.yml:/etc/prometheus/alertmanager.yml` | Local [configuration file](https://www.prometheus.io/docs/alerting/latest/configuration/) `alertmanager.yml` (try [this example](https://git.launchpad.net/~canonical-server/ubuntu-docker-images/+git/prometheus-alertmanager/plain/oci/examples/config/alertmanager.yml)). |
1725+| `-v /path/to/persisted/data:/alertmanager` | Persist data instead of initializing a new database for each newly launched container. **Important note**: the directory you will be using to persist the data needs to belong to `nogroup:nobody`. You can run `chown nogroup:nobody <path_to_persist_data>` before launching your container. |
1726
1727-View silences:
1728-```
1729-$ amtool silence query
1730-ID Matchers Ends At Created By Comment
1731-b3ede22e-ca14-4aa0-932c-ca2f3445f926 alertname=Test_Alert 2017-08-02 19:54:50 UTC kellel
1732
1733-$ amtool silence query instance=~".+0"
1734-ID Matchers Ends At Created By Comment
1735-e48cb58a-0b17-49ba-b734-3585139b1d25 alertname=Test_Alert instance=~.+0 2017-08-02 22:41:39 UTC kellel
1736-```
1737+#### Testing/Debugging
1738
1739-Expire a silence:
1740-```
1741-$ amtool silence expire b3ede22e-ca14-4aa0-932c-ca2f3445f926
1742-```
1743+To debug the container:
1744
1745-Expire all silences matching a query:
1746+```sh
1747+docker logs -f prometheus-alertmanager-container
1748 ```
1749-$ amtool silence query instance=~".+0"
1750-ID Matchers Ends At Created By Comment
1751-e48cb58a-0b17-49ba-b734-3585139b1d25 alertname=Test_Alert instance=~.+0 2017-08-02 22:41:39 UTC kellel
1752-
1753-$ amtool silence expire $(amtool silence -q query instance=~".+0")
1754
1755-$ amtool silence query instance=~".+0"
1756+To get an interactive shell:
1757
1758+```sh
1759+docker exec -it prometheus-alertmanager-container /bin/bash
1760 ```
1761
1762-Expire all silences:
1763-```
1764-$ amtool silence expire $(amtool silence query -q)
1765-```
1766
1767-### Configuration
1768+## Deploy with Kubernetes
1769
1770-`amtool` allows a configuration file to specify some options for convenience. The default configuration file paths are `$HOME/.config/amtool/config.yml` or `/etc/amtool/config.yml`
1771+Works with any Kubernetes; if you don't have one, we recommend you [install MicroK8s](https://microk8s.io/) and `microk8s.enable dns storage` then `snap alias microk8s.kubectl kubectl`.
1772
1773-An example configuration file might look like the following:
1774+Download
1775+[alertmanager.yml](https://git.launchpad.net/~canonical-server/ubuntu-docker-images/+git/prometheus-alertmanager/plain/oci/examples/config/alertmanager.yml) and
1776+[prometheus-alertmanager-deployment.yml](https://git.launchpad.net/~canonical-server/ubuntu-docker-images/+git/prometheus-alertmanager/plain/oci/examples/alertmanager-deployment.yml) and set `containers.prometheus-alertmanager.image` in `prometheus-alertmanager-deployment.yml` to your chosen channel tag (e.g. `ubuntu/prometheus-alertmanager:0.21-20.04_beta`), then:
1777
1778+```sh
1779+kubectl create configmap prometheus-alertmanager-config --from-file=alertmanager=alertmanager.yml
1780+kubectl apply -f prometheus-alertmanager-deployment.yml
1781 ```
1782-# Define the path that `amtool` can find your `alertmanager` instance
1783-alertmanager.url: "http://localhost:9093"
1784-
1785-# Override the default author. (unset defaults to your username)
1786-author: me@example.com
1787-
1788-# Force amtool to give you an error if you don't include a comment on a silence
1789-comment_required: true
1790
1791-# Set a default output format. (unset defaults to simple)
1792-output: extended
1793+You will now be able to connect to the Prometheus Alertmanager server on `localhost:30093`.
1794
1795-# Set a default receiver
1796-receiver: team-X-pager
1797-```
1798-
1799-### Routes
1800+## Bugs and feature requests
1801
1802-`amtool` allows you to visualize the routes of your configuration in form of text tree view.
1803-Also you can use it to test the routing by passing it label set of an alert
1804-and it prints out all receivers the alert would match ordered and separated by `,`.
1805-(If you use `--verify.receivers` amtool returns error code 1 on mismatch)
1806+If you find a bug in our image or want to request a specific feature, please file a bug here:
1807
1808-Example of usage:
1809-```
1810-# View routing tree of remote Alertmanager
1811-$ amtool config routes --alertmanager.url=http://localhost:9090
1812+[https://bugs.launchpad.net/ubuntu-docker-images/+filebug](https://bugs.launchpad.net/ubuntu-docker-images/+filebug)
1813
1814-# Test if alert matches expected receiver
1815-$ amtool config routes test --config.file=doc/examples/simple.yml --tree --verify.receivers=team-X-pager service=database owner=team-X
1816-```
1817+Please title the bug "`prometheus-alertmanager: <issue summary>`". Make sure to include the digest of the image you are using, from:
1818
1819-## High Availability
1820-
1821-Alertmanager's high availability is in production use at many companies and is enabled by default.
1822-
1823-> Important: Both UDP and TCP are needed in alertmanager 0.15 and higher for the cluster to work.
1824-> - If you are using a firewall, make sure to whitelist the clustering port for both protocols.
1825-> - If you are running in a container, make sure to expose the clustering port for both protocols.
1826-
1827-To create a highly available cluster of the Alertmanager the instances need to
1828-be configured to communicate with each other. This is configured using the
1829-`--cluster.*` flags.
1830-
1831-- `--cluster.listen-address` string: cluster listen address (default "0.0.0.0:9094"; empty string disables HA mode)
1832-- `--cluster.advertise-address` string: cluster advertise address
1833-- `--cluster.peer` value: initial peers (repeat flag for each additional peer)
1834-- `--cluster.peer-timeout` value: peer timeout period (default "15s")
1835-- `--cluster.gossip-interval` value: cluster message propagation speed
1836- (default "200ms")
1837-- `--cluster.pushpull-interval` value: lower values will increase
1838- convergence speeds at expense of bandwidth (default "1m0s")
1839-- `--cluster.settle-timeout` value: maximum time to wait for cluster
1840- connections to settle before evaluating notifications.
1841-- `--cluster.tcp-timeout` value: timeout value for tcp connections, reads and writes (default "10s")
1842-- `--cluster.probe-timeout` value: time to wait for ack before marking node unhealthy
1843- (default "500ms")
1844-- `--cluster.probe-interval` value: interval between random node probes (default "1s")
1845-- `--cluster.reconnect-interval` value: interval between attempting to reconnect to lost peers (default "10s")
1846-- `--cluster.reconnect-timeout` value: length of time to attempt to reconnect to a lost peer (default: "6h0m0s")
1847-
1848-The chosen port in the `cluster.listen-address` flag is the port that needs to be
1849-specified in the `cluster.peer` flag of the other peers.
1850-
1851-The `cluster.advertise-address` flag is required if the instance doesn't have
1852-an IP address that is part of [RFC 6980](https://tools.ietf.org/html/rfc6890)
1853-with a default route.
1854-
1855-To start a cluster of three peers on your local machine use [`goreman`](https://github.com/mattn/goreman) and the
1856-Procfile within this repository.
1857-
1858- goreman start
1859-
1860-To point your Prometheus 1.4, or later, instance to multiple Alertmanagers, configure them
1861-in your `prometheus.yml` configuration file, for example:
1862-
1863-```yaml
1864-alerting:
1865- alertmanagers:
1866- - static_configs:
1867- - targets:
1868- - alertmanager1:9093
1869- - alertmanager2:9093
1870- - alertmanager3:9093
1871+```sh
1872+docker images --no-trunc --quiet ubuntu/prometheus-alertmanager:<tag>
1873 ```
1874
1875-> Important: Do not load balance traffic between Prometheus and its Alertmanagers, but instead point Prometheus to a list of all Alertmanagers. The Alertmanager implementation expects all alerts to be sent to all Alertmanagers to ensure high availability.
1876-
1877-### Turn off high availability
1878-
1879-If running Alertmanager in high availability mode is not desired, setting `--cluster.listen-address=` prevents Alertmanager from listening to incoming peer requests.
1880-
1881-## Contributing
1882-
1883-Check the [Prometheus contributing page](https://github.com/prometheus/prometheus/blob/master/CONTRIBUTING.md).
1884-
1885-To contribute to the user interface, refer to [ui/app/CONTRIBUTING.md](ui/app/CONTRIBUTING.md).
1886-
1887-## Architecture
1888-
1889-![](doc/arch.svg)
1890-
1891-## License
1892-
1893-Apache License 2.0, see [LICENSE](https://github.com/prometheus/alertmanager/blob/master/LICENSE).
1894
1895-[hub]: https://hub.docker.com/r/prom/alertmanager/
1896-[circleci]: https://circleci.com/gh/prometheus/alertmanager
1897-[quay]: https://quay.io/repository/prometheus/alertmanager
1898diff --git a/VERSION b/VERSION
1899deleted file mode 100644
1900index 8854156..0000000
1901--- a/VERSION
1902+++ /dev/null
1903@@ -1 +0,0 @@
1904-0.21.0
1905diff --git a/api/api.go b/api/api.go
1906deleted file mode 100644
1907index 6b4a883..0000000
1908--- a/api/api.go
1909+++ /dev/null
1910@@ -1,230 +0,0 @@
1911-// Copyright 2019 Prometheus Team
1912-// Licensed under the Apache License, Version 2.0 (the "License");
1913-// you may not use this file except in compliance with the License.
1914-// You may obtain a copy of the License at
1915-//
1916-// http://www.apache.org/licenses/LICENSE-2.0
1917-//
1918-// Unless required by applicable law or agreed to in writing, software
1919-// distributed under the License is distributed on an "AS IS" BASIS,
1920-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1921-// See the License for the specific language governing permissions and
1922-// limitations under the License.
1923-
1924-package api
1925-
1926-import (
1927- "errors"
1928- "fmt"
1929- "net/http"
1930- "runtime"
1931- "time"
1932-
1933- apiv1 "github.com/prometheus/alertmanager/api/v1"
1934- apiv2 "github.com/prometheus/alertmanager/api/v2"
1935- "github.com/prometheus/alertmanager/cluster"
1936- "github.com/prometheus/alertmanager/config"
1937- "github.com/prometheus/alertmanager/dispatch"
1938- "github.com/prometheus/alertmanager/provider"
1939- "github.com/prometheus/alertmanager/silence"
1940- "github.com/prometheus/alertmanager/types"
1941- "github.com/prometheus/client_golang/prometheus"
1942- "github.com/prometheus/common/model"
1943- "github.com/prometheus/common/route"
1944-
1945- "github.com/go-kit/kit/log"
1946-)
1947-
1948-// API represents all APIs of Alertmanager.
1949-type API struct {
1950- v1 *apiv1.API
1951- v2 *apiv2.API
1952- requestsInFlight prometheus.Gauge
1953- concurrencyLimitExceeded prometheus.Counter
1954- timeout time.Duration
1955- inFlightSem chan struct{}
1956-}
1957-
1958-// Options for the creation of an API object. Alerts, Silences, and StatusFunc
1959-// are mandatory to set. The zero value for everything else is a safe default.
1960-type Options struct {
1961- // Alerts to be used by the API. Mandatory.
1962- Alerts provider.Alerts
1963- // Silences to be used by the API. Mandatory.
1964- Silences *silence.Silences
1965- // StatusFunc is used be the API to retrieve the AlertStatus of an
1966- // alert. Mandatory.
1967- StatusFunc func(model.Fingerprint) types.AlertStatus
1968- // Peer from the gossip cluster. If nil, no clustering will be used.
1969- Peer *cluster.Peer
1970- // Timeout for all HTTP connections. The zero value (and negative
1971- // values) result in no timeout.
1972- Timeout time.Duration
1973- // Concurrency limit for GET requests. The zero value (and negative
1974- // values) result in a limit of GOMAXPROCS or 8, whichever is
1975- // larger. Status code 503 is served for GET requests that would exceed
1976- // the concurrency limit.
1977- Concurrency int
1978- // Logger is used for logging, if nil, no logging will happen.
1979- Logger log.Logger
1980- // Registry is used to register Prometheus metrics. If nil, no metrics
1981- // registration will happen.
1982- Registry prometheus.Registerer
1983- // GroupFunc returns a list of alert groups. The alerts are grouped
1984- // according to the current active configuration. Alerts returned are
1985- // filtered by the arguments provided to the function.
1986- GroupFunc func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string)
1987-}
1988-
1989-func (o Options) validate() error {
1990- if o.Alerts == nil {
1991- return errors.New("mandatory field Alerts not set")
1992- }
1993- if o.Silences == nil {
1994- return errors.New("mandatory field Silences not set")
1995- }
1996- if o.StatusFunc == nil {
1997- return errors.New("mandatory field StatusFunc not set")
1998- }
1999- if o.GroupFunc == nil {
2000- return errors.New("mandatory field GroupFunc not set")
2001- }
2002- return nil
2003-}
2004-
2005-// New creates a new API object combining all API versions. Note that an Update
2006-// call is also needed to get the APIs into an operational state.
2007-func New(opts Options) (*API, error) {
2008- if err := opts.validate(); err != nil {
2009- return nil, fmt.Errorf("invalid API options: %s", err)
2010- }
2011- l := opts.Logger
2012- if l == nil {
2013- l = log.NewNopLogger()
2014- }
2015- concurrency := opts.Concurrency
2016- if concurrency < 1 {
2017- concurrency = runtime.GOMAXPROCS(0)
2018- if concurrency < 8 {
2019- concurrency = 8
2020- }
2021- }
2022-
2023- v1 := apiv1.New(
2024- opts.Alerts,
2025- opts.Silences,
2026- opts.StatusFunc,
2027- opts.Peer,
2028- log.With(l, "version", "v1"),
2029- opts.Registry,
2030- )
2031-
2032- v2, err := apiv2.NewAPI(
2033- opts.Alerts,
2034- opts.GroupFunc,
2035- opts.StatusFunc,
2036- opts.Silences,
2037- opts.Peer,
2038- log.With(l, "version", "v2"),
2039- opts.Registry,
2040- )
2041-
2042- if err != nil {
2043- return nil, err
2044- }
2045-
2046- // TODO(beorn7): For now, this hardcodes the method="get" label. Other
2047- // methods should get the same instrumentation.
2048- requestsInFlight := prometheus.NewGauge(prometheus.GaugeOpts{
2049- Name: "alertmanager_http_requests_in_flight",
2050- Help: "Current number of HTTP requests being processed.",
2051- ConstLabels: prometheus.Labels{"method": "get"},
2052- })
2053- concurrencyLimitExceeded := prometheus.NewCounter(prometheus.CounterOpts{
2054- Name: "alertmanager_http_concurrency_limit_exceeded_total",
2055- Help: "Total number of times an HTTP request failed because the concurrency limit was reached.",
2056- ConstLabels: prometheus.Labels{"method": "get"},
2057- })
2058- if opts.Registry != nil {
2059- if err := opts.Registry.Register(requestsInFlight); err != nil {
2060- return nil, err
2061- }
2062- if err := opts.Registry.Register(concurrencyLimitExceeded); err != nil {
2063- return nil, err
2064- }
2065- }
2066-
2067- return &API{
2068- v1: v1,
2069- v2: v2,
2070- requestsInFlight: requestsInFlight,
2071- concurrencyLimitExceeded: concurrencyLimitExceeded,
2072- timeout: opts.Timeout,
2073- inFlightSem: make(chan struct{}, concurrency),
2074- }, nil
2075-}
2076-
2077-// Register all APIs. It registers APIv1 with the provided router directly. As
2078-// APIv2 works on the http.Handler level, this method also creates a new
2079-// http.ServeMux and then uses it to register both the provided router (to
2080-// handle "/") and APIv2 (to handle "<routePrefix>/api/v2"). The method returns
2081-// the newly created http.ServeMux. If a timeout has been set on construction of
2082-// API, it is enforced for all HTTP request going through this mux. The same is
2083-// true for the concurrency limit, with the exception that it is only applied to
2084-// GET requests.
2085-func (api *API) Register(r *route.Router, routePrefix string) *http.ServeMux {
2086- api.v1.Register(r.WithPrefix("/api/v1"))
2087-
2088- mux := http.NewServeMux()
2089- mux.Handle("/", api.limitHandler(r))
2090-
2091- apiPrefix := ""
2092- if routePrefix != "/" {
2093- apiPrefix = routePrefix
2094- }
2095- // TODO(beorn7): HTTP instrumentation is only in place for Router. Since
2096- // /api/v2 works on the Handler level, it is currently not instrumented
2097- // at all (with the exception of requestsInFlight, which is handled in
2098- // limitHandler below).
2099- mux.Handle(
2100- apiPrefix+"/api/v2/",
2101- api.limitHandler(http.StripPrefix(apiPrefix+"/api/v2", api.v2.Handler)),
2102- )
2103-
2104- return mux
2105-}
2106-
2107-// Update config and resolve timeout of each API. APIv2 also needs
2108-// setAlertStatus to be updated.
2109-func (api *API) Update(cfg *config.Config, setAlertStatus func(model.LabelSet)) {
2110- api.v1.Update(cfg)
2111- api.v2.Update(cfg, setAlertStatus)
2112-}
2113-
2114-func (api *API) limitHandler(h http.Handler) http.Handler {
2115- concLimiter := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
2116- if req.Method == http.MethodGet { // Only limit concurrency of GETs.
2117- select {
2118- case api.inFlightSem <- struct{}{}: // All good, carry on.
2119- api.requestsInFlight.Inc()
2120- defer func() {
2121- <-api.inFlightSem
2122- api.requestsInFlight.Dec()
2123- }()
2124- default:
2125- api.concurrencyLimitExceeded.Inc()
2126- http.Error(rsp, fmt.Sprintf(
2127- "Limit of concurrent GET requests reached (%d), try again later.\n", cap(api.inFlightSem),
2128- ), http.StatusServiceUnavailable)
2129- return
2130- }
2131- }
2132- h.ServeHTTP(rsp, req)
2133- })
2134- if api.timeout <= 0 {
2135- return concLimiter
2136- }
2137- return http.TimeoutHandler(concLimiter, api.timeout, fmt.Sprintf(
2138- "Exceeded configured timeout of %v.\n", api.timeout,
2139- ))
2140-}
2141diff --git a/api/metrics/metrics.go b/api/metrics/metrics.go
2142deleted file mode 100644
2143index 483569a..0000000
2144--- a/api/metrics/metrics.go
2145+++ /dev/null
2146@@ -1,54 +0,0 @@
2147-// Copyright 2019 Prometheus Team
2148-// Licensed under the Apache License, Version 2.0 (the "License");
2149-// you may not use this file except in compliance with the License.
2150-// You may obtain a copy of the License at
2151-//
2152-// http://www.apache.org/licenses/LICENSE-2.0
2153-//
2154-// Unless required by applicable law or agreed to in writing, software
2155-// distributed under the License is distributed on an "AS IS" BASIS,
2156-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2157-// See the License for the specific language governing permissions and
2158-// limitations under the License.
2159-
2160-package metrics
2161-
2162-import "github.com/prometheus/client_golang/prometheus"
2163-
2164-// Alerts stores metrics for alerts which are common across all API versions.
2165-type Alerts struct {
2166- firing prometheus.Counter
2167- resolved prometheus.Counter
2168- invalid prometheus.Counter
2169-}
2170-
2171-// NewAlerts returns an *Alerts struct for the given API version.
2172-func NewAlerts(version string, r prometheus.Registerer) *Alerts {
2173- numReceivedAlerts := prometheus.NewCounterVec(prometheus.CounterOpts{
2174- Name: "alertmanager_alerts_received_total",
2175- Help: "The total number of received alerts.",
2176- ConstLabels: prometheus.Labels{"version": version},
2177- }, []string{"status"})
2178- numInvalidAlerts := prometheus.NewCounter(prometheus.CounterOpts{
2179- Name: "alertmanager_alerts_invalid_total",
2180- Help: "The total number of received alerts that were invalid.",
2181- ConstLabels: prometheus.Labels{"version": version},
2182- })
2183- if r != nil {
2184- r.MustRegister(numReceivedAlerts, numInvalidAlerts)
2185- }
2186- return &Alerts{
2187- firing: numReceivedAlerts.WithLabelValues("firing"),
2188- resolved: numReceivedAlerts.WithLabelValues("resolved"),
2189- invalid: numInvalidAlerts,
2190- }
2191-}
2192-
2193-// Firing returns a counter of firing alerts.
2194-func (a *Alerts) Firing() prometheus.Counter { return a.firing }
2195-
2196-// Resolved returns a counter of resolved alerts.
2197-func (a *Alerts) Resolved() prometheus.Counter { return a.resolved }
2198-
2199-// Invalid returns a counter of invalid alerts.
2200-func (a *Alerts) Invalid() prometheus.Counter { return a.invalid }
2201diff --git a/api/v1/api.go b/api/v1/api.go
2202deleted file mode 100644
2203index 3a14664..0000000
2204--- a/api/v1/api.go
2205+++ /dev/null
2206@@ -1,797 +0,0 @@
2207-// Copyright 2015 Prometheus Team
2208-// Licensed under the Apache License, Version 2.0 (the "License");
2209-// you may not use this file except in compliance with the License.
2210-// You may obtain a copy of the License at
2211-//
2212-// http://www.apache.org/licenses/LICENSE-2.0
2213-//
2214-// Unless required by applicable law or agreed to in writing, software
2215-// distributed under the License is distributed on an "AS IS" BASIS,
2216-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2217-// See the License for the specific language governing permissions and
2218-// limitations under the License.
2219-
2220-package v1
2221-
2222-import (
2223- "encoding/json"
2224- "errors"
2225- "fmt"
2226- "net/http"
2227- "regexp"
2228- "sort"
2229- "sync"
2230- "time"
2231-
2232- "github.com/go-kit/kit/log"
2233- "github.com/go-kit/kit/log/level"
2234- "github.com/prometheus/client_golang/prometheus"
2235- "github.com/prometheus/common/model"
2236- "github.com/prometheus/common/route"
2237- "github.com/prometheus/common/version"
2238-
2239- "github.com/prometheus/alertmanager/api/metrics"
2240- "github.com/prometheus/alertmanager/cluster"
2241- "github.com/prometheus/alertmanager/config"
2242- "github.com/prometheus/alertmanager/dispatch"
2243- "github.com/prometheus/alertmanager/pkg/labels"
2244- "github.com/prometheus/alertmanager/provider"
2245- "github.com/prometheus/alertmanager/silence"
2246- "github.com/prometheus/alertmanager/silence/silencepb"
2247- "github.com/prometheus/alertmanager/types"
2248-)
2249-
2250-var corsHeaders = map[string]string{
2251- "Access-Control-Allow-Headers": "Accept, Authorization, Content-Type, Origin",
2252- "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
2253- "Access-Control-Allow-Origin": "*",
2254- "Access-Control-Expose-Headers": "Date",
2255- "Cache-Control": "no-cache, no-store, must-revalidate",
2256-}
2257-
2258-// Alert is the API representation of an alert, which is a regular alert
2259-// annotated with silencing and inhibition info.
2260-type Alert struct {
2261- *model.Alert
2262- Status types.AlertStatus `json:"status"`
2263- Receivers []string `json:"receivers"`
2264- Fingerprint string `json:"fingerprint"`
2265-}
2266-
2267-// Enables cross-site script calls.
2268-func setCORS(w http.ResponseWriter) {
2269- for h, v := range corsHeaders {
2270- w.Header().Set(h, v)
2271- }
2272-}
2273-
2274-// API provides registration of handlers for API routes.
2275-type API struct {
2276- alerts provider.Alerts
2277- silences *silence.Silences
2278- config *config.Config
2279- route *dispatch.Route
2280- uptime time.Time
2281- peer *cluster.Peer
2282- logger log.Logger
2283- m *metrics.Alerts
2284-
2285- getAlertStatus getAlertStatusFn
2286-
2287- mtx sync.RWMutex
2288-}
2289-
2290-type getAlertStatusFn func(model.Fingerprint) types.AlertStatus
2291-
2292-// New returns a new API.
2293-func New(
2294- alerts provider.Alerts,
2295- silences *silence.Silences,
2296- sf getAlertStatusFn,
2297- peer *cluster.Peer,
2298- l log.Logger,
2299- r prometheus.Registerer,
2300-) *API {
2301- if l == nil {
2302- l = log.NewNopLogger()
2303- }
2304-
2305- return &API{
2306- alerts: alerts,
2307- silences: silences,
2308- getAlertStatus: sf,
2309- uptime: time.Now(),
2310- peer: peer,
2311- logger: l,
2312- m: metrics.NewAlerts("v1", r),
2313- }
2314-}
2315-
2316-// Register registers the API handlers under their correct routes
2317-// in the given router.
2318-func (api *API) Register(r *route.Router) {
2319- wrap := func(f http.HandlerFunc) http.HandlerFunc {
2320- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2321- setCORS(w)
2322- f(w, r)
2323- })
2324- }
2325-
2326- r.Options("/*path", wrap(func(w http.ResponseWriter, r *http.Request) {}))
2327-
2328- r.Get("/status", wrap(api.status))
2329- r.Get("/receivers", wrap(api.receivers))
2330-
2331- r.Get("/alerts", wrap(api.listAlerts))
2332- r.Post("/alerts", wrap(api.addAlerts))
2333-
2334- r.Get("/silences", wrap(api.listSilences))
2335- r.Post("/silences", wrap(api.setSilence))
2336- r.Get("/silence/:sid", wrap(api.getSilence))
2337- r.Del("/silence/:sid", wrap(api.delSilence))
2338-}
2339-
2340-// Update sets the configuration string to a new value.
2341-func (api *API) Update(cfg *config.Config) {
2342- api.mtx.Lock()
2343- defer api.mtx.Unlock()
2344-
2345- api.config = cfg
2346- api.route = dispatch.NewRoute(cfg.Route, nil)
2347-}
2348-
2349-type errorType string
2350-
2351-const (
2352- errorInternal errorType = "server_error"
2353- errorBadData errorType = "bad_data"
2354-)
2355-
2356-type apiError struct {
2357- typ errorType
2358- err error
2359-}
2360-
2361-func (e *apiError) Error() string {
2362- return fmt.Sprintf("%s: %s", e.typ, e.err)
2363-}
2364-
2365-func (api *API) receivers(w http.ResponseWriter, req *http.Request) {
2366- api.mtx.RLock()
2367- defer api.mtx.RUnlock()
2368-
2369- receivers := make([]string, 0, len(api.config.Receivers))
2370- for _, r := range api.config.Receivers {
2371- receivers = append(receivers, r.Name)
2372- }
2373-
2374- api.respond(w, receivers)
2375-}
2376-
2377-func (api *API) status(w http.ResponseWriter, req *http.Request) {
2378- api.mtx.RLock()
2379-
2380- var status = struct {
2381- ConfigYAML string `json:"configYAML"`
2382- ConfigJSON *config.Config `json:"configJSON"`
2383- VersionInfo map[string]string `json:"versionInfo"`
2384- Uptime time.Time `json:"uptime"`
2385- ClusterStatus *clusterStatus `json:"clusterStatus"`
2386- }{
2387- ConfigYAML: api.config.String(),
2388- ConfigJSON: api.config,
2389- VersionInfo: map[string]string{
2390- "version": version.Version,
2391- "revision": version.Revision,
2392- "branch": version.Branch,
2393- "buildUser": version.BuildUser,
2394- "buildDate": version.BuildDate,
2395- "goVersion": version.GoVersion,
2396- },
2397- Uptime: api.uptime,
2398- ClusterStatus: getClusterStatus(api.peer),
2399- }
2400-
2401- api.mtx.RUnlock()
2402-
2403- api.respond(w, status)
2404-}
2405-
2406-type peerStatus struct {
2407- Name string `json:"name"`
2408- Address string `json:"address"`
2409-}
2410-
2411-type clusterStatus struct {
2412- Name string `json:"name"`
2413- Status string `json:"status"`
2414- Peers []peerStatus `json:"peers"`
2415-}
2416-
2417-func getClusterStatus(p *cluster.Peer) *clusterStatus {
2418- if p == nil {
2419- return nil
2420- }
2421- s := &clusterStatus{Name: p.Name(), Status: p.Status()}
2422-
2423- for _, n := range p.Peers() {
2424- s.Peers = append(s.Peers, peerStatus{
2425- Name: n.Name,
2426- Address: n.Address(),
2427- })
2428- }
2429- return s
2430-}
2431-
2432-func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
2433- var (
2434- err error
2435- receiverFilter *regexp.Regexp
2436- // Initialize result slice to prevent api returning `null` when there
2437- // are no alerts present
2438- res = []*Alert{}
2439- matchers = []*labels.Matcher{}
2440- ctx = r.Context()
2441-
2442- showActive, showInhibited bool
2443- showSilenced, showUnprocessed bool
2444- )
2445-
2446- getBoolParam := func(name string) (bool, error) {
2447- v := r.FormValue(name)
2448- if v == "" {
2449- return true, nil
2450- }
2451- if v == "false" {
2452- return false, nil
2453- }
2454- if v != "true" {
2455- err := fmt.Errorf("parameter %q can either be 'true' or 'false', not %q", name, v)
2456- api.respondError(w, apiError{
2457- typ: errorBadData,
2458- err: err,
2459- }, nil)
2460- return false, err
2461- }
2462- return true, nil
2463- }
2464-
2465- if filter := r.FormValue("filter"); filter != "" {
2466- matchers, err = labels.ParseMatchers(filter)
2467- if err != nil {
2468- api.respondError(w, apiError{
2469- typ: errorBadData,
2470- err: err,
2471- }, nil)
2472- return
2473- }
2474- }
2475-
2476- showActive, err = getBoolParam("active")
2477- if err != nil {
2478- return
2479- }
2480-
2481- showSilenced, err = getBoolParam("silenced")
2482- if err != nil {
2483- return
2484- }
2485-
2486- showInhibited, err = getBoolParam("inhibited")
2487- if err != nil {
2488- return
2489- }
2490-
2491- showUnprocessed, err = getBoolParam("unprocessed")
2492- if err != nil {
2493- return
2494- }
2495-
2496- if receiverParam := r.FormValue("receiver"); receiverParam != "" {
2497- receiverFilter, err = regexp.Compile("^(?:" + receiverParam + ")$")
2498- if err != nil {
2499- api.respondError(w, apiError{
2500- typ: errorBadData,
2501- err: fmt.Errorf(
2502- "failed to parse receiver param: %s",
2503- receiverParam,
2504- ),
2505- }, nil)
2506- return
2507- }
2508- }
2509-
2510- alerts := api.alerts.GetPending()
2511- defer alerts.Close()
2512-
2513- api.mtx.RLock()
2514- for a := range alerts.Next() {
2515- if err = alerts.Err(); err != nil {
2516- break
2517- }
2518- if err = ctx.Err(); err != nil {
2519- break
2520- }
2521-
2522- routes := api.route.Match(a.Labels)
2523- receivers := make([]string, 0, len(routes))
2524- for _, r := range routes {
2525- receivers = append(receivers, r.RouteOpts.Receiver)
2526- }
2527-
2528- if receiverFilter != nil && !receiversMatchFilter(receivers, receiverFilter) {
2529- continue
2530- }
2531-
2532- if !alertMatchesFilterLabels(&a.Alert, matchers) {
2533- continue
2534- }
2535-
2536- // Continue if the alert is resolved.
2537- if !a.Alert.EndsAt.IsZero() && a.Alert.EndsAt.Before(time.Now()) {
2538- continue
2539- }
2540-
2541- status := api.getAlertStatus(a.Fingerprint())
2542-
2543- if !showActive && status.State == types.AlertStateActive {
2544- continue
2545- }
2546-
2547- if !showUnprocessed && status.State == types.AlertStateUnprocessed {
2548- continue
2549- }
2550-
2551- if !showSilenced && len(status.SilencedBy) != 0 {
2552- continue
2553- }
2554-
2555- if !showInhibited && len(status.InhibitedBy) != 0 {
2556- continue
2557- }
2558-
2559- alert := &Alert{
2560- Alert: &a.Alert,
2561- Status: status,
2562- Receivers: receivers,
2563- Fingerprint: a.Fingerprint().String(),
2564- }
2565-
2566- res = append(res, alert)
2567- }
2568- api.mtx.RUnlock()
2569-
2570- if err != nil {
2571- api.respondError(w, apiError{
2572- typ: errorInternal,
2573- err: err,
2574- }, nil)
2575- return
2576- }
2577- sort.Slice(res, func(i, j int) bool {
2578- return res[i].Fingerprint < res[j].Fingerprint
2579- })
2580- api.respond(w, res)
2581-}
2582-
2583-func receiversMatchFilter(receivers []string, filter *regexp.Regexp) bool {
2584- for _, r := range receivers {
2585- if filter.MatchString(r) {
2586- return true
2587- }
2588- }
2589-
2590- return false
2591-}
2592-
2593-func alertMatchesFilterLabels(a *model.Alert, matchers []*labels.Matcher) bool {
2594- sms := make(map[string]string)
2595- for name, value := range a.Labels {
2596- sms[string(name)] = string(value)
2597- }
2598- return matchFilterLabels(matchers, sms)
2599-}
2600-
2601-func (api *API) addAlerts(w http.ResponseWriter, r *http.Request) {
2602- var alerts []*types.Alert
2603- if err := api.receive(r, &alerts); err != nil {
2604- api.respondError(w, apiError{
2605- typ: errorBadData,
2606- err: err,
2607- }, nil)
2608- return
2609- }
2610-
2611- api.insertAlerts(w, r, alerts...)
2612-}
2613-
2614-func (api *API) insertAlerts(w http.ResponseWriter, r *http.Request, alerts ...*types.Alert) {
2615- now := time.Now()
2616-
2617- api.mtx.RLock()
2618- resolveTimeout := time.Duration(api.config.Global.ResolveTimeout)
2619- api.mtx.RUnlock()
2620-
2621- for _, alert := range alerts {
2622- alert.UpdatedAt = now
2623-
2624- // Ensure StartsAt is set.
2625- if alert.StartsAt.IsZero() {
2626- if alert.EndsAt.IsZero() {
2627- alert.StartsAt = now
2628- } else {
2629- alert.StartsAt = alert.EndsAt
2630- }
2631- }
2632- // If no end time is defined, set a timeout after which an alert
2633- // is marked resolved if it is not updated.
2634- if alert.EndsAt.IsZero() {
2635- alert.Timeout = true
2636- alert.EndsAt = now.Add(resolveTimeout)
2637- }
2638- if alert.EndsAt.After(time.Now()) {
2639- api.m.Firing().Inc()
2640- } else {
2641- api.m.Resolved().Inc()
2642- }
2643- }
2644-
2645- // Make a best effort to insert all alerts that are valid.
2646- var (
2647- validAlerts = make([]*types.Alert, 0, len(alerts))
2648- validationErrs = &types.MultiError{}
2649- )
2650- for _, a := range alerts {
2651- removeEmptyLabels(a.Labels)
2652-
2653- if err := a.Validate(); err != nil {
2654- validationErrs.Add(err)
2655- api.m.Invalid().Inc()
2656- continue
2657- }
2658- validAlerts = append(validAlerts, a)
2659- }
2660- if err := api.alerts.Put(validAlerts...); err != nil {
2661- api.respondError(w, apiError{
2662- typ: errorInternal,
2663- err: err,
2664- }, nil)
2665- return
2666- }
2667-
2668- if validationErrs.Len() > 0 {
2669- api.respondError(w, apiError{
2670- typ: errorBadData,
2671- err: validationErrs,
2672- }, nil)
2673- return
2674- }
2675-
2676- api.respond(w, nil)
2677-}
2678-
2679-func removeEmptyLabels(ls model.LabelSet) {
2680- for k, v := range ls {
2681- if string(v) == "" {
2682- delete(ls, k)
2683- }
2684- }
2685-}
2686-
2687-func (api *API) setSilence(w http.ResponseWriter, r *http.Request) {
2688- var sil types.Silence
2689- if err := api.receive(r, &sil); err != nil {
2690- api.respondError(w, apiError{
2691- typ: errorBadData,
2692- err: err,
2693- }, nil)
2694- return
2695- }
2696-
2697- // This is an API only validation, it cannot be done internally
2698- // because the expired silence is semantically important.
2699- // But one should not be able to create expired silences, that
2700- // won't have any use.
2701- if sil.Expired() {
2702- api.respondError(w, apiError{
2703- typ: errorBadData,
2704- err: errors.New("start time must not be equal to end time"),
2705- }, nil)
2706- return
2707- }
2708-
2709- if sil.EndsAt.Before(time.Now()) {
2710- api.respondError(w, apiError{
2711- typ: errorBadData,
2712- err: errors.New("end time can't be in the past"),
2713- }, nil)
2714- return
2715- }
2716-
2717- psil, err := silenceToProto(&sil)
2718- if err != nil {
2719- api.respondError(w, apiError{
2720- typ: errorBadData,
2721- err: err,
2722- }, nil)
2723- return
2724- }
2725-
2726- sid, err := api.silences.Set(psil)
2727- if err != nil {
2728- api.respondError(w, apiError{
2729- typ: errorBadData,
2730- err: err,
2731- }, nil)
2732- return
2733- }
2734-
2735- api.respond(w, struct {
2736- SilenceID string `json:"silenceId"`
2737- }{
2738- SilenceID: sid,
2739- })
2740-}
2741-
2742-func (api *API) getSilence(w http.ResponseWriter, r *http.Request) {
2743- sid := route.Param(r.Context(), "sid")
2744-
2745- sils, _, err := api.silences.Query(silence.QIDs(sid))
2746- if err != nil || len(sils) == 0 {
2747- http.Error(w, fmt.Sprint("Error getting silence: ", err), http.StatusNotFound)
2748- return
2749- }
2750- sil, err := silenceFromProto(sils[0])
2751- if err != nil {
2752- api.respondError(w, apiError{
2753- typ: errorInternal,
2754- err: err,
2755- }, nil)
2756- return
2757- }
2758-
2759- api.respond(w, sil)
2760-}
2761-
2762-func (api *API) delSilence(w http.ResponseWriter, r *http.Request) {
2763- sid := route.Param(r.Context(), "sid")
2764-
2765- if err := api.silences.Expire(sid); err != nil {
2766- api.respondError(w, apiError{
2767- typ: errorBadData,
2768- err: err,
2769- }, nil)
2770- return
2771- }
2772- api.respond(w, nil)
2773-}
2774-
2775-func (api *API) listSilences(w http.ResponseWriter, r *http.Request) {
2776- psils, _, err := api.silences.Query()
2777- if err != nil {
2778- api.respondError(w, apiError{
2779- typ: errorInternal,
2780- err: err,
2781- }, nil)
2782- return
2783- }
2784-
2785- matchers := []*labels.Matcher{}
2786- if filter := r.FormValue("filter"); filter != "" {
2787- matchers, err = labels.ParseMatchers(filter)
2788- if err != nil {
2789- api.respondError(w, apiError{
2790- typ: errorBadData,
2791- err: err,
2792- }, nil)
2793- return
2794- }
2795- }
2796-
2797- sils := []*types.Silence{}
2798- for _, ps := range psils {
2799- s, err := silenceFromProto(ps)
2800- if err != nil {
2801- api.respondError(w, apiError{
2802- typ: errorInternal,
2803- err: err,
2804- }, nil)
2805- return
2806- }
2807-
2808- if !silenceMatchesFilterLabels(s, matchers) {
2809- continue
2810- }
2811- sils = append(sils, s)
2812- }
2813-
2814- var active, pending, expired []*types.Silence
2815-
2816- for _, s := range sils {
2817- switch s.Status.State {
2818- case types.SilenceStateActive:
2819- active = append(active, s)
2820- case types.SilenceStatePending:
2821- pending = append(pending, s)
2822- case types.SilenceStateExpired:
2823- expired = append(expired, s)
2824- }
2825- }
2826-
2827- sort.Slice(active, func(i int, j int) bool {
2828- return active[i].EndsAt.Before(active[j].EndsAt)
2829- })
2830- sort.Slice(pending, func(i int, j int) bool {
2831- return pending[i].StartsAt.Before(pending[j].EndsAt)
2832- })
2833- sort.Slice(expired, func(i int, j int) bool {
2834- return expired[i].EndsAt.After(expired[j].EndsAt)
2835- })
2836-
2837- // Initialize silences explicitly to an empty list (instead of nil)
2838- // So that it does not get converted to "null" in JSON.
2839- silences := []*types.Silence{}
2840- silences = append(silences, active...)
2841- silences = append(silences, pending...)
2842- silences = append(silences, expired...)
2843-
2844- api.respond(w, silences)
2845-}
2846-
2847-func silenceMatchesFilterLabels(s *types.Silence, matchers []*labels.Matcher) bool {
2848- sms := make(map[string]string)
2849- for _, m := range s.Matchers {
2850- sms[m.Name] = m.Value
2851- }
2852-
2853- return matchFilterLabels(matchers, sms)
2854-}
2855-
2856-func matchFilterLabels(matchers []*labels.Matcher, sms map[string]string) bool {
2857- for _, m := range matchers {
2858- v, prs := sms[m.Name]
2859- switch m.Type {
2860- case labels.MatchNotRegexp, labels.MatchNotEqual:
2861- if string(m.Value) == "" && prs {
2862- continue
2863- }
2864- if !m.Matches(string(v)) {
2865- return false
2866- }
2867- default:
2868- if string(m.Value) == "" && !prs {
2869- continue
2870- }
2871- if !prs || !m.Matches(string(v)) {
2872- return false
2873- }
2874- }
2875- }
2876-
2877- return true
2878-}
2879-
2880-func silenceToProto(s *types.Silence) (*silencepb.Silence, error) {
2881- sil := &silencepb.Silence{
2882- Id: s.ID,
2883- StartsAt: s.StartsAt,
2884- EndsAt: s.EndsAt,
2885- UpdatedAt: s.UpdatedAt,
2886- Comment: s.Comment,
2887- CreatedBy: s.CreatedBy,
2888- }
2889- for _, m := range s.Matchers {
2890- matcher := &silencepb.Matcher{
2891- Name: m.Name,
2892- Pattern: m.Value,
2893- Type: silencepb.Matcher_EQUAL,
2894- }
2895- if m.IsRegex {
2896- matcher.Type = silencepb.Matcher_REGEXP
2897- }
2898- sil.Matchers = append(sil.Matchers, matcher)
2899- }
2900- return sil, nil
2901-}
2902-
2903-func silenceFromProto(s *silencepb.Silence) (*types.Silence, error) {
2904- sil := &types.Silence{
2905- ID: s.Id,
2906- StartsAt: s.StartsAt,
2907- EndsAt: s.EndsAt,
2908- UpdatedAt: s.UpdatedAt,
2909- Status: types.SilenceStatus{
2910- State: types.CalcSilenceState(s.StartsAt, s.EndsAt),
2911- },
2912- Comment: s.Comment,
2913- CreatedBy: s.CreatedBy,
2914- }
2915- for _, m := range s.Matchers {
2916- matcher := &types.Matcher{
2917- Name: m.Name,
2918- Value: m.Pattern,
2919- }
2920- switch m.Type {
2921- case silencepb.Matcher_EQUAL:
2922- case silencepb.Matcher_REGEXP:
2923- matcher.IsRegex = true
2924- default:
2925- return nil, fmt.Errorf("unknown matcher type")
2926- }
2927- sil.Matchers = append(sil.Matchers, matcher)
2928- }
2929-
2930- return sil, nil
2931-}
2932-
2933-type status string
2934-
2935-const (
2936- statusSuccess status = "success"
2937- statusError status = "error"
2938-)
2939-
2940-type response struct {
2941- Status status `json:"status"`
2942- Data interface{} `json:"data,omitempty"`
2943- ErrorType errorType `json:"errorType,omitempty"`
2944- Error string `json:"error,omitempty"`
2945-}
2946-
2947-func (api *API) respond(w http.ResponseWriter, data interface{}) {
2948- w.Header().Set("Content-Type", "application/json")
2949- w.WriteHeader(200)
2950-
2951- b, err := json.Marshal(&response{
2952- Status: statusSuccess,
2953- Data: data,
2954- })
2955- if err != nil {
2956- level.Error(api.logger).Log("msg", "Error marshaling JSON", "err", err)
2957- return
2958- }
2959-
2960- if _, err := w.Write(b); err != nil {
2961- level.Error(api.logger).Log("msg", "failed to write data to connection", "err", err)
2962- }
2963-}
2964-
2965-func (api *API) respondError(w http.ResponseWriter, apiErr apiError, data interface{}) {
2966- w.Header().Set("Content-Type", "application/json")
2967-
2968- switch apiErr.typ {
2969- case errorBadData:
2970- w.WriteHeader(http.StatusBadRequest)
2971- case errorInternal:
2972- w.WriteHeader(http.StatusInternalServerError)
2973- default:
2974- panic(fmt.Sprintf("unknown error type %q", apiErr.Error()))
2975- }
2976-
2977- b, err := json.Marshal(&response{
2978- Status: statusError,
2979- ErrorType: apiErr.typ,
2980- Error: apiErr.err.Error(),
2981- Data: data,
2982- })
2983- if err != nil {
2984- return
2985- }
2986- level.Error(api.logger).Log("msg", "API error", "err", apiErr.Error())
2987-
2988- if _, err := w.Write(b); err != nil {
2989- level.Error(api.logger).Log("msg", "failed to write data to connection", "err", err)
2990- }
2991-}
2992-
2993-func (api *API) receive(r *http.Request, v interface{}) error {
2994- dec := json.NewDecoder(r.Body)
2995- defer r.Body.Close()
2996-
2997- err := dec.Decode(v)
2998- if err != nil {
2999- level.Debug(api.logger).Log("msg", "Decoding request failed", "err", err)
3000- return err
3001- }
3002- return nil
3003-}
3004diff --git a/api/v1/api_test.go b/api/v1/api_test.go
3005deleted file mode 100644
3006index d25f362..0000000
3007--- a/api/v1/api_test.go
3008+++ /dev/null
3009@@ -1,581 +0,0 @@
3010-// Copyright 2018 Prometheus Team
3011-// Licensed under the Apache License, Version 2.0 (the "License");
3012-// you may not use this file except in compliance with the License.
3013-// You may obtain a copy of the License at
3014-//
3015-// http://www.apache.org/licenses/LICENSE-2.0
3016-//
3017-// Unless required by applicable law or agreed to in writing, software
3018-// distributed under the License is distributed on an "AS IS" BASIS,
3019-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3020-// See the License for the specific language governing permissions and
3021-// limitations under the License.
3022-
3023-package v1
3024-
3025-import (
3026- "bytes"
3027- "encoding/json"
3028- "errors"
3029- "fmt"
3030- "io/ioutil"
3031- "net/http"
3032- "net/http/httptest"
3033- "regexp"
3034- "testing"
3035- "time"
3036-
3037- "github.com/prometheus/common/model"
3038- "github.com/stretchr/testify/require"
3039-
3040- "github.com/prometheus/alertmanager/config"
3041- "github.com/prometheus/alertmanager/dispatch"
3042- "github.com/prometheus/alertmanager/pkg/labels"
3043- "github.com/prometheus/alertmanager/provider"
3044- "github.com/prometheus/alertmanager/types"
3045-)
3046-
3047-// fakeAlerts is a struct implementing the provider.Alerts interface for tests.
3048-type fakeAlerts struct {
3049- fps map[model.Fingerprint]int
3050- alerts []*types.Alert
3051- err error
3052-}
3053-
3054-func newFakeAlerts(alerts []*types.Alert, withErr bool) *fakeAlerts {
3055- fps := make(map[model.Fingerprint]int)
3056- for i, a := range alerts {
3057- fps[a.Fingerprint()] = i
3058- }
3059- f := &fakeAlerts{
3060- alerts: alerts,
3061- fps: fps,
3062- }
3063- if withErr {
3064- f.err = errors.New("error occurred")
3065- }
3066- return f
3067-}
3068-
3069-func (f *fakeAlerts) Subscribe() provider.AlertIterator { return nil }
3070-func (f *fakeAlerts) Get(model.Fingerprint) (*types.Alert, error) { return nil, nil }
3071-func (f *fakeAlerts) Put(alerts ...*types.Alert) error {
3072- return f.err
3073-}
3074-func (f *fakeAlerts) GetPending() provider.AlertIterator {
3075- ch := make(chan *types.Alert)
3076- done := make(chan struct{})
3077- go func() {
3078- defer close(ch)
3079- for _, a := range f.alerts {
3080- ch <- a
3081- }
3082- }()
3083- return provider.NewAlertIterator(ch, done, f.err)
3084-}
3085-
3086-func newGetAlertStatus(f *fakeAlerts) func(model.Fingerprint) types.AlertStatus {
3087- return func(fp model.Fingerprint) types.AlertStatus {
3088- status := types.AlertStatus{SilencedBy: []string{}, InhibitedBy: []string{}}
3089-
3090- i, ok := f.fps[fp]
3091- if !ok {
3092- return status
3093- }
3094- alert := f.alerts[i]
3095- switch alert.Labels["state"] {
3096- case "active":
3097- status.State = types.AlertStateActive
3098- case "unprocessed":
3099- status.State = types.AlertStateUnprocessed
3100- case "suppressed":
3101- status.State = types.AlertStateSuppressed
3102- }
3103- if alert.Labels["silenced_by"] != "" {
3104- status.SilencedBy = append(status.SilencedBy, string(alert.Labels["silenced_by"]))
3105- }
3106- if alert.Labels["inhibited_by"] != "" {
3107- status.InhibitedBy = append(status.InhibitedBy, string(alert.Labels["inhibited_by"]))
3108- }
3109- return status
3110- }
3111-}
3112-
3113-func TestAddAlerts(t *testing.T) {
3114- now := func(offset int) time.Time {
3115- return time.Now().Add(time.Duration(offset) * time.Second)
3116- }
3117-
3118- for i, tc := range []struct {
3119- start, end time.Time
3120- err bool
3121- code int
3122- }{
3123- {time.Time{}, time.Time{}, false, 200},
3124- {now(0), time.Time{}, false, 200},
3125- {time.Time{}, now(-1), false, 200},
3126- {time.Time{}, now(0), false, 200},
3127- {time.Time{}, now(1), false, 200},
3128- {now(-2), now(-1), false, 200},
3129- {now(1), now(2), false, 200},
3130- {now(1), now(0), false, 400},
3131- {now(0), time.Time{}, true, 500},
3132- } {
3133- alerts := []model.Alert{{
3134- StartsAt: tc.start,
3135- EndsAt: tc.end,
3136- Labels: model.LabelSet{"label1": "test1"},
3137- Annotations: model.LabelSet{"annotation1": "some text"},
3138- }}
3139- b, err := json.Marshal(&alerts)
3140- if err != nil {
3141- t.Errorf("Unexpected error %v", err)
3142- }
3143-
3144- alertsProvider := newFakeAlerts([]*types.Alert{}, tc.err)
3145- api := New(alertsProvider, nil, newGetAlertStatus(alertsProvider), nil, nil, nil)
3146- defaultGlobalConfig := config.DefaultGlobalConfig()
3147- route := config.Route{}
3148- api.Update(&config.Config{
3149- Global: &defaultGlobalConfig,
3150- Route: &route,
3151- })
3152-
3153- r, err := http.NewRequest("POST", "/api/v1/alerts", bytes.NewReader(b))
3154- w := httptest.NewRecorder()
3155- if err != nil {
3156- t.Errorf("Unexpected error %v", err)
3157- }
3158-
3159- api.addAlerts(w, r)
3160- res := w.Result()
3161- body, _ := ioutil.ReadAll(res.Body)
3162-
3163- require.Equal(t, tc.code, w.Code, fmt.Sprintf("test case: %d, StartsAt %v, EndsAt %v, Response: %s", i, tc.start, tc.end, string(body)))
3164- }
3165-}
3166-
3167-func TestListAlerts(t *testing.T) {
3168- now := time.Now()
3169- alerts := []*types.Alert{
3170- &types.Alert{
3171- Alert: model.Alert{
3172- Labels: model.LabelSet{"state": "active", "alertname": "alert1"},
3173- StartsAt: now.Add(-time.Minute),
3174- },
3175- },
3176- &types.Alert{
3177- Alert: model.Alert{
3178- Labels: model.LabelSet{"state": "unprocessed", "alertname": "alert2"},
3179- StartsAt: now.Add(-time.Minute),
3180- },
3181- },
3182- &types.Alert{
3183- Alert: model.Alert{
3184- Labels: model.LabelSet{"state": "suppressed", "silenced_by": "abc", "alertname": "alert3"},
3185- StartsAt: now.Add(-time.Minute),
3186- },
3187- },
3188- &types.Alert{
3189- Alert: model.Alert{
3190- Labels: model.LabelSet{"state": "suppressed", "inhibited_by": "abc", "alertname": "alert4"},
3191- StartsAt: now.Add(-time.Minute),
3192- },
3193- },
3194- &types.Alert{
3195- Alert: model.Alert{
3196- Labels: model.LabelSet{"alertname": "alert5"},
3197- StartsAt: now.Add(-2 * time.Minute),
3198- EndsAt: now.Add(-time.Minute),
3199- },
3200- },
3201- }
3202-
3203- for i, tc := range []struct {
3204- err bool
3205- params map[string]string
3206-
3207- code int
3208- anames []string
3209- }{
3210- {
3211- false,
3212- map[string]string{},
3213- 200,
3214- []string{"alert1", "alert2", "alert3", "alert4"},
3215- },
3216- {
3217- false,
3218- map[string]string{"active": "true", "unprocessed": "true", "silenced": "true", "inhibited": "true"},
3219- 200,
3220- []string{"alert1", "alert2", "alert3", "alert4"},
3221- },
3222- {
3223- false,
3224- map[string]string{"active": "false", "unprocessed": "true", "silenced": "true", "inhibited": "true"},
3225- 200,
3226- []string{"alert2", "alert3", "alert4"},
3227- },
3228- {
3229- false,
3230- map[string]string{"active": "true", "unprocessed": "false", "silenced": "true", "inhibited": "true"},
3231- 200,
3232- []string{"alert1", "alert3", "alert4"},
3233- },
3234- {
3235- false,
3236- map[string]string{"active": "true", "unprocessed": "true", "silenced": "false", "inhibited": "true"},
3237- 200,
3238- []string{"alert1", "alert2", "alert4"},
3239- },
3240- {
3241- false,
3242- map[string]string{"active": "true", "unprocessed": "true", "silenced": "true", "inhibited": "false"},
3243- 200,
3244- []string{"alert1", "alert2", "alert3"},
3245- },
3246- {
3247- false,
3248- map[string]string{"filter": "{alertname=\"alert3\""},
3249- 200,
3250- []string{"alert3"},
3251- },
3252- {
3253- false,
3254- map[string]string{"filter": "{alertname"},
3255- 400,
3256- []string{},
3257- },
3258- {
3259- false,
3260- map[string]string{"receiver": "other"},
3261- 200,
3262- []string{},
3263- },
3264- {
3265- false,
3266- map[string]string{"active": "invalid"},
3267- 400,
3268- []string{},
3269- },
3270- {
3271- true,
3272- map[string]string{},
3273- 500,
3274- []string{},
3275- },
3276- } {
3277- alertsProvider := newFakeAlerts(alerts, tc.err)
3278- api := New(alertsProvider, nil, newGetAlertStatus(alertsProvider), nil, nil, nil)
3279- api.route = dispatch.NewRoute(&config.Route{Receiver: "def-receiver"}, nil)
3280-
3281- r, err := http.NewRequest("GET", "/api/v1/alerts", nil)
3282- if err != nil {
3283- t.Fatalf("Unexpected error %v", err)
3284- }
3285- q := r.URL.Query()
3286- for k, v := range tc.params {
3287- q.Add(k, v)
3288- }
3289- r.URL.RawQuery = q.Encode()
3290- w := httptest.NewRecorder()
3291-
3292- api.listAlerts(w, r)
3293- body, _ := ioutil.ReadAll(w.Result().Body)
3294-
3295- var res response
3296- err = json.Unmarshal(body, &res)
3297- if err != nil {
3298- t.Fatalf("Unexpected error %v", err)
3299- }
3300-
3301- require.Equal(t, tc.code, w.Code, fmt.Sprintf("test case: %d, response: %s", i, string(body)))
3302- if w.Code != 200 {
3303- continue
3304- }
3305-
3306- // Data needs to be serialized/deserialized to be converted to the real type.
3307- b, err := json.Marshal(res.Data)
3308- if err != nil {
3309- t.Fatalf("Unexpected error %v", err)
3310- }
3311- retAlerts := []*Alert{}
3312- err = json.Unmarshal(b, &retAlerts)
3313- if err != nil {
3314- t.Fatalf("Unexpected error %v", err)
3315- }
3316-
3317- anames := []string{}
3318- for _, a := range retAlerts {
3319- name, ok := a.Labels["alertname"]
3320- if ok {
3321- anames = append(anames, string(name))
3322- }
3323- }
3324- require.Equal(t, tc.anames, anames, fmt.Sprintf("test case: %d, alert names are not equal", i))
3325- }
3326-}
3327-
3328-func TestAlertFiltering(t *testing.T) {
3329- type test struct {
3330- alert *model.Alert
3331- msg string
3332- expected bool
3333- }
3334-
3335- // Equal
3336- equal, err := labels.NewMatcher(labels.MatchEqual, "label1", "test1")
3337- if err != nil {
3338- t.Errorf("Unexpected error %v", err)
3339- }
3340-
3341- tests := []test{
3342- {&model.Alert{Labels: model.LabelSet{"label1": "test1"}}, "label1=test1", true},
3343- {&model.Alert{Labels: model.LabelSet{"label1": "test2"}}, "label1=test2", false},
3344- {&model.Alert{Labels: model.LabelSet{"label2": "test2"}}, "label2=test2", false},
3345- }
3346-
3347- for _, test := range tests {
3348- actual := alertMatchesFilterLabels(test.alert, []*labels.Matcher{equal})
3349- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3350- require.Equal(t, test.expected, actual, msg)
3351- }
3352-
3353- // Not Equal
3354- notEqual, err := labels.NewMatcher(labels.MatchNotEqual, "label1", "test1")
3355- if err != nil {
3356- t.Errorf("Unexpected error %v", err)
3357- }
3358-
3359- tests = []test{
3360- {&model.Alert{Labels: model.LabelSet{"label1": "test1"}}, "label1!=test1", false},
3361- {&model.Alert{Labels: model.LabelSet{"label1": "test2"}}, "label1!=test2", true},
3362- {&model.Alert{Labels: model.LabelSet{"label2": "test2"}}, "label2!=test2", true},
3363- }
3364-
3365- for _, test := range tests {
3366- actual := alertMatchesFilterLabels(test.alert, []*labels.Matcher{notEqual})
3367- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3368- require.Equal(t, test.expected, actual, msg)
3369- }
3370-
3371- // Regexp Equal
3372- regexpEqual, err := labels.NewMatcher(labels.MatchRegexp, "label1", "tes.*")
3373- if err != nil {
3374- t.Errorf("Unexpected error %v", err)
3375- }
3376-
3377- tests = []test{
3378- {&model.Alert{Labels: model.LabelSet{"label1": "test1"}}, "label1=~test1", true},
3379- {&model.Alert{Labels: model.LabelSet{"label1": "test2"}}, "label1=~test2", true},
3380- {&model.Alert{Labels: model.LabelSet{"label2": "test2"}}, "label2=~test2", false},
3381- }
3382-
3383- for _, test := range tests {
3384- actual := alertMatchesFilterLabels(test.alert, []*labels.Matcher{regexpEqual})
3385- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3386- require.Equal(t, test.expected, actual, msg)
3387- }
3388-
3389- // Regexp Not Equal
3390- regexpNotEqual, err := labels.NewMatcher(labels.MatchNotRegexp, "label1", "tes.*")
3391- if err != nil {
3392- t.Errorf("Unexpected error %v", err)
3393- }
3394-
3395- tests = []test{
3396- {&model.Alert{Labels: model.LabelSet{"label1": "test1"}}, "label1!~test1", false},
3397- {&model.Alert{Labels: model.LabelSet{"label1": "test2"}}, "label1!~test2", false},
3398- {&model.Alert{Labels: model.LabelSet{"label2": "test2"}}, "label2!~test2", true},
3399- }
3400-
3401- for _, test := range tests {
3402- actual := alertMatchesFilterLabels(test.alert, []*labels.Matcher{regexpNotEqual})
3403- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3404- require.Equal(t, test.expected, actual, msg)
3405- }
3406-}
3407-
3408-func TestSilenceFiltering(t *testing.T) {
3409- type test struct {
3410- silence *types.Silence
3411- msg string
3412- expected bool
3413- }
3414-
3415- // Equal
3416- equal, err := labels.NewMatcher(labels.MatchEqual, "label1", "test1")
3417- if err != nil {
3418- t.Errorf("Unexpected error %v", err)
3419- }
3420-
3421- tests := []test{
3422- {
3423- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test1"})},
3424- "label1=test1",
3425- true,
3426- },
3427- {
3428- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test2"})},
3429- "label1=test2",
3430- false,
3431- },
3432- {
3433- &types.Silence{Matchers: newMatcher(model.LabelSet{"label2": "test2"})},
3434- "label2=test2",
3435- false,
3436- },
3437- }
3438-
3439- for _, test := range tests {
3440- actual := silenceMatchesFilterLabels(test.silence, []*labels.Matcher{equal})
3441- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3442- require.Equal(t, test.expected, actual, msg)
3443- }
3444-
3445- // Not Equal
3446- notEqual, err := labels.NewMatcher(labels.MatchNotEqual, "label1", "test1")
3447- if err != nil {
3448- t.Errorf("Unexpected error %v", err)
3449- }
3450-
3451- tests = []test{
3452- {
3453- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test1"})},
3454- "label1!=test1",
3455- false,
3456- },
3457- {
3458- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test2"})},
3459- "label1!=test2",
3460- true,
3461- },
3462- {
3463- &types.Silence{Matchers: newMatcher(model.LabelSet{"label2": "test2"})},
3464- "label2!=test2",
3465- true,
3466- },
3467- }
3468-
3469- for _, test := range tests {
3470- actual := silenceMatchesFilterLabels(test.silence, []*labels.Matcher{notEqual})
3471- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3472- require.Equal(t, test.expected, actual, msg)
3473- }
3474-
3475- // Regexp Equal
3476- regexpEqual, err := labels.NewMatcher(labels.MatchRegexp, "label1", "tes.*")
3477- if err != nil {
3478- t.Errorf("Unexpected error %v", err)
3479- }
3480-
3481- tests = []test{
3482- {
3483- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test1"})},
3484- "label1=~test1",
3485- true,
3486- },
3487- {
3488- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test2"})},
3489- "label1=~test2",
3490- true,
3491- },
3492- {
3493- &types.Silence{Matchers: newMatcher(model.LabelSet{"label2": "test2"})},
3494- "label2=~test2",
3495- false,
3496- },
3497- }
3498-
3499- for _, test := range tests {
3500- actual := silenceMatchesFilterLabels(test.silence, []*labels.Matcher{regexpEqual})
3501- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3502- require.Equal(t, test.expected, actual, msg)
3503- }
3504-
3505- // Regexp Not Equal
3506- regexpNotEqual, err := labels.NewMatcher(labels.MatchNotRegexp, "label1", "tes.*")
3507- if err != nil {
3508- t.Errorf("Unexpected error %v", err)
3509- }
3510-
3511- tests = []test{
3512- {
3513- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test1"})},
3514- "label1!~test1",
3515- false,
3516- },
3517- {
3518- &types.Silence{Matchers: newMatcher(model.LabelSet{"label1": "test2"})},
3519- "label1!~test2",
3520- false,
3521- },
3522- {
3523- &types.Silence{Matchers: newMatcher(model.LabelSet{"label2": "test2"})},
3524- "label2!~test2",
3525- true,
3526- },
3527- }
3528-
3529- for _, test := range tests {
3530- actual := silenceMatchesFilterLabels(test.silence, []*labels.Matcher{regexpNotEqual})
3531- msg := fmt.Sprintf("Expected %t for %s", test.expected, test.msg)
3532- require.Equal(t, test.expected, actual, msg)
3533- }
3534-}
3535-
3536-func TestReceiversMatchFilter(t *testing.T) {
3537- receivers := []string{"pagerduty", "slack", "pushover"}
3538-
3539- filter, err := regexp.Compile(fmt.Sprintf("^(?:%s)$", "push.*"))
3540- if err != nil {
3541- t.Errorf("Unexpected error %v", err)
3542- }
3543- require.True(t, receiversMatchFilter(receivers, filter))
3544-
3545- filter, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", "push"))
3546- if err != nil {
3547- t.Errorf("Unexpected error %v", err)
3548- }
3549- require.False(t, receiversMatchFilter(receivers, filter))
3550-}
3551-
3552-func TestMatchFilterLabels(t *testing.T) {
3553- testCases := []struct {
3554- matcher labels.MatchType
3555- expected bool
3556- }{
3557- {labels.MatchEqual, true},
3558- {labels.MatchRegexp, true},
3559- {labels.MatchNotEqual, false},
3560- {labels.MatchNotRegexp, false},
3561- }
3562-
3563- for _, tc := range testCases {
3564- l, err := labels.NewMatcher(tc.matcher, "foo", "")
3565- require.NoError(t, err)
3566- sms := map[string]string{
3567- "baz": "bar",
3568- }
3569- ls := []*labels.Matcher{l}
3570-
3571- require.Equal(t, tc.expected, matchFilterLabels(ls, sms))
3572-
3573- l, err = labels.NewMatcher(tc.matcher, "foo", "")
3574- require.NoError(t, err)
3575- sms = map[string]string{
3576- "baz": "bar",
3577- "foo": "quux",
3578- }
3579- ls = []*labels.Matcher{l}
3580- require.NotEqual(t, tc.expected, matchFilterLabels(ls, sms))
3581- }
3582-}
3583-
3584-func newMatcher(labelSet model.LabelSet) types.Matchers {
3585- matchers := make([]*types.Matcher, 0, len(labelSet))
3586- for key, val := range labelSet {
3587- matchers = append(matchers, types.NewMatcher(key, string(val)))
3588- }
3589- return matchers
3590-}
3591diff --git a/api/v2/api.go b/api/v2/api.go
3592deleted file mode 100644
3593index 6cde197..0000000
3594--- a/api/v2/api.go
3595+++ /dev/null
3596@@ -1,798 +0,0 @@
3597-// Copyright 2018 Prometheus Team
3598-// Licensed under the Apache License, Version 2.0 (the "License");
3599-// you may not use this file except in compliance with the License.
3600-// You may obtain a copy of the License at
3601-//
3602-// http://www.apache.org/licenses/LICENSE-2.0
3603-//
3604-// Unless required by applicable law or agreed to in writing, software
3605-// distributed under the License is distributed on an "AS IS" BASIS,
3606-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3607-// See the License for the specific language governing permissions and
3608-// limitations under the License.
3609-
3610-package v2
3611-
3612-import (
3613- "fmt"
3614- "net/http"
3615- "regexp"
3616- "sort"
3617- "sync"
3618- "time"
3619-
3620- "github.com/go-kit/kit/log"
3621- "github.com/go-kit/kit/log/level"
3622- "github.com/go-openapi/loads"
3623- "github.com/go-openapi/runtime/middleware"
3624- "github.com/go-openapi/strfmt"
3625- "github.com/prometheus/client_golang/prometheus"
3626- prometheus_model "github.com/prometheus/common/model"
3627- "github.com/prometheus/common/version"
3628- "github.com/rs/cors"
3629-
3630- "github.com/prometheus/alertmanager/api/metrics"
3631- open_api_models "github.com/prometheus/alertmanager/api/v2/models"
3632- "github.com/prometheus/alertmanager/api/v2/restapi"
3633- "github.com/prometheus/alertmanager/api/v2/restapi/operations"
3634- alert_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert"
3635- alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup"
3636- general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
3637- receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver"
3638- silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence"
3639- "github.com/prometheus/alertmanager/cluster"
3640- "github.com/prometheus/alertmanager/config"
3641- "github.com/prometheus/alertmanager/dispatch"
3642- "github.com/prometheus/alertmanager/pkg/labels"
3643- "github.com/prometheus/alertmanager/provider"
3644- "github.com/prometheus/alertmanager/silence"
3645- "github.com/prometheus/alertmanager/silence/silencepb"
3646- "github.com/prometheus/alertmanager/types"
3647-)
3648-
3649-// API represents an Alertmanager API v2
3650-type API struct {
3651- peer *cluster.Peer
3652- silences *silence.Silences
3653- alerts provider.Alerts
3654- alertGroups groupsFn
3655- getAlertStatus getAlertStatusFn
3656- uptime time.Time
3657-
3658- // mtx protects alertmanagerConfig, setAlertStatus and route.
3659- mtx sync.RWMutex
3660- // resolveTimeout represents the default resolve timeout that an alert is
3661- // assigned if no end time is specified.
3662- alertmanagerConfig *config.Config
3663- route *dispatch.Route
3664- setAlertStatus setAlertStatusFn
3665-
3666- logger log.Logger
3667- m *metrics.Alerts
3668-
3669- Handler http.Handler
3670-}
3671-
3672-type groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string)
3673-type getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus
3674-type setAlertStatusFn func(prometheus_model.LabelSet)
3675-
3676-// NewAPI returns a new Alertmanager API v2
3677-func NewAPI(
3678- alerts provider.Alerts,
3679- gf groupsFn,
3680- sf getAlertStatusFn,
3681- silences *silence.Silences,
3682- peer *cluster.Peer,
3683- l log.Logger,
3684- r prometheus.Registerer,
3685-) (*API, error) {
3686- api := API{
3687- alerts: alerts,
3688- getAlertStatus: sf,
3689- alertGroups: gf,
3690- peer: peer,
3691- silences: silences,
3692- logger: l,
3693- m: metrics.NewAlerts("v2", r),
3694- uptime: time.Now(),
3695- }
3696-
3697- // Load embedded swagger file.
3698- swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
3699- if err != nil {
3700- return nil, fmt.Errorf("failed to load embedded swagger file: %v", err.Error())
3701- }
3702-
3703- // Create new service API.
3704- openAPI := operations.NewAlertmanagerAPI(swaggerSpec)
3705-
3706- // Skip the redoc middleware, only serving the OpenAPI specification and
3707- // the API itself via RoutesHandler. See:
3708- // https://github.com/go-swagger/go-swagger/issues/1779
3709- openAPI.Middleware = func(b middleware.Builder) http.Handler {
3710- return middleware.Spec("", swaggerSpec.Raw(), openAPI.Context().RoutesHandler(b))
3711- }
3712-
3713- openAPI.AlertGetAlertsHandler = alert_ops.GetAlertsHandlerFunc(api.getAlertsHandler)
3714- openAPI.AlertPostAlertsHandler = alert_ops.PostAlertsHandlerFunc(api.postAlertsHandler)
3715- openAPI.AlertgroupGetAlertGroupsHandler = alertgroup_ops.GetAlertGroupsHandlerFunc(api.getAlertGroupsHandler)
3716- openAPI.GeneralGetStatusHandler = general_ops.GetStatusHandlerFunc(api.getStatusHandler)
3717- openAPI.ReceiverGetReceiversHandler = receiver_ops.GetReceiversHandlerFunc(api.getReceiversHandler)
3718- openAPI.SilenceDeleteSilenceHandler = silence_ops.DeleteSilenceHandlerFunc(api.deleteSilenceHandler)
3719- openAPI.SilenceGetSilenceHandler = silence_ops.GetSilenceHandlerFunc(api.getSilenceHandler)
3720- openAPI.SilenceGetSilencesHandler = silence_ops.GetSilencesHandlerFunc(api.getSilencesHandler)
3721- openAPI.SilencePostSilencesHandler = silence_ops.PostSilencesHandlerFunc(api.postSilencesHandler)
3722-
3723- handleCORS := cors.Default().Handler
3724- api.Handler = handleCORS(openAPI.Serve(nil))
3725-
3726- return &api, nil
3727-}
3728-
3729-func (api *API) requestLogger(req *http.Request) log.Logger {
3730- return log.With(api.logger, "path", req.URL.Path, "method", req.Method)
3731-}
3732-
3733-// Update sets the API struct members that may change between reloads of alertmanager.
3734-func (api *API) Update(cfg *config.Config, setAlertStatus setAlertStatusFn) {
3735- api.mtx.Lock()
3736- defer api.mtx.Unlock()
3737-
3738- api.alertmanagerConfig = cfg
3739- api.route = dispatch.NewRoute(cfg.Route, nil)
3740- api.setAlertStatus = setAlertStatus
3741-}
3742-
3743-func (api *API) getStatusHandler(params general_ops.GetStatusParams) middleware.Responder {
3744- api.mtx.RLock()
3745- defer api.mtx.RUnlock()
3746-
3747- original := api.alertmanagerConfig.String()
3748- uptime := strfmt.DateTime(api.uptime)
3749-
3750- status := open_api_models.ClusterStatusStatusDisabled
3751-
3752- resp := open_api_models.AlertmanagerStatus{
3753- Uptime: &uptime,
3754- VersionInfo: &open_api_models.VersionInfo{
3755- Version: &version.Version,
3756- Revision: &version.Revision,
3757- Branch: &version.Branch,
3758- BuildUser: &version.BuildUser,
3759- BuildDate: &version.BuildDate,
3760- GoVersion: &version.GoVersion,
3761- },
3762- Config: &open_api_models.AlertmanagerConfig{
3763- Original: &original,
3764- },
3765- Cluster: &open_api_models.ClusterStatus{
3766- Status: &status,
3767- Peers: []*open_api_models.PeerStatus{},
3768- },
3769- }
3770-
3771- // If alertmanager cluster feature is disabled, then api.peers == nil.
3772- if api.peer != nil {
3773- status := api.peer.Status()
3774-
3775- peers := []*open_api_models.PeerStatus{}
3776- for _, n := range api.peer.Peers() {
3777- address := n.Address()
3778- peers = append(peers, &open_api_models.PeerStatus{
3779- Name: &n.Name,
3780- Address: &address,
3781- })
3782- }
3783-
3784- sort.Slice(peers, func(i, j int) bool {
3785- return *peers[i].Name < *peers[j].Name
3786- })
3787-
3788- resp.Cluster = &open_api_models.ClusterStatus{
3789- Name: api.peer.Name(),
3790- Status: &status,
3791- Peers: peers,
3792- }
3793- }
3794-
3795- return general_ops.NewGetStatusOK().WithPayload(&resp)
3796-}
3797-
3798-func (api *API) getReceiversHandler(params receiver_ops.GetReceiversParams) middleware.Responder {
3799- api.mtx.RLock()
3800- defer api.mtx.RUnlock()
3801-
3802- receivers := make([]*open_api_models.Receiver, 0, len(api.alertmanagerConfig.Receivers))
3803- for _, r := range api.alertmanagerConfig.Receivers {
3804- receivers = append(receivers, &open_api_models.Receiver{Name: &r.Name})
3805- }
3806-
3807- return receiver_ops.NewGetReceiversOK().WithPayload(receivers)
3808-}
3809-
3810-func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Responder {
3811- var (
3812- receiverFilter *regexp.Regexp
3813- // Initialize result slice to prevent api returning `null` when there
3814- // are no alerts present
3815- res = open_api_models.GettableAlerts{}
3816- ctx = params.HTTPRequest.Context()
3817-
3818- logger = api.requestLogger(params.HTTPRequest)
3819- )
3820-
3821- matchers, err := parseFilter(params.Filter)
3822- if err != nil {
3823- level.Error(logger).Log("msg", "Failed to parse matchers", "err", err)
3824- return alertgroup_ops.NewGetAlertGroupsBadRequest().WithPayload(err.Error())
3825- }
3826-
3827- if params.Receiver != nil {
3828- receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$")
3829- if err != nil {
3830- level.Error(logger).Log("msg", "Failed to compile receiver regex", "err", err)
3831- return alert_ops.
3832- NewGetAlertsBadRequest().
3833- WithPayload(
3834- fmt.Sprintf("failed to parse receiver param: %v", err.Error()),
3835- )
3836- }
3837- }
3838-
3839- alerts := api.alerts.GetPending()
3840- defer alerts.Close()
3841-
3842- alertFilter := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
3843- now := time.Now()
3844-
3845- api.mtx.RLock()
3846- for a := range alerts.Next() {
3847- if err = alerts.Err(); err != nil {
3848- break
3849- }
3850- if err = ctx.Err(); err != nil {
3851- break
3852- }
3853-
3854- routes := api.route.Match(a.Labels)
3855- receivers := make([]string, 0, len(routes))
3856- for _, r := range routes {
3857- receivers = append(receivers, r.RouteOpts.Receiver)
3858- }
3859-
3860- if receiverFilter != nil && !receiversMatchFilter(receivers, receiverFilter) {
3861- continue
3862- }
3863-
3864- if !alertFilter(a, now) {
3865- continue
3866- }
3867-
3868- alert := alertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
3869-
3870- res = append(res, alert)
3871- }
3872- api.mtx.RUnlock()
3873-
3874- if err != nil {
3875- level.Error(logger).Log("msg", "Failed to get alerts", "err", err)
3876- return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error())
3877- }
3878- sort.Slice(res, func(i, j int) bool {
3879- return *res[i].Fingerprint < *res[j].Fingerprint
3880- })
3881-
3882- return alert_ops.NewGetAlertsOK().WithPayload(res)
3883-}
3884-
3885-func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware.Responder {
3886- logger := api.requestLogger(params.HTTPRequest)
3887-
3888- alerts := openAPIAlertsToAlerts(params.Alerts)
3889- now := time.Now()
3890-
3891- api.mtx.RLock()
3892- resolveTimeout := time.Duration(api.alertmanagerConfig.Global.ResolveTimeout)
3893- api.mtx.RUnlock()
3894-
3895- for _, alert := range alerts {
3896- alert.UpdatedAt = now
3897-
3898- // Ensure StartsAt is set.
3899- if alert.StartsAt.IsZero() {
3900- if alert.EndsAt.IsZero() {
3901- alert.StartsAt = now
3902- } else {
3903- alert.StartsAt = alert.EndsAt
3904- }
3905- }
3906- // If no end time is defined, set a timeout after which an alert
3907- // is marked resolved if it is not updated.
3908- if alert.EndsAt.IsZero() {
3909- alert.Timeout = true
3910- alert.EndsAt = now.Add(resolveTimeout)
3911- }
3912- if alert.EndsAt.After(time.Now()) {
3913- api.m.Firing().Inc()
3914- } else {
3915- api.m.Resolved().Inc()
3916- }
3917- }
3918-
3919- // Make a best effort to insert all alerts that are valid.
3920- var (
3921- validAlerts = make([]*types.Alert, 0, len(alerts))
3922- validationErrs = &types.MultiError{}
3923- )
3924- for _, a := range alerts {
3925- removeEmptyLabels(a.Labels)
3926-
3927- if err := a.Validate(); err != nil {
3928- validationErrs.Add(err)
3929- api.m.Invalid().Inc()
3930- continue
3931- }
3932- validAlerts = append(validAlerts, a)
3933- }
3934- if err := api.alerts.Put(validAlerts...); err != nil {
3935- level.Error(logger).Log("msg", "Failed to create alerts", "err", err)
3936- return alert_ops.NewPostAlertsInternalServerError().WithPayload(err.Error())
3937- }
3938-
3939- if validationErrs.Len() > 0 {
3940- level.Error(logger).Log("msg", "Failed to validate alerts", "err", validationErrs.Error())
3941- return alert_ops.NewPostAlertsBadRequest().WithPayload(validationErrs.Error())
3942- }
3943-
3944- return alert_ops.NewPostAlertsOK()
3945-}
3946-
3947-func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams) middleware.Responder {
3948- logger := api.requestLogger(params.HTTPRequest)
3949-
3950- matchers, err := parseFilter(params.Filter)
3951- if err != nil {
3952- level.Error(logger).Log("msg", "Failed to parse matchers", "err", err)
3953- return alertgroup_ops.NewGetAlertGroupsBadRequest().WithPayload(err.Error())
3954- }
3955-
3956- var receiverFilter *regexp.Regexp
3957- if params.Receiver != nil {
3958- receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$")
3959- if err != nil {
3960- level.Error(logger).Log("msg", "Failed to compile receiver regex", "err", err)
3961- return alertgroup_ops.
3962- NewGetAlertGroupsBadRequest().
3963- WithPayload(
3964- fmt.Sprintf("failed to parse receiver param: %v", err.Error()),
3965- )
3966- }
3967- }
3968-
3969- rf := func(receiverFilter *regexp.Regexp) func(r *dispatch.Route) bool {
3970- return func(r *dispatch.Route) bool {
3971- receiver := r.RouteOpts.Receiver
3972- if receiverFilter != nil && !receiverFilter.MatchString(receiver) {
3973- return false
3974- }
3975- return true
3976- }
3977- }(receiverFilter)
3978-
3979- af := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
3980- alertGroups, allReceivers := api.alertGroups(rf, af)
3981-
3982- res := make(open_api_models.AlertGroups, 0, len(alertGroups))
3983-
3984- for _, alertGroup := range alertGroups {
3985- ag := &open_api_models.AlertGroup{
3986- Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver},
3987- Labels: modelLabelSetToAPILabelSet(alertGroup.Labels),
3988- Alerts: make([]*open_api_models.GettableAlert, 0, len(alertGroup.Alerts)),
3989- }
3990-
3991- for _, alert := range alertGroup.Alerts {
3992- fp := alert.Fingerprint()
3993- receivers := allReceivers[fp]
3994- status := api.getAlertStatus(fp)
3995- apiAlert := alertToOpenAPIAlert(alert, status, receivers)
3996- ag.Alerts = append(ag.Alerts, apiAlert)
3997- }
3998- res = append(res, ag)
3999- }
4000-
4001- return alertgroup_ops.NewGetAlertGroupsOK().WithPayload(res)
4002-}
4003-
4004-func (api *API) alertFilter(matchers []*labels.Matcher, silenced, inhibited, active bool) func(a *types.Alert, now time.Time) bool {
4005- return func(a *types.Alert, now time.Time) bool {
4006- if !a.EndsAt.IsZero() && a.EndsAt.Before(now) {
4007- return false
4008- }
4009-
4010- // Set alert's current status based on its label set.
4011- api.setAlertStatus(a.Labels)
4012-
4013- // Get alert's current status after seeing if it is suppressed.
4014- status := api.getAlertStatus(a.Fingerprint())
4015-
4016- if !active && status.State == types.AlertStateActive {
4017- return false
4018- }
4019-
4020- if !silenced && len(status.SilencedBy) != 0 {
4021- return false
4022- }
4023-
4024- if !inhibited && len(status.InhibitedBy) != 0 {
4025- return false
4026- }
4027-
4028- return alertMatchesFilterLabels(&a.Alert, matchers)
4029- }
4030-}
4031-
4032-func alertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers []string) *open_api_models.GettableAlert {
4033- startsAt := strfmt.DateTime(alert.StartsAt)
4034- updatedAt := strfmt.DateTime(alert.UpdatedAt)
4035- endsAt := strfmt.DateTime(alert.EndsAt)
4036-
4037- apiReceivers := make([]*open_api_models.Receiver, 0, len(receivers))
4038- for i := range receivers {
4039- apiReceivers = append(apiReceivers, &open_api_models.Receiver{Name: &receivers[i]})
4040- }
4041-
4042- fp := alert.Fingerprint().String()
4043- state := string(status.State)
4044- aa := &open_api_models.GettableAlert{
4045- Alert: open_api_models.Alert{
4046- GeneratorURL: strfmt.URI(alert.GeneratorURL),
4047- Labels: modelLabelSetToAPILabelSet(alert.Labels),
4048- },
4049- Annotations: modelLabelSetToAPILabelSet(alert.Annotations),
4050- StartsAt: &startsAt,
4051- UpdatedAt: &updatedAt,
4052- EndsAt: &endsAt,
4053- Fingerprint: &fp,
4054- Receivers: apiReceivers,
4055- Status: &open_api_models.AlertStatus{
4056- State: &state,
4057- SilencedBy: status.SilencedBy,
4058- InhibitedBy: status.InhibitedBy,
4059- },
4060- }
4061-
4062- if aa.Status.SilencedBy == nil {
4063- aa.Status.SilencedBy = []string{}
4064- }
4065-
4066- if aa.Status.InhibitedBy == nil {
4067- aa.Status.InhibitedBy = []string{}
4068- }
4069-
4070- return aa
4071-}
4072-
4073-func openAPIAlertsToAlerts(apiAlerts open_api_models.PostableAlerts) []*types.Alert {
4074- alerts := []*types.Alert{}
4075- for _, apiAlert := range apiAlerts {
4076- alert := types.Alert{
4077- Alert: prometheus_model.Alert{
4078- Labels: apiLabelSetToModelLabelSet(apiAlert.Labels),
4079- Annotations: apiLabelSetToModelLabelSet(apiAlert.Annotations),
4080- StartsAt: time.Time(apiAlert.StartsAt),
4081- EndsAt: time.Time(apiAlert.EndsAt),
4082- GeneratorURL: string(apiAlert.GeneratorURL),
4083- },
4084- }
4085- alerts = append(alerts, &alert)
4086- }
4087-
4088- return alerts
4089-}
4090-
4091-func removeEmptyLabels(ls prometheus_model.LabelSet) {
4092- for k, v := range ls {
4093- if string(v) == "" {
4094- delete(ls, k)
4095- }
4096- }
4097-}
4098-
4099-func modelLabelSetToAPILabelSet(modelLabelSet prometheus_model.LabelSet) open_api_models.LabelSet {
4100- apiLabelSet := open_api_models.LabelSet{}
4101- for key, value := range modelLabelSet {
4102- apiLabelSet[string(key)] = string(value)
4103- }
4104-
4105- return apiLabelSet
4106-}
4107-
4108-func apiLabelSetToModelLabelSet(apiLabelSet open_api_models.LabelSet) prometheus_model.LabelSet {
4109- modelLabelSet := prometheus_model.LabelSet{}
4110- for key, value := range apiLabelSet {
4111- modelLabelSet[prometheus_model.LabelName(key)] = prometheus_model.LabelValue(value)
4112- }
4113-
4114- return modelLabelSet
4115-}
4116-
4117-func receiversMatchFilter(receivers []string, filter *regexp.Regexp) bool {
4118- for _, r := range receivers {
4119- if filter.MatchString(r) {
4120- return true
4121- }
4122- }
4123-
4124- return false
4125-}
4126-
4127-func alertMatchesFilterLabels(a *prometheus_model.Alert, matchers []*labels.Matcher) bool {
4128- sms := make(map[string]string)
4129- for name, value := range a.Labels {
4130- sms[string(name)] = string(value)
4131- }
4132- return matchFilterLabels(matchers, sms)
4133-}
4134-
4135-func matchFilterLabels(matchers []*labels.Matcher, sms map[string]string) bool {
4136- for _, m := range matchers {
4137- v, prs := sms[m.Name]
4138- switch m.Type {
4139- case labels.MatchNotRegexp, labels.MatchNotEqual:
4140- if m.Value == "" && prs {
4141- continue
4142- }
4143- if !m.Matches(v) {
4144- return false
4145- }
4146- default:
4147- if m.Value == "" && !prs {
4148- continue
4149- }
4150- if !prs || !m.Matches(v) {
4151- return false
4152- }
4153- }
4154- }
4155-
4156- return true
4157-}
4158-
4159-func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middleware.Responder {
4160- logger := api.requestLogger(params.HTTPRequest)
4161-
4162- matchers := []*labels.Matcher{}
4163- if params.Filter != nil {
4164- for _, matcherString := range params.Filter {
4165- matcher, err := labels.ParseMatcher(matcherString)
4166- if err != nil {
4167- level.Error(logger).Log("msg", "Failed to parse matchers", "err", err)
4168- return alert_ops.NewGetAlertsBadRequest().WithPayload(err.Error())
4169- }
4170-
4171- matchers = append(matchers, matcher)
4172- }
4173- }
4174-
4175- psils, _, err := api.silences.Query()
4176- if err != nil {
4177- level.Error(logger).Log("msg", "Failed to get silences", "err", err)
4178- return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error())
4179- }
4180-
4181- sils := open_api_models.GettableSilences{}
4182- for _, ps := range psils {
4183- silence, err := gettableSilenceFromProto(ps)
4184- if err != nil {
4185- level.Error(logger).Log("msg", "Failed to unmarshal silence from proto", "err", err)
4186- return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error())
4187- }
4188- if !gettableSilenceMatchesFilterLabels(silence, matchers) {
4189- continue
4190- }
4191- sils = append(sils, &silence)
4192- }
4193-
4194- sortSilences(sils)
4195-
4196- return silence_ops.NewGetSilencesOK().WithPayload(sils)
4197-}
4198-
4199-var (
4200- silenceStateOrder = map[types.SilenceState]int{
4201- types.SilenceStateActive: 1,
4202- types.SilenceStatePending: 2,
4203- types.SilenceStateExpired: 3,
4204- }
4205-)
4206-
4207-// sortSilences sorts first according to the state "active, pending, expired"
4208-// then by end time or start time depending on the state.
4209-// active silences should show the next to expire first
4210-// pending silences are ordered based on which one starts next
4211-// expired are ordered based on which one expired most recently
4212-func sortSilences(sils open_api_models.GettableSilences) {
4213- sort.Slice(sils, func(i, j int) bool {
4214- state1 := types.SilenceState(*sils[i].Status.State)
4215- state2 := types.SilenceState(*sils[j].Status.State)
4216- if state1 != state2 {
4217- return silenceStateOrder[state1] < silenceStateOrder[state2]
4218- }
4219- switch state1 {
4220- case types.SilenceStateActive:
4221- endsAt1 := time.Time(*sils[i].Silence.EndsAt)
4222- endsAt2 := time.Time(*sils[j].Silence.EndsAt)
4223- return endsAt1.Before(endsAt2)
4224- case types.SilenceStatePending:
4225- startsAt1 := time.Time(*sils[i].Silence.StartsAt)
4226- startsAt2 := time.Time(*sils[j].Silence.StartsAt)
4227- return startsAt1.Before(startsAt2)
4228- case types.SilenceStateExpired:
4229- endsAt1 := time.Time(*sils[i].Silence.EndsAt)
4230- endsAt2 := time.Time(*sils[j].Silence.EndsAt)
4231- return endsAt1.After(endsAt2)
4232- }
4233- return false
4234- })
4235-}
4236-
4237-func gettableSilenceMatchesFilterLabels(s open_api_models.GettableSilence, matchers []*labels.Matcher) bool {
4238- sms := make(map[string]string)
4239- for _, m := range s.Matchers {
4240- sms[*m.Name] = *m.Value
4241- }
4242-
4243- return matchFilterLabels(matchers, sms)
4244-}
4245-
4246-func (api *API) getSilenceHandler(params silence_ops.GetSilenceParams) middleware.Responder {
4247- logger := api.requestLogger(params.HTTPRequest)
4248-
4249- sils, _, err := api.silences.Query(silence.QIDs(params.SilenceID.String()))
4250- if err != nil {
4251- level.Error(logger).Log("msg", "Failed to get silence by id", "err", err, "id", params.SilenceID.String())
4252- return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error())
4253- }
4254-
4255- if len(sils) == 0 {
4256- level.Error(logger).Log("msg", "Failed to find silence", "err", err, "id", params.SilenceID.String())
4257- return silence_ops.NewGetSilenceNotFound()
4258- }
4259-
4260- sil, err := gettableSilenceFromProto(sils[0])
4261- if err != nil {
4262- level.Error(logger).Log("msg", "Failed to convert unmarshal from proto", "err", err)
4263- return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error())
4264- }
4265-
4266- return silence_ops.NewGetSilenceOK().WithPayload(&sil)
4267-}
4268-
4269-func (api *API) deleteSilenceHandler(params silence_ops.DeleteSilenceParams) middleware.Responder {
4270- logger := api.requestLogger(params.HTTPRequest)
4271-
4272- sid := params.SilenceID.String()
4273- if err := api.silences.Expire(sid); err != nil {
4274- level.Error(logger).Log("msg", "Failed to expire silence", "err", err)
4275- return silence_ops.NewDeleteSilenceInternalServerError().WithPayload(err.Error())
4276- }
4277- return silence_ops.NewDeleteSilenceOK()
4278-}
4279-
4280-func gettableSilenceFromProto(s *silencepb.Silence) (open_api_models.GettableSilence, error) {
4281- start := strfmt.DateTime(s.StartsAt)
4282- end := strfmt.DateTime(s.EndsAt)
4283- updated := strfmt.DateTime(s.UpdatedAt)
4284- state := string(types.CalcSilenceState(s.StartsAt, s.EndsAt))
4285- sil := open_api_models.GettableSilence{
4286- Silence: open_api_models.Silence{
4287- StartsAt: &start,
4288- EndsAt: &end,
4289- Comment: &s.Comment,
4290- CreatedBy: &s.CreatedBy,
4291- },
4292- ID: &s.Id,
4293- UpdatedAt: &updated,
4294- Status: &open_api_models.SilenceStatus{
4295- State: &state,
4296- },
4297- }
4298-
4299- for _, m := range s.Matchers {
4300- matcher := &open_api_models.Matcher{
4301- Name: &m.Name,
4302- Value: &m.Pattern,
4303- }
4304- switch m.Type {
4305- case silencepb.Matcher_EQUAL:
4306- f := false
4307- matcher.IsRegex = &f
4308- case silencepb.Matcher_REGEXP:
4309- t := true
4310- matcher.IsRegex = &t
4311- default:
4312- return sil, fmt.Errorf(
4313- "unknown matcher type for matcher '%v' in silence '%v'",
4314- m.Name,
4315- s.Id,
4316- )
4317- }
4318- sil.Matchers = append(sil.Matchers, matcher)
4319- }
4320-
4321- return sil, nil
4322-}
4323-
4324-func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middleware.Responder {
4325- logger := api.requestLogger(params.HTTPRequest)
4326-
4327- sil, err := postableSilenceToProto(params.Silence)
4328- if err != nil {
4329- level.Error(logger).Log("msg", "Failed to marshal silence to proto", "err", err)
4330- return silence_ops.NewPostSilencesBadRequest().WithPayload(
4331- fmt.Sprintf("failed to convert API silence to internal silence: %v", err.Error()),
4332- )
4333- }
4334-
4335- if sil.StartsAt.After(sil.EndsAt) || sil.StartsAt.Equal(sil.EndsAt) {
4336- msg := "Failed to create silence: start time must be before end time"
4337- level.Error(logger).Log("msg", msg, "starts_at", sil.StartsAt, "ends_at", sil.EndsAt)
4338- return silence_ops.NewPostSilencesBadRequest().WithPayload(msg)
4339- }
4340-
4341- if sil.EndsAt.Before(time.Now()) {
4342- msg := "Failed to create silence: end time can't be in the past"
4343- level.Error(logger).Log("msg", msg, "ends_at", sil.EndsAt)
4344- return silence_ops.NewPostSilencesBadRequest().WithPayload(msg)
4345- }
4346-
4347- sid, err := api.silences.Set(sil)
4348- if err != nil {
4349- level.Error(logger).Log("msg", "Failed to create silence", "err", err)
4350- if err == silence.ErrNotFound {
4351- return silence_ops.NewPostSilencesNotFound().WithPayload(err.Error())
4352- }
4353- return silence_ops.NewPostSilencesBadRequest().WithPayload(err.Error())
4354- }
4355-
4356- return silence_ops.NewPostSilencesOK().WithPayload(&silence_ops.PostSilencesOKBody{
4357- SilenceID: sid,
4358- })
4359-}
4360-
4361-func postableSilenceToProto(s *open_api_models.PostableSilence) (*silencepb.Silence, error) {
4362- sil := &silencepb.Silence{
4363- Id: s.ID,
4364- StartsAt: time.Time(*s.StartsAt),
4365- EndsAt: time.Time(*s.EndsAt),
4366- Comment: *s.Comment,
4367- CreatedBy: *s.CreatedBy,
4368- }
4369- for _, m := range s.Matchers {
4370- matcher := &silencepb.Matcher{
4371- Name: *m.Name,
4372- Pattern: *m.Value,
4373- Type: silencepb.Matcher_EQUAL,
4374- }
4375- if *m.IsRegex {
4376- matcher.Type = silencepb.Matcher_REGEXP
4377- }
4378- sil.Matchers = append(sil.Matchers, matcher)
4379- }
4380- return sil, nil
4381-}
4382-
4383-func parseFilter(filter []string) ([]*labels.Matcher, error) {
4384- matchers := make([]*labels.Matcher, 0, len(filter))
4385- for _, matcherString := range filter {
4386- matcher, err := labels.ParseMatcher(matcherString)
4387- if err != nil {
4388- return nil, err
4389- }
4390-
4391- matchers = append(matchers, matcher)
4392- }
4393- return matchers, nil
4394-}
4395diff --git a/api/v2/api_test.go b/api/v2/api_test.go
4396deleted file mode 100644
4397index 4cb4b74..0000000
4398--- a/api/v2/api_test.go
4399+++ /dev/null
4400@@ -1,173 +0,0 @@
4401-// Copyright 2019 Prometheus Team
4402-// Licensed under the Apache License, Version 2.0 (the "License");
4403-// you may not use this file except in compliance with the License.
4404-// You may obtain a copy of the License at
4405-//
4406-// http://www.apache.org/licenses/LICENSE-2.0
4407-//
4408-// Unless required by applicable law or agreed to in writing, software
4409-// distributed under the License is distributed on an "AS IS" BASIS,
4410-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4411-// See the License for the specific language governing permissions and
4412-// limitations under the License.
4413-
4414-package v2
4415-
4416-import (
4417- "strconv"
4418- "testing"
4419- "time"
4420-
4421- "github.com/go-openapi/strfmt"
4422- "github.com/prometheus/common/model"
4423- "github.com/stretchr/testify/require"
4424-
4425- open_api_models "github.com/prometheus/alertmanager/api/v2/models"
4426- general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
4427- "github.com/prometheus/alertmanager/config"
4428- "github.com/prometheus/alertmanager/types"
4429-)
4430-
4431-// If api.peers == nil, Alertmanager cluster feature is disabled. Make sure to
4432-// not try to access properties of peer, which would trigger a nil pointer
4433-// dereference.
4434-func TestGetStatusHandlerWithNilPeer(t *testing.T) {
4435- api := API{
4436- uptime: time.Now(),
4437- peer: nil,
4438- alertmanagerConfig: &config.Config{},
4439- }
4440-
4441- // Test ensures this method call does not panic.
4442- status := api.getStatusHandler(general_ops.GetStatusParams{}).(*general_ops.GetStatusOK)
4443-
4444- c := status.Payload.Cluster
4445-
4446- if c == nil || c.Status == nil {
4447- t.Fatal("expected cluster status not to be nil, violating the openapi specification")
4448- }
4449-
4450- if c.Peers == nil {
4451- t.Fatal("expected cluster peers to be not nil when api.peer is nil, violating the openapi specification")
4452- }
4453- if len(c.Peers) != 0 {
4454- t.Fatal("expected cluster peers to be empty when api.peer is nil, violating the openapi specification")
4455- }
4456-
4457- if c.Name != "" {
4458- t.Fatal("expected cluster name to be empty, violating the openapi specification")
4459- }
4460-}
4461-
4462-func assertEqualStrings(t *testing.T, expected string, actual string) {
4463- if expected != actual {
4464- t.Fatal("expected: ", expected, ", actual: ", actual)
4465- }
4466-}
4467-
4468-var (
4469- testComment = "comment"
4470- createdBy = "test"
4471-)
4472-
4473-func gettableSilence(id string, state string,
4474- updatedAt string, start string, end string,
4475-) *open_api_models.GettableSilence {
4476-
4477- updAt, err := strfmt.ParseDateTime(updatedAt)
4478- if err != nil {
4479- panic(err)
4480- }
4481- strAt, err := strfmt.ParseDateTime(start)
4482- if err != nil {
4483- panic(err)
4484- }
4485- endAt, err := strfmt.ParseDateTime(end)
4486- if err != nil {
4487- panic(err)
4488- }
4489- return &open_api_models.GettableSilence{
4490- Silence: open_api_models.Silence{
4491- StartsAt: &strAt,
4492- EndsAt: &endAt,
4493- Comment: &testComment,
4494- CreatedBy: &createdBy,
4495- },
4496- ID: &id,
4497- UpdatedAt: &updAt,
4498- Status: &open_api_models.SilenceStatus{
4499- State: &state,
4500- },
4501- }
4502-}
4503-
4504-func TestGetSilencesHandler(t *testing.T) {
4505-
4506- updateTime := "2019-01-01T12:00:00+00:00"
4507- silences := []*open_api_models.GettableSilence{
4508- gettableSilence("silence-6-expired", "expired", updateTime,
4509- "2019-01-01T12:00:00+00:00", "2019-01-01T11:00:00+00:00"),
4510- gettableSilence("silence-1-active", "active", updateTime,
4511- "2019-01-01T12:00:00+00:00", "2019-01-01T13:00:00+00:00"),
4512- gettableSilence("silence-7-expired", "expired", updateTime,
4513- "2019-01-01T12:00:00+00:00", "2019-01-01T10:00:00+00:00"),
4514- gettableSilence("silence-5-expired", "expired", updateTime,
4515- "2019-01-01T12:00:00+00:00", "2019-01-01T12:00:00+00:00"),
4516- gettableSilence("silence-0-active", "active", updateTime,
4517- "2019-01-01T12:00:00+00:00", "2019-01-01T12:00:00+00:00"),
4518- gettableSilence("silence-4-pending", "pending", updateTime,
4519- "2019-01-01T13:00:00+00:00", "2019-01-01T12:00:00+00:00"),
4520- gettableSilence("silence-3-pending", "pending", updateTime,
4521- "2019-01-01T12:00:00+00:00", "2019-01-01T12:00:00+00:00"),
4522- gettableSilence("silence-2-active", "active", updateTime,
4523- "2019-01-01T12:00:00+00:00", "2019-01-01T14:00:00+00:00"),
4524- }
4525- sortSilences(open_api_models.GettableSilences(silences))
4526-
4527- for i, sil := range silences {
4528- assertEqualStrings(t, "silence-"+strconv.Itoa(i)+"-"+*sil.Status.State, *sil.ID)
4529- }
4530-}
4531-
4532-func convertDateTime(ts time.Time) *strfmt.DateTime {
4533- dt := strfmt.DateTime(ts)
4534- return &dt
4535-}
4536-
4537-func TestAlertToOpenAPIAlert(t *testing.T) {
4538- var (
4539- start = time.Now().Add(-time.Minute)
4540- updated = time.Now()
4541- active = "active"
4542- fp = "0223b772b51c29e1"
4543- receivers = []string{"receiver1", "receiver2"}
4544-
4545- alert = &types.Alert{
4546- Alert: model.Alert{
4547- Labels: model.LabelSet{"severity": "critical", "alertname": "alert1"},
4548- StartsAt: start,
4549- },
4550- UpdatedAt: updated,
4551- }
4552- )
4553- openAPIAlert := alertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers)
4554- require.Equal(t, &open_api_models.GettableAlert{
4555- Annotations: open_api_models.LabelSet{},
4556- Alert: open_api_models.Alert{
4557- Labels: open_api_models.LabelSet{"severity": "critical", "alertname": "alert1"},
4558- },
4559- Status: &open_api_models.AlertStatus{
4560- State: &active,
4561- InhibitedBy: []string{},
4562- SilencedBy: []string{},
4563- },
4564- StartsAt: convertDateTime(start),
4565- EndsAt: convertDateTime(time.Time{}),
4566- UpdatedAt: convertDateTime(updated),
4567- Fingerprint: &fp,
4568- Receivers: []*open_api_models.Receiver{
4569- &open_api_models.Receiver{Name: &receivers[0]},
4570- &open_api_models.Receiver{Name: &receivers[1]},
4571- },
4572- }, openAPIAlert)
4573-}
4574diff --git a/api/v2/client/alert/alert_client.go b/api/v2/client/alert/alert_client.go
4575deleted file mode 100644
4576index 7d299c4..0000000
4577--- a/api/v2/client/alert/alert_client.go
4578+++ /dev/null
4579@@ -1,114 +0,0 @@
4580-// Code generated by go-swagger; DO NOT EDIT.
4581-
4582-// Copyright Prometheus Team
4583-// Licensed under the Apache License, Version 2.0 (the "License");
4584-// you may not use this file except in compliance with the License.
4585-// You may obtain a copy of the License at
4586-//
4587-// http://www.apache.org/licenses/LICENSE-2.0
4588-//
4589-// Unless required by applicable law or agreed to in writing, software
4590-// distributed under the License is distributed on an "AS IS" BASIS,
4591-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4592-// See the License for the specific language governing permissions and
4593-// limitations under the License.
4594-//
4595-
4596-package alert
4597-
4598-// This file was generated by the swagger tool.
4599-// Editing this file might prove futile when you re-run the swagger generate command
4600-
4601-import (
4602- "fmt"
4603-
4604- "github.com/go-openapi/runtime"
4605-
4606- strfmt "github.com/go-openapi/strfmt"
4607-)
4608-
4609-// New creates a new alert API client.
4610-func New(transport runtime.ClientTransport, formats strfmt.Registry) *Client {
4611- return &Client{transport: transport, formats: formats}
4612-}
4613-
4614-/*
4615-Client for alert API
4616-*/
4617-type Client struct {
4618- transport runtime.ClientTransport
4619- formats strfmt.Registry
4620-}
4621-
4622-/*
4623-GetAlerts Get a list of alerts
4624-*/
4625-func (a *Client) GetAlerts(params *GetAlertsParams) (*GetAlertsOK, error) {
4626- // TODO: Validate the params before sending
4627- if params == nil {
4628- params = NewGetAlertsParams()
4629- }
4630-
4631- result, err := a.transport.Submit(&runtime.ClientOperation{
4632- ID: "getAlerts",
4633- Method: "GET",
4634- PathPattern: "/alerts",
4635- ProducesMediaTypes: []string{"application/json"},
4636- ConsumesMediaTypes: []string{"application/json"},
4637- Schemes: []string{"http"},
4638- Params: params,
4639- Reader: &GetAlertsReader{formats: a.formats},
4640- Context: params.Context,
4641- Client: params.HTTPClient,
4642- })
4643- if err != nil {
4644- return nil, err
4645- }
4646- success, ok := result.(*GetAlertsOK)
4647- if ok {
4648- return success, nil
4649- }
4650- // unexpected success response
4651- // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
4652- msg := fmt.Sprintf("unexpected success response for getAlerts: API contract not enforced by server. Client expected to get an error, but got: %T", result)
4653- panic(msg)
4654-}
4655-
4656-/*
4657-PostAlerts Create new Alerts
4658-*/
4659-func (a *Client) PostAlerts(params *PostAlertsParams) (*PostAlertsOK, error) {
4660- // TODO: Validate the params before sending
4661- if params == nil {
4662- params = NewPostAlertsParams()
4663- }
4664-
4665- result, err := a.transport.Submit(&runtime.ClientOperation{
4666- ID: "postAlerts",
4667- Method: "POST",
4668- PathPattern: "/alerts",
4669- ProducesMediaTypes: []string{"application/json"},
4670- ConsumesMediaTypes: []string{"application/json"},
4671- Schemes: []string{"http"},
4672- Params: params,
4673- Reader: &PostAlertsReader{formats: a.formats},
4674- Context: params.Context,
4675- Client: params.HTTPClient,
4676- })
4677- if err != nil {
4678- return nil, err
4679- }
4680- success, ok := result.(*PostAlertsOK)
4681- if ok {
4682- return success, nil
4683- }
4684- // unexpected success response
4685- // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
4686- msg := fmt.Sprintf("unexpected success response for postAlerts: API contract not enforced by server. Client expected to get an error, but got: %T", result)
4687- panic(msg)
4688-}
4689-
4690-// SetTransport changes the transport on the client
4691-func (a *Client) SetTransport(transport runtime.ClientTransport) {
4692- a.transport = transport
4693-}
4694diff --git a/api/v2/client/alert/get_alerts_parameters.go b/api/v2/client/alert/get_alerts_parameters.go
4695deleted file mode 100644
4696index cba0387..0000000
4697--- a/api/v2/client/alert/get_alerts_parameters.go
4698+++ /dev/null
4699@@ -1,350 +0,0 @@
4700-// Code generated by go-swagger; DO NOT EDIT.
4701-
4702-// Copyright Prometheus Team
4703-// Licensed under the Apache License, Version 2.0 (the "License");
4704-// you may not use this file except in compliance with the License.
4705-// You may obtain a copy of the License at
4706-//
4707-// http://www.apache.org/licenses/LICENSE-2.0
4708-//
4709-// Unless required by applicable law or agreed to in writing, software
4710-// distributed under the License is distributed on an "AS IS" BASIS,
4711-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4712-// See the License for the specific language governing permissions and
4713-// limitations under the License.
4714-//
4715-
4716-package alert
4717-
4718-// This file was generated by the swagger tool.
4719-// Editing this file might prove futile when you re-run the swagger generate command
4720-
4721-import (
4722- "context"
4723- "net/http"
4724- "time"
4725-
4726- "github.com/go-openapi/errors"
4727- "github.com/go-openapi/runtime"
4728- cr "github.com/go-openapi/runtime/client"
4729- "github.com/go-openapi/swag"
4730-
4731- strfmt "github.com/go-openapi/strfmt"
4732-)
4733-
4734-// NewGetAlertsParams creates a new GetAlertsParams object
4735-// with the default values initialized.
4736-func NewGetAlertsParams() *GetAlertsParams {
4737- var (
4738- activeDefault = bool(true)
4739- inhibitedDefault = bool(true)
4740- silencedDefault = bool(true)
4741- unprocessedDefault = bool(true)
4742- )
4743- return &GetAlertsParams{
4744- Active: &activeDefault,
4745- Inhibited: &inhibitedDefault,
4746- Silenced: &silencedDefault,
4747- Unprocessed: &unprocessedDefault,
4748-
4749- timeout: cr.DefaultTimeout,
4750- }
4751-}
4752-
4753-// NewGetAlertsParamsWithTimeout creates a new GetAlertsParams object
4754-// with the default values initialized, and the ability to set a timeout on a request
4755-func NewGetAlertsParamsWithTimeout(timeout time.Duration) *GetAlertsParams {
4756- var (
4757- activeDefault = bool(true)
4758- inhibitedDefault = bool(true)
4759- silencedDefault = bool(true)
4760- unprocessedDefault = bool(true)
4761- )
4762- return &GetAlertsParams{
4763- Active: &activeDefault,
4764- Inhibited: &inhibitedDefault,
4765- Silenced: &silencedDefault,
4766- Unprocessed: &unprocessedDefault,
4767-
4768- timeout: timeout,
4769- }
4770-}
4771-
4772-// NewGetAlertsParamsWithContext creates a new GetAlertsParams object
4773-// with the default values initialized, and the ability to set a context for a request
4774-func NewGetAlertsParamsWithContext(ctx context.Context) *GetAlertsParams {
4775- var (
4776- activeDefault = bool(true)
4777- inhibitedDefault = bool(true)
4778- silencedDefault = bool(true)
4779- unprocessedDefault = bool(true)
4780- )
4781- return &GetAlertsParams{
4782- Active: &activeDefault,
4783- Inhibited: &inhibitedDefault,
4784- Silenced: &silencedDefault,
4785- Unprocessed: &unprocessedDefault,
4786-
4787- Context: ctx,
4788- }
4789-}
4790-
4791-// NewGetAlertsParamsWithHTTPClient creates a new GetAlertsParams object
4792-// with the default values initialized, and the ability to set a custom HTTPClient for a request
4793-func NewGetAlertsParamsWithHTTPClient(client *http.Client) *GetAlertsParams {
4794- var (
4795- activeDefault = bool(true)
4796- inhibitedDefault = bool(true)
4797- silencedDefault = bool(true)
4798- unprocessedDefault = bool(true)
4799- )
4800- return &GetAlertsParams{
4801- Active: &activeDefault,
4802- Inhibited: &inhibitedDefault,
4803- Silenced: &silencedDefault,
4804- Unprocessed: &unprocessedDefault,
4805- HTTPClient: client,
4806- }
4807-}
4808-
4809-/*GetAlertsParams contains all the parameters to send to the API endpoint
4810-for the get alerts operation typically these are written to a http.Request
4811-*/
4812-type GetAlertsParams struct {
4813-
4814- /*Active
4815- Show active alerts
4816-
4817- */
4818- Active *bool
4819- /*Filter
4820- A list of matchers to filter alerts by
4821-
4822- */
4823- Filter []string
4824- /*Inhibited
4825- Show inhibited alerts
4826-
4827- */
4828- Inhibited *bool
4829- /*Receiver
4830- A regex matching receivers to filter alerts by
4831-
4832- */
4833- Receiver *string
4834- /*Silenced
4835- Show silenced alerts
4836-
4837- */
4838- Silenced *bool
4839- /*Unprocessed
4840- Show unprocessed alerts
4841-
4842- */
4843- Unprocessed *bool
4844-
4845- timeout time.Duration
4846- Context context.Context
4847- HTTPClient *http.Client
4848-}
4849-
4850-// WithTimeout adds the timeout to the get alerts params
4851-func (o *GetAlertsParams) WithTimeout(timeout time.Duration) *GetAlertsParams {
4852- o.SetTimeout(timeout)
4853- return o
4854-}
4855-
4856-// SetTimeout adds the timeout to the get alerts params
4857-func (o *GetAlertsParams) SetTimeout(timeout time.Duration) {
4858- o.timeout = timeout
4859-}
4860-
4861-// WithContext adds the context to the get alerts params
4862-func (o *GetAlertsParams) WithContext(ctx context.Context) *GetAlertsParams {
4863- o.SetContext(ctx)
4864- return o
4865-}
4866-
4867-// SetContext adds the context to the get alerts params
4868-func (o *GetAlertsParams) SetContext(ctx context.Context) {
4869- o.Context = ctx
4870-}
4871-
4872-// WithHTTPClient adds the HTTPClient to the get alerts params
4873-func (o *GetAlertsParams) WithHTTPClient(client *http.Client) *GetAlertsParams {
4874- o.SetHTTPClient(client)
4875- return o
4876-}
4877-
4878-// SetHTTPClient adds the HTTPClient to the get alerts params
4879-func (o *GetAlertsParams) SetHTTPClient(client *http.Client) {
4880- o.HTTPClient = client
4881-}
4882-
4883-// WithActive adds the active to the get alerts params
4884-func (o *GetAlertsParams) WithActive(active *bool) *GetAlertsParams {
4885- o.SetActive(active)
4886- return o
4887-}
4888-
4889-// SetActive adds the active to the get alerts params
4890-func (o *GetAlertsParams) SetActive(active *bool) {
4891- o.Active = active
4892-}
4893-
4894-// WithFilter adds the filter to the get alerts params
4895-func (o *GetAlertsParams) WithFilter(filter []string) *GetAlertsParams {
4896- o.SetFilter(filter)
4897- return o
4898-}
4899-
4900-// SetFilter adds the filter to the get alerts params
4901-func (o *GetAlertsParams) SetFilter(filter []string) {
4902- o.Filter = filter
4903-}
4904-
4905-// WithInhibited adds the inhibited to the get alerts params
4906-func (o *GetAlertsParams) WithInhibited(inhibited *bool) *GetAlertsParams {
4907- o.SetInhibited(inhibited)
4908- return o
4909-}
4910-
4911-// SetInhibited adds the inhibited to the get alerts params
4912-func (o *GetAlertsParams) SetInhibited(inhibited *bool) {
4913- o.Inhibited = inhibited
4914-}
4915-
4916-// WithReceiver adds the receiver to the get alerts params
4917-func (o *GetAlertsParams) WithReceiver(receiver *string) *GetAlertsParams {
4918- o.SetReceiver(receiver)
4919- return o
4920-}
4921-
4922-// SetReceiver adds the receiver to the get alerts params
4923-func (o *GetAlertsParams) SetReceiver(receiver *string) {
4924- o.Receiver = receiver
4925-}
4926-
4927-// WithSilenced adds the silenced to the get alerts params
4928-func (o *GetAlertsParams) WithSilenced(silenced *bool) *GetAlertsParams {
4929- o.SetSilenced(silenced)
4930- return o
4931-}
4932-
4933-// SetSilenced adds the silenced to the get alerts params
4934-func (o *GetAlertsParams) SetSilenced(silenced *bool) {
4935- o.Silenced = silenced
4936-}
4937-
4938-// WithUnprocessed adds the unprocessed to the get alerts params
4939-func (o *GetAlertsParams) WithUnprocessed(unprocessed *bool) *GetAlertsParams {
4940- o.SetUnprocessed(unprocessed)
4941- return o
4942-}
4943-
4944-// SetUnprocessed adds the unprocessed to the get alerts params
4945-func (o *GetAlertsParams) SetUnprocessed(unprocessed *bool) {
4946- o.Unprocessed = unprocessed
4947-}
4948-
4949-// WriteToRequest writes these params to a swagger request
4950-func (o *GetAlertsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
4951-
4952- if err := r.SetTimeout(o.timeout); err != nil {
4953- return err
4954- }
4955- var res []error
4956-
4957- if o.Active != nil {
4958-
4959- // query param active
4960- var qrActive bool
4961- if o.Active != nil {
4962- qrActive = *o.Active
4963- }
4964- qActive := swag.FormatBool(qrActive)
4965- if qActive != "" {
4966- if err := r.SetQueryParam("active", qActive); err != nil {
4967- return err
4968- }
4969- }
4970-
4971- }
4972-
4973- valuesFilter := o.Filter
4974-
4975- joinedFilter := swag.JoinByFormat(valuesFilter, "multi")
4976- // query array param filter
4977- if err := r.SetQueryParam("filter", joinedFilter...); err != nil {
4978- return err
4979- }
4980-
4981- if o.Inhibited != nil {
4982-
4983- // query param inhibited
4984- var qrInhibited bool
4985- if o.Inhibited != nil {
4986- qrInhibited = *o.Inhibited
4987- }
4988- qInhibited := swag.FormatBool(qrInhibited)
4989- if qInhibited != "" {
4990- if err := r.SetQueryParam("inhibited", qInhibited); err != nil {
4991- return err
4992- }
4993- }
4994-
4995- }
4996-
4997- if o.Receiver != nil {
4998-
4999- // query param receiver
5000- var qrReceiver string
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: