From 5ba9b9c7c34cff1276004e893ed0a4b0998f5256 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Tue, 12 Sep 2023 17:00:18 +0100 Subject: [PATCH 01/20] Add test for PromQL braces when parsing lists of matchers (#3507) * Add test for PromQL braces when parsing lists of matchers Signed-off-by: George Robinson * Use acceptance tests Signed-off-by: George Robinson * Add test creating silence with braces Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- test/cli/acceptance.go | 9 +++--- test/cli/acceptance/cli_test.go | 56 +++++++++++++++++++++++---------- test/cli/mock.go | 17 +++++++++- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/test/cli/acceptance.go b/test/cli/acceptance.go index 0229b6594f..f3c20984b1 100644 --- a/test/cli/acceptance.go +++ b/test/cli/acceptance.go @@ -490,9 +490,10 @@ func (am *Alertmanager) addAlertCommand(alert *TestAlert) ([]byte, error) { } // QueryAlerts uses the amtool cli to query alerts. -func (am *Alertmanager) QueryAlerts() ([]TestAlert, error) { +func (am *Alertmanager) QueryAlerts(match ...string) ([]TestAlert, error) { amURLFlag := "--alertmanager.url=" + am.getURL("/") - cmd := exec.Command(amtool, amURLFlag, "alert", "query") + args := append([]string{amURLFlag, "alert", "query"}, match...) + cmd := exec.Command(amtool, args...) output, err := cmd.CombinedOutput() if err != nil { return nil, err @@ -558,9 +559,9 @@ func (am *Alertmanager) addSilenceCommand(sil *TestSilence) ([]byte, error) { } // QuerySilence queries the current silences using the 'amtool silence query' command. -func (am *Alertmanager) QuerySilence() ([]TestSilence, error) { +func (am *Alertmanager) QuerySilence(match ...string) ([]TestSilence, error) { amURLFlag := "--alertmanager.url=" + am.getURL("/") - args := []string{amURLFlag, "silence", "query"} + args := append([]string{amURLFlag, "silence", "query"}, match...) cmd := exec.Command(amtool, args...) out, err := cmd.CombinedOutput() if err != nil { diff --git a/test/cli/acceptance/cli_test.go b/test/cli/acceptance/cli_test.go index 4d5f9bc1fd..c047cdb444 100644 --- a/test/cli/acceptance/cli_test.go +++ b/test/cli/acceptance/cli_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/prometheus/alertmanager/api/v2/models" . "github.com/prometheus/alertmanager/test/cli" ) @@ -114,13 +115,26 @@ receivers: am.AddAlerts(alert1, alert2) alerts, err := am.QueryAlerts() - if err != nil { - t.Fatal("Failed to query alerts", err) - } - expectedAlerts := 2 - if len(alerts) != expectedAlerts { - t.Fatalf("Incorrect number of alerts, expected %v, got %v", expectedAlerts, len(alerts)) - } + require.NoError(t, err) + require.Len(t, alerts, 2) + + // Get the first alert using the alertname heuristic + alerts, err = am.QueryAlerts("test1") + require.NoError(t, err) + require.Len(t, alerts, 1) + + // QueryAlerts uses the simple output option, which means just the alertname + // label is printed. We can assert that querying works as expected as we know + // there are two alerts called "test1" and "test2". + expectedLabels := models.LabelSet{"name": "test1"} + require.True(t, alerts[0].HasLabels(expectedLabels)) + + // Get the second alert + alerts, err = am.QueryAlerts("alertname=test2") + require.NoError(t, err) + require.Len(t, alerts, 1) + expectedLabels = models.LabelSet{"name": "test2"} + require.True(t, alerts[0].HasLabels(expectedLabels)) } func TestQuerySilence(t *testing.T) { @@ -153,20 +167,30 @@ receivers: am := amc.Members()[0] - silence1 := Silence(0, 4).Match("alertname=test1", "severity=warn").Comment("test1") - silence2 := Silence(0, 4).Match("foo").Comment("test foo") + silence1 := Silence(0, 4).Match("test1", "severity=warn").Comment("test1") + silence2 := Silence(0, 4).Match("alertname=test2", "severity=warn").Comment("test2") + silence3 := Silence(0, 4).Match("{alertname=test3}", "severity=warn").Comment("test3") am.SetSilence(0, silence1) am.SetSilence(0, silence2) + am.SetSilence(0, silence3) + // Get all silences sils, err := am.QuerySilence() - if err != nil { - t.Error("Failed to query silences: ", err) - } - expectedSils := 2 - if len(sils) != expectedSils { - t.Errorf("Incorrect number of silences queried, expected: %v, actual: %v", expectedSils, len(sils)) - } + require.NoError(t, err) + require.Len(t, sils, 3) + expected1 := []string{"alertname=\"test1\"", "severity=\"warn\""} + require.Equal(t, expected1, sils[0].GetMatches()) + expected2 := []string{"alertname=\"test2\"", "severity=\"warn\""} + require.Equal(t, expected2, sils[1].GetMatches()) + expected3 := []string{"alertname=\"{alertname=test3}\"", "severity=\"warn\""} + require.Equal(t, expected3, sils[2].GetMatches()) + + // Get the first silence using the alertname heuristic + sils, err = am.QuerySilence("test1") + require.NoError(t, err) + require.Len(t, sils, 1) + require.Equal(t, expected1, sils[0].GetMatches()) } func TestRoutesShow(t *testing.T) { diff --git a/test/cli/mock.go b/test/cli/mock.go index a6570160d0..0116110ac1 100644 --- a/test/cli/mock.go +++ b/test/cli/mock.go @@ -76,7 +76,12 @@ func (s *TestSilence) Match(v ...string) *TestSilence { return s } -// MatchRE adds a new regex matcher to the silence +// GetMatches returns the plain matchers for the silence. +func (s TestSilence) GetMatches() []string { + return s.match +} + +// MatchRE adds a new regex matcher to the silence. func (s *TestSilence) MatchRE(v ...string) *TestSilence { if len(v)%2 == 1 { panic("bad key/values") @@ -85,6 +90,11 @@ func (s *TestSilence) MatchRE(v ...string) *TestSilence { return s } +// GetMatchREs returns the regex matchers for the silence. +func (s *TestSilence) GetMatchREs() []string { + return s.matchRE +} + // Comment sets the comment to the silence. func (s *TestSilence) Comment(c string) *TestSilence { s.comment = c @@ -185,6 +195,11 @@ func (a *TestAlert) Active(tss ...float64) *TestAlert { return a } +// HasLabels returns true if the two label sets are equivalent, otherwise false. +func (a *TestAlert) HasLabels(labels models.LabelSet) bool { + return reflect.DeepEqual(a.labels, labels) +} + func equalAlerts(a, b *models.GettableAlert, opts *AcceptanceOpts) bool { if !reflect.DeepEqual(a.Labels, b.Labels) { return false From 5a462df83ab1d22ed0e5b806756be83dce64bb13 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 21 Sep 2023 13:57:11 +0100 Subject: [PATCH 02/20] Support UTF-8 label matchers: Add more acceptance tests for braces when using amtool (#3523) * Add tests for PromQL braces when using amtool alert Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- test/cli/acceptance.go | 43 ++++++++++++++++++++++++--------- test/cli/acceptance/cli_test.go | 25 ++++++++++++++----- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/test/cli/acceptance.go b/test/cli/acceptance.go index f3c20984b1..74fd144769 100644 --- a/test/cli/acceptance.go +++ b/test/cli/acceptance.go @@ -457,27 +457,46 @@ func Version() (string, error) { // AddAlertsAt declares alerts that are to be added to the Alertmanager // server at a relative point in time. -func (am *Alertmanager) AddAlertsAt(at float64, alerts ...*TestAlert) { +func (am *Alertmanager) AddAlertsAt(omitEquals bool, at float64, alerts ...*TestAlert) { am.t.Do(at, func() { - am.AddAlerts(alerts...) + am.AddAlerts(omitEquals, alerts...) }) } // AddAlerts declares alerts that are to be added to the Alertmanager server. -func (am *Alertmanager) AddAlerts(alerts ...*TestAlert) { +// The omitEquals option omits alertname= from the command line args passed to +// amtool and instead uses the alertname value as the first argument to the command. +// For example `amtool alert add foo` instead of `amtool alert add alertname=foo`. +// This has been added to allow certain tests to test adding alerts both with and +// without alertname=. All other tests that use AddAlerts as a fixture can set this +// to false. +func (am *Alertmanager) AddAlerts(omitEquals bool, alerts ...*TestAlert) { for _, alert := range alerts { - out, err := am.addAlertCommand(alert) + out, err := am.addAlertCommand(omitEquals, alert) if err != nil { am.t.Errorf("Error adding alert: %v\nOutput: %s", err, string(out)) } } } -func (am *Alertmanager) addAlertCommand(alert *TestAlert) ([]byte, error) { +func (am *Alertmanager) addAlertCommand(omitEquals bool, alert *TestAlert) ([]byte, error) { amURLFlag := "--alertmanager.url=" + am.getURL("/") args := []string{amURLFlag, "alert", "add"} - for key, val := range alert.labels { - args = append(args, key+"="+val) + // Make a copy of the labels + labels := make(models.LabelSet, len(alert.labels)) + for k, v := range alert.labels { + labels[k] = v + } + if omitEquals { + // If alertname is present and omitEquals is true then the command should + // be `amtool alert add foo ...` and not `amtool alert add alertname=foo ...`. + if alertname, ok := labels["alertname"]; ok { + args = append(args, alertname) + delete(labels, "alertname") + } + } + for k, v := range labels { + args = append(args, k+"="+v) } startsAt := strfmt.DateTime(am.opts.expandTime(alert.startsAt)) args = append(args, "--start="+startsAt.String()) @@ -522,7 +541,7 @@ func parseAlertQueryResponse(data []byte) ([]TestAlert, error) { } summary := strings.TrimSpace(line[summPos:]) alert := TestAlert{ - labels: models.LabelSet{"name": alertName}, + labels: models.LabelSet{"alertname": alertName}, startsAt: float64(startsAt.Unix()), summary: summary, } @@ -670,13 +689,13 @@ func (am *Alertmanager) showRouteCommand() ([]byte, error) { return cmd.CombinedOutput() } -func (am *Alertmanager) TestRoute() ([]byte, error) { - return am.testRouteCommand() +func (am *Alertmanager) TestRoute(labels ...string) ([]byte, error) { + return am.testRouteCommand(labels...) } -func (am *Alertmanager) testRouteCommand() ([]byte, error) { +func (am *Alertmanager) testRouteCommand(labels ...string) ([]byte, error) { amURLFlag := "--alertmanager.url=" + am.getURL("/") - args := []string{amURLFlag, "config", "routes", "test"} + args := append([]string{amURLFlag, "config", "routes", "test"}, labels...) cmd := exec.Command(amtool, args...) return cmd.CombinedOutput() } diff --git a/test/cli/acceptance/cli_test.go b/test/cli/acceptance/cli_test.go index c047cdb444..bc6af58599 100644 --- a/test/cli/acceptance/cli_test.go +++ b/test/cli/acceptance/cli_test.go @@ -72,7 +72,7 @@ receivers: am := amc.Members()[0] alert1 := Alert("alertname", "test1").Active(1, 2) - am.AddAlertsAt(0, alert1) + am.AddAlertsAt(false, 0, alert1) co.Want(Between(1, 2), Alert("alertname", "test1").Active(1)) at.Run() @@ -111,12 +111,13 @@ receivers: am := amc.Members()[0] alert1 := Alert("alertname", "test1", "severity", "warning").Active(1) - alert2 := Alert("alertname", "test2", "severity", "info").Active(1) - am.AddAlerts(alert1, alert2) + alert2 := Alert("alertname", "alertname=test2", "severity", "info").Active(1) + alert3 := Alert("alertname", "{alertname=test3}", "severity", "info").Active(1) + am.AddAlerts(true, alert1, alert2, alert3) alerts, err := am.QueryAlerts() require.NoError(t, err) - require.Len(t, alerts, 2) + require.Len(t, alerts, 3) // Get the first alert using the alertname heuristic alerts, err = am.QueryAlerts("test1") @@ -126,14 +127,21 @@ receivers: // QueryAlerts uses the simple output option, which means just the alertname // label is printed. We can assert that querying works as expected as we know // there are two alerts called "test1" and "test2". - expectedLabels := models.LabelSet{"name": "test1"} + expectedLabels := models.LabelSet{"alertname": "test1"} require.True(t, alerts[0].HasLabels(expectedLabels)) // Get the second alert alerts, err = am.QueryAlerts("alertname=test2") require.NoError(t, err) require.Len(t, alerts, 1) - expectedLabels = models.LabelSet{"name": "test2"} + expectedLabels = models.LabelSet{"alertname": "test2"} + require.True(t, alerts[0].HasLabels(expectedLabels)) + + // Get the third alert + alerts, err = am.QueryAlerts("{alertname=test3}") + require.NoError(t, err) + require.Len(t, alerts, 1) + expectedLabels = models.LabelSet{"alertname": "{alertname=test3}"} require.True(t, alerts[0].HasLabels(expectedLabels)) } @@ -257,4 +265,9 @@ receivers: am := amc.Members()[0] _, err := am.TestRoute() require.NoError(t, err) + + // Bad labels should return error + out, err := am.TestRoute("{foo=bar}") + require.EqualError(t, err, "exit status 1") + require.Equal(t, "amtool: error: Failed to parse labels: bad matcher format: {foo=bar}\n\n", string(out)) } From b517645d951da230fbb078da417c5ac46928dbd6 Mon Sep 17 00:00:00 2001 From: Jan Fajerski Date: Thu, 21 Sep 2023 18:22:17 +0200 Subject: [PATCH 03/20] Github actions (#3299) * Move CI to github actions Signed-off-by: Jan Fajerski * Skip email test in github action Signed-off-by: Jan Fajerski * build before lint Signed-off-by: Jan Fajerski --------- Signed-off-by: Jan Fajerski --- .github/workflows/ci.yml | 30 +++++++++++++++++++++++++++++ .github/workflows/golangci-lint.yml | 27 ++++++++++++++++++++++++++ .github/workflows/mixin.yml | 22 +++++++++++++++++++++ .github/workflows/publish.yml | 24 +++++++++++++++++++++++ .github/workflows/release.yml | 25 ++++++++++++++++++++++++ .gitignore | 1 + Makefile | 8 ++++++++ 7 files changed, 137 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .github/workflows/mixin.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..f2f54493c3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +--- +name: CI +on: # yamllint disable-line rule:truthy + pull_request: + workflow_call: +jobs: + test_frontend: + name: Test alertmanager frontend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: make clean + - run: make all + working-directory: ./ui/app + - run: make assets + - run: make apiv2 + - run: git diff --exit-code + + test: + name: Test + runs-on: ubuntu-latest + # Whenever the Go version is updated here, .promu.yml + # should also be updated. + container: + image: quay.io/prometheus/golang-builder:1.19-base + steps: + - uses: actions/checkout@v3 + - uses: prometheus/promci@v0.0.2 + - uses: ./.github/promci/actions/setup_environment + - run: make diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000000..e85360a0fe --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,27 @@ +name: golangci-lint +on: + pull_request: + paths: + - "go.sum" + - "go.mod" + - "**.go" + - "scripts/errcheck_excludes.txt" + - ".github/workflows/golangci-lint.yml" + - ".golangci.yml" + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: install Go + uses: actions/setup-go@v2 + with: + go-version: 1.19.x + - run: make build + - name: Lint + uses: golangci/golangci-lint-action@v3.4.0 + with: + version: v1.51.2 diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml new file mode 100644 index 0000000000..e3c0c9804d --- /dev/null +++ b/.github/workflows/mixin.yml @@ -0,0 +1,22 @@ +name: mixin +on: + pull_request: + paths: + - "doc/alertmanager-mixin/**" + +jobs: + mixin: + name: mixin-lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: install Go + uses: actions/setup-go@v2 + with: + go-version: 1.19.x + # pin the mixtool version until https://github.com/monitoring-mixins/mixtool/issues/135 is merged. + - run: go install github.com/monitoring-mixins/mixtool/cmd/mixtool@2282201396b69055bb0f92f187049027a16d2130 + - run: go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest + - run: go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest + - run: make -C doc/alertmanager-mixin lint diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000000..3f81857954 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,24 @@ +--- +name: Publish +on: # yamllint disable-line rule:truthy + push: + branches: + - main +jobs: + ci: + name: Run ci + uses: ./.github/workflows/ci.yml + + publish_main: + name: Publish main branch artefacts + runs-on: ubuntu-latest + needs: ci + steps: + - uses: actions/checkout@v3 + - uses: prometheus/promci@v0.0.2 + - uses: ./.github/promci/actions/publish_main + with: + docker_hub_login: ${{ secrets.docker_hub_login }} + docker_hub_password: ${{ secrets.docker_hub_password }} + quay_io_login: ${{ secrets.quay_io_login }} + quay_io_password: ${{ secrets.quay_io_password }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..9538aa8330 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +--- +name: Release +on: # yamllint disable-line rule:truthy + push: + tags: + - v* +jobs: + ci: + name: Run ci + uses: ./.github/workflows/ci.yml + + publish_release: + name: Publish release arfefacts + runs-on: ubuntu-latest + needs: ci + steps: + - uses: actions/checkout@v3 + - uses: prometheus/promci@v0.0.2 + - uses: ./.github/promci/actions/publish_release + with: + docker_hub_login: ${{ secrets.docker_hub_login }} + docker_hub_password: ${{ secrets.docker_hub_password }} + quay_io_login: ${{ secrets.quay_io_login }} + quay_io_password: ${{ secrets.quay_io_password }} + github_token: ${{ secrets.PROMBOT_GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index aa4b780003..7ef77d321a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ !/.travis.yml !/.promu.yml !/api/v2/openapi.yaml +!.github/workflows/*.yml diff --git a/Makefile b/Makefile index c6bfcad067..e9da94c762 100644 --- a/Makefile +++ b/Makefile @@ -78,3 +78,11 @@ clean: template/email.tmpl \ api/v2/models api/v2/restapi api/v2/client - @cd $(FRONTEND_DIR) && $(MAKE) clean + +# In github actions we skip the email test for now. Service containers in github +# actions currently have a bug, see https://github.com/prometheus/alertmanager/pull/3299 +# So define a test target, that skips the email test for now. +.PHONY: test +test: $(GOTEST_DIR) + @echo ">> running all tests, except notify/email" + $(GOTEST) $(test-flags) $(GOOPTS) `go list ./... | grep -v notify/email` From c6be0bcabf37e837d24786e3d5c10565ac499531 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Mon, 25 Sep 2023 11:11:56 +0100 Subject: [PATCH 04/20] Remove unused function GetAlertmanagerURL (#3535) Signed-off-by: George Robinson --- cli/utils.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cli/utils.go b/cli/utils.go index a1a3db7180..60221e239a 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -19,7 +19,6 @@ import ( "fmt" "net/url" "os" - "path" kingpin "github.com/alecthomas/kingpin/v2" "github.com/prometheus/common/model" @@ -30,13 +29,6 @@ import ( "github.com/prometheus/alertmanager/pkg/labels" ) -// GetAlertmanagerURL appends the given path to the alertmanager base URL -func GetAlertmanagerURL(p string) url.URL { - amURL := *alertmanagerURL - amURL.Path = path.Join(alertmanagerURL.Path, p) - return amURL -} - // parseMatchers parses a list of matchers (cli arguments). func parseMatchers(inputMatchers []string) ([]labels.Matcher, error) { matchers := make([]labels.Matcher, 0, len(inputMatchers)) From 57fdd136ab39ee8037a4d9fab5f32c83d6910522 Mon Sep 17 00:00:00 2001 From: Yijie Qin Date: Fri, 29 Sep 2023 10:07:52 -0400 Subject: [PATCH 05/20] Add the route ID to uuid (#3372) * Add the route ID to uuid Signed-off-by: Yijie Qin --------- Signed-off-by: Yijie Qin --- dispatch/route.go | 23 ++++++++++++++ dispatch/route_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/dispatch/route.go b/dispatch/route.go index 4b3673c531..5ada178dab 100644 --- a/dispatch/route.go +++ b/dispatch/route.go @@ -180,6 +180,29 @@ func (r *Route) Key() string { return b.String() } +// ID returns a unique identifier for the route. +func (r *Route) ID() string { + b := strings.Builder{} + + position := -1 + if r.parent != nil { + // Find the position in the same level leaf. + for i, cr := range r.parent.Routes { + if cr == r { + position = i + break + } + } + } + b.WriteString(r.Key()) + + if position > -1 { + b.WriteRune('/') + b.WriteString(fmt.Sprint(position)) + } + return b.String() +} + // Walk traverses the route tree in depth-first order. func (r *Route) Walk(visit func(*Route)) { visit(r) diff --git a/dispatch/route_test.go b/dispatch/route_test.go index e633ae5f6f..f89b1a640b 100644 --- a/dispatch/route_test.go +++ b/dispatch/route_test.go @@ -853,3 +853,71 @@ routes: } } } + +func TestRouteID(t *testing.T) { + in := ` +receiver: 'notify-def' + +routes: +- matchers: ['{owner="team-A"}', '{level!="critical"}'] + receiver: 'notify-D' + group_by: [...] + continue: true +- matchers: ['{owner="team-A"}', '{level!="critical"}'] + receiver: 'notify-A' + routes: + - matchers: ['{env="testing"}', '{baz!~".*quux"}'] + receiver: 'notify-testing' + group_by: [...] + - match: + env: "production" + receiver: 'notify-productionA' + group_wait: 1m + continue: true + - matchers: [ env=~"produ.*", job=~".*"] + receiver: 'notify-productionB' + group_wait: 30s + group_interval: 5m + repeat_interval: 1h + group_by: ['job'] +- match_re: + owner: 'team-(B|C)' + group_by: ['foo', 'bar'] + group_wait: 2m + receiver: 'notify-BC' +- matchers: [group_by="role"] + group_by: ['role'] + routes: + - matchers: ['{env="testing"}'] + receiver: 'notify-testing' + routes: + - matchers: [wait="long"] + group_wait: 2m +` + + var ctree config.Route + if err := yaml.UnmarshalStrict([]byte(in), &ctree); err != nil { + t.Fatal(err) + } + tree := NewRoute(&ctree, nil) + + expected := []string{ + "{}", + "{}/{level!=\"critical\",owner=\"team-A\"}/0", + "{}/{level!=\"critical\",owner=\"team-A\"}/1", + "{}/{level!=\"critical\",owner=\"team-A\"}/{baz!~\".*quux\",env=\"testing\"}/0", + "{}/{level!=\"critical\",owner=\"team-A\"}/{env=\"production\"}/1", + "{}/{level!=\"critical\",owner=\"team-A\"}/{env=~\"produ.*\",job=~\".*\"}/2", + "{}/{owner=~\"^(?:team-(B|C))$\"}/2", + "{}/{group_by=\"role\"}/3", + "{}/{group_by=\"role\"}/{env=\"testing\"}/0", + "{}/{group_by=\"role\"}/{env=\"testing\"}/{wait=\"long\"}/0", + } + + var got []string + tree.Walk(func(r *Route) { + got = append(got, r.ID()) + }) + + require.ElementsMatch(t, got, expected) +} From 0daf0bf24c5c7d96887dd46b8c5fe87ffbfc4326 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 15:13:02 +0000 Subject: [PATCH 06/20] Bump golang.org/x/net from 0.14.0 to 0.15.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.15.0. - [Commits](https://github.com/golang/net/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 212be78ea0..bbd4ec9dcd 100644 --- a/go.mod +++ b/go.mod @@ -41,8 +41,8 @@ require ( github.com/xlab/treeprint v1.2.0 go.uber.org/atomic v1.11.0 golang.org/x/mod v0.12.0 - golang.org/x/net v0.14.0 - golang.org/x/text v0.12.0 + golang.org/x/net v0.15.0 + golang.org/x/text v0.13.0 golang.org/x/tools v0.12.0 gopkg.in/telebot.v3 v3.1.3 gopkg.in/yaml.v2 v2.4.0 @@ -84,10 +84,10 @@ require ( go.mongodb.org/mongo-driver v1.11.3 // indirect go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index f80d129c86..e7e90ae8bd 100644 --- a/go.sum +++ b/go.sum @@ -608,8 +608,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -699,8 +699,8 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -824,8 +824,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -839,8 +839,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 1da68ac7ccbfbe1a63aeadb17e4474de0fe05e01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 15:13:12 +0000 Subject: [PATCH 07/20] Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0 Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 212be78ea0..0d08f0e091 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/oklog/run v1.1.0 github.com/oklog/ulid v1.3.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_golang v1.17.0 github.com/prometheus/common v0.44.0 github.com/prometheus/common/assets v0.2.0 github.com/prometheus/common/sigv4 v0.1.0 @@ -77,8 +77,8 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/procfs v0.11.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect @@ -89,6 +89,6 @@ require ( golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.11.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f80d129c86..3ab31818d5 100644 --- a/go.sum +++ b/go.sum @@ -475,14 +475,14 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -501,8 +501,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1082,8 +1082,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 641480717aa3f391308333b71579b27a867717ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 15:13:17 +0000 Subject: [PATCH 08/20] Bump github.com/rs/cors from 1.9.0 to 1.10.1 Bumps [github.com/rs/cors](https://github.com/rs/cors) from 1.9.0 to 1.10.1. - [Release notes](https://github.com/rs/cors/releases) - [Commits](https://github.com/rs/cors/compare/v1.9.0...v1.10.1) --- updated-dependencies: - dependency-name: github.com/rs/cors dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 212be78ea0..9b7468526e 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/prometheus/common/assets v0.2.0 github.com/prometheus/common/sigv4 v0.1.0 github.com/prometheus/exporter-toolkit v0.10.0 - github.com/rs/cors v1.9.0 + github.com/rs/cors v1.10.1 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index f80d129c86..fbb75289d9 100644 --- a/go.sum +++ b/go.sum @@ -509,8 +509,8 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= -github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= From c1b5a371ba57a23d8ecbaae70406c54146f3dc5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 08:15:18 +0000 Subject: [PATCH 09/20] Bump golang.org/x/tools from 0.12.0 to 0.13.0 Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.12.0 to 0.13.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.12.0...v0.13.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eba4886698..9549cbb6c3 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( golang.org/x/mod v0.12.0 golang.org/x/net v0.15.0 golang.org/x/text v0.13.0 - golang.org/x/tools v0.12.0 + golang.org/x/tools v0.13.0 gopkg.in/telebot.v3 v3.1.3 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index feeba7ed96..e572c6e1b6 100644 --- a/go.sum +++ b/go.sum @@ -904,8 +904,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From d7b865d6cc7211b975652bc60dd5c820b3f907d5 Mon Sep 17 00:00:00 2001 From: Jan Fajerski Date: Thu, 12 Oct 2023 11:43:30 +0200 Subject: [PATCH 10/20] actions: cross build in ci and fix publish (#3533) * actions: cross build in ci and fix publish Signed-off-by: Jan Fajerski * actions: build before release publishing Signed-off-by: Jan Fajerski --------- Signed-off-by: Jan Fajerski --- .github/workflows/ci.yml | 15 +++++++++++++++ .github/workflows/publish.yml | 18 ++++++++++++++++-- .github/workflows/release.yml | 21 +++++++++++++++++---- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2f54493c3..42f82d9da9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,21 @@ jobs: - run: make apiv2 - run: git diff --exit-code + build: + name: Build Alertmanager for common architectures + runs-on: ubuntu-latest + strategy: + matrix: + thread: [ 0, 1, 2 ] + steps: + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0 + - uses: ./.github/promci/actions/build + with: + promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386" + parallelism: 3 + thread: ${{ matrix.thread }} + test: name: Test runs-on: ubuntu-latest diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3f81857954..23239f73a8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,13 +9,27 @@ jobs: name: Run ci uses: ./.github/workflows/ci.yml + build: + name: Build Alertmanager for all architectures + runs-on: ubuntu-latest + strategy: + matrix: + thread: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ] + needs: ci + steps: + - uses: actions/checkout@v3 + - uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0 + - uses: ./.github/promci/actions/build + with: + parallelism: 12 + thread: ${{ matrix.thread }} publish_main: name: Publish main branch artefacts runs-on: ubuntu-latest - needs: ci + needs: build steps: - uses: actions/checkout@v3 - - uses: prometheus/promci@v0.0.2 + - uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0 - uses: ./.github/promci/actions/publish_main with: docker_hub_login: ${{ secrets.docker_hub_login }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9538aa8330..3630b6dda2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,17 +9,30 @@ jobs: name: Run ci uses: ./.github/workflows/ci.yml - publish_release: - name: Publish release arfefacts + build: + name: Build Alertmanager for all architectures runs-on: ubuntu-latest + strategy: + matrix: + thread: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ] needs: ci steps: - uses: actions/checkout@v3 - - uses: prometheus/promci@v0.0.2 + - uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0 + - uses: ./.github/promci/actions/build + with: + parallelism: 12 + thread: ${{ matrix.thread }} + publish_release: + name: Publish release artefacts + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + - uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0 - uses: ./.github/promci/actions/publish_release with: docker_hub_login: ${{ secrets.docker_hub_login }} docker_hub_password: ${{ secrets.docker_hub_password }} quay_io_login: ${{ secrets.quay_io_login }} quay_io_password: ${{ secrets.quay_io_password }} - github_token: ${{ secrets.PROMBOT_GITHUB_TOKEN }} From 573e2c3694f24f5649aa1e7ec73787a6b920d4d6 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 12 Oct 2023 16:12:12 +0100 Subject: [PATCH 11/20] Update configuration docs for Repeat interval (#3552) Although it is true that Repeat interval should be greater than or equal to the Group interval, it should also be a multiple of it too. If the Repeat interval is not a multiple, then because of how aggregation groups are flushed, it will be made into one implicitly. This commit documents this behavior. Signed-off-by: George Robinson --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 13bc3bc4c8..f084805c1d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -200,7 +200,7 @@ matchers: # Note that this parameter is implicitly bound by Alertmanager's # `--data.retention` configuration flag. Notifications will be resent after either # repeat_interval or the data retention period have passed, whichever -# occurs first. `repeat_interval` should not be less than `group_interval`. +# occurs first. `repeat_interval` should be a multiple of `group_interval`. [ repeat_interval: | default = 4h ] # Times when the route should be muted. These must match the name of a From 318d2a3abf6da8d5a4b53cecafb9dca32fe49e29 Mon Sep 17 00:00:00 2001 From: Lukas Hauser Date: Thu, 12 Oct 2023 17:30:32 +0200 Subject: [PATCH 12/20] Fix Docs: default (#3550) Signed-off-by: Lukas Hauser --- docs/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index f084805c1d..8a8d796066 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -734,10 +734,10 @@ Microsoft Teams notifications are sent via the [Incoming Webhooks](https://learn [ webhook_url: ] # Message title template. -[ title: | default = '{{ template "teams.default.title" . }}' ] +[ title: | default = '{{ template "msteams.default.title" . }}' ] # Message body template. -[ text: | default = '{{ template "teams.default.text" . }}' ] +[ text: | default = '{{ template "msteams.default.text" . }}' ] # The HTTP client's configuration. [ http_config: | default = global.http_config ] From acb58400fd2b324dd4cc56110c8e891e8b1c0420 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Fri, 13 Oct 2023 14:15:05 +0100 Subject: [PATCH 13/20] Refactor: Move `inTimeIntervals` from `notify` to `timeinterval` (#3556) * Refactor: Move `inTimeIntervals` from `notify` to `timeinterval` There's absolutely no change of functionality here and I've expanded coverage for similar logic in both places. --------- Signed-off-by: gotjosh --- cmd/alertmanager/main.go | 4 +- notify/notify.go | 43 ++++++-------- notify/notify_test.go | 6 +- timeinterval/timeinterval.go | 32 ++++++++++- timeinterval/timeinterval_test.go | 95 +++++++++++++++++++++++++++++++ types/types.go | 5 ++ 6 files changed, 152 insertions(+), 33 deletions(-) diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go index 3dc48f9f4d..2d4a58e254 100644 --- a/cmd/alertmanager/main.go +++ b/cmd/alertmanager/main.go @@ -478,6 +478,8 @@ func run() int { timeIntervals[ti.Name] = ti.TimeIntervals } + intervener := timeinterval.NewIntervener(timeIntervals) + inhibitor.Stop() disp.Stop() @@ -497,7 +499,7 @@ func run() int { waitFunc, inhibitor, silencer, - timeIntervals, + intervener, notificationLog, pipelinePeer, ) diff --git a/notify/notify.go b/notify/notify.go index 11ddfba536..2c9f768a36 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -374,7 +374,7 @@ func (pb *PipelineBuilder) New( wait func() time.Duration, inhibitor *inhibit.Inhibitor, silencer *silence.Silencer, - times map[string][]timeinterval.TimeInterval, + intervener *timeinterval.Intervener, notificationLog NotificationLog, peer Peer, ) RoutingStage { @@ -382,8 +382,8 @@ func (pb *PipelineBuilder) New( ms := NewGossipSettleStage(peer) is := NewMuteStage(inhibitor) - tas := NewTimeActiveStage(times) - tms := NewTimeMuteStage(times) + tas := NewTimeActiveStage(intervener) + tms := NewTimeMuteStage(intervener) ss := NewMuteStage(silencer) for name := range receivers { @@ -868,13 +868,13 @@ func (n SetNotifiesStage) Exec(ctx context.Context, l log.Logger, alerts ...*typ } type timeStage struct { - Times map[string][]timeinterval.TimeInterval + muter types.TimeMuter } type TimeMuteStage timeStage -func NewTimeMuteStage(ti map[string][]timeinterval.TimeInterval) *TimeMuteStage { - return &TimeMuteStage{ti} +func NewTimeMuteStage(m types.TimeMuter) *TimeMuteStage { + return &TimeMuteStage{m} } // Exec implements the stage interface for TimeMuteStage. @@ -889,7 +889,12 @@ func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*type return ctx, alerts, errors.New("missing now timestamp") } - muted, err := inTimeIntervals(now, tms.Times, muteTimeIntervalNames) + // Skip this stage if there are no mute timings. + if len(muteTimeIntervalNames) == 0 { + return ctx, alerts, nil + } + + muted, err := tms.muter.Mutes(muteTimeIntervalNames, now) if err != nil { return ctx, alerts, err } @@ -904,8 +909,8 @@ func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*type type TimeActiveStage timeStage -func NewTimeActiveStage(ti map[string][]timeinterval.TimeInterval) *TimeActiveStage { - return &TimeActiveStage{ti} +func NewTimeActiveStage(m types.TimeMuter) *TimeActiveStage { + return &TimeActiveStage{m} } // Exec implements the stage interface for TimeActiveStage. @@ -926,32 +931,16 @@ func (tas TimeActiveStage) Exec(ctx context.Context, l log.Logger, alerts ...*ty return ctx, alerts, errors.New("missing now timestamp") } - active, err := inTimeIntervals(now, tas.Times, activeTimeIntervalNames) + muted, err := tas.muter.Mutes(activeTimeIntervalNames, now) if err != nil { return ctx, alerts, err } // If the current time is not inside an active time, all alerts are removed from the pipeline - if !active { + if !muted { level.Debug(l).Log("msg", "Notifications not sent, route is not within active time") return ctx, nil, nil } return ctx, alerts, nil } - -// inTimeIntervals returns true if the current time is contained in one of the given time intervals. -func inTimeIntervals(now time.Time, intervals map[string][]timeinterval.TimeInterval, intervalNames []string) (bool, error) { - for _, name := range intervalNames { - interval, ok := intervals[name] - if !ok { - return false, errors.Errorf("time interval %s doesn't exist in config", name) - } - for _, ti := range interval { - if ti.ContainsTime(now.UTC()) { - return true, nil - } - } - } - return false, nil -} diff --git a/notify/notify_test.go b/notify/notify_test.go index 810131775d..d3eeb4670a 100644 --- a/notify/notify_test.go +++ b/notify/notify_test.go @@ -840,7 +840,8 @@ func TestTimeMuteStage(t *testing.T) { t.Fatalf("Couldn't unmarshal time interval %s", err) } m := map[string][]timeinterval.TimeInterval{"test": intervals} - stage := NewTimeMuteStage(m) + intervener := timeinterval.NewIntervener(m) + stage := NewTimeMuteStage(intervener) outAlerts := []*types.Alert{} nonMuteCount := 0 @@ -924,7 +925,8 @@ func TestTimeActiveStage(t *testing.T) { t.Fatalf("Couldn't unmarshal time interval %s", err) } m := map[string][]timeinterval.TimeInterval{"test": intervals} - stage := NewTimeActiveStage(m) + intervener := timeinterval.NewIntervener(m) + stage := NewTimeActiveStage(intervener) outAlerts := []*types.Alert{} nonMuteCount := 0 diff --git a/timeinterval/timeinterval.go b/timeinterval/timeinterval.go index a5018aaef9..fe8c97d729 100644 --- a/timeinterval/timeinterval.go +++ b/timeinterval/timeinterval.go @@ -27,6 +27,35 @@ import ( "gopkg.in/yaml.v2" ) +// Intervener determines whether a given time and active route time interval should mute outgoing notifications. +// It implements the TimeMuter interface. +type Intervener struct { + intervals map[string][]TimeInterval +} + +func (i *Intervener) Mutes(names []string, now time.Time) (bool, error) { + for _, name := range names { + interval, ok := i.intervals[name] + if !ok { + return false, fmt.Errorf("time interval %s doesn't exist in config", name) + } + + for _, ti := range interval { + if ti.ContainsTime(now.UTC()) { + return true, nil + } + } + } + + return false, nil +} + +func NewIntervener(ti map[string][]TimeInterval) *Intervener { + return &Intervener{ + intervals: ti, + } +} + // TimeInterval describes intervals of time. ContainsTime will tell you if a golang time is contained // within the interval. type TimeInterval struct { @@ -436,9 +465,6 @@ func (ir InclusiveRange) MarshalYAML() (interface{}, error) { return string(bytes), err } -// TimeLayout specifies the layout to be used in time.Parse() calls for time intervals. -const TimeLayout = "15:04" - var ( validTime = "^((([01][0-9])|(2[0-3])):[0-5][0-9])$|(^24:00$)" validTimeRE = regexp.MustCompile(validTime) diff --git a/timeinterval/timeinterval_test.go b/timeinterval/timeinterval_test.go index 9c3a2c1d87..8377ac119f 100644 --- a/timeinterval/timeinterval_test.go +++ b/timeinterval/timeinterval_test.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" ) @@ -659,3 +660,97 @@ func mustLoadLocation(name string) *time.Location { } return loc } + +func TestIntervener_Mutes(t *testing.T) { + // muteIn mutes alerts outside business hours in November, using the +1100 timezone. + muteIn := ` +--- +- weekdays: + - monday:friday + location: Australia/Sydney + months: + - November + times: + - start_time: 00:00 + end_time: 09:00 + - start_time: 17:00 + end_time: 24:00 +- weekdays: + - saturday + - sunday + months: + - November + location: 'Australia/Sydney' +` + intervalName := "test" + var intervals []TimeInterval + err := yaml.Unmarshal([]byte(muteIn), &intervals) + require.NoError(t, err) + m := map[string][]TimeInterval{intervalName: intervals} + + tc := []struct { + name string + firedAt string + expected bool + err error + }{ + { + name: "Should not mute on Friday during business hours", + firedAt: "19 Nov 21 13:00 +1100", + expected: false, + }, + { + name: "Should not mute on a Tuesday before 5pm", + firedAt: "16 Nov 21 16:59 +1100", + expected: false, + }, + { + name: "Should mute on a Saturday", + firedAt: "20 Nov 21 10:00 +1100", + expected: true, + }, + { + name: "Should mute before 9am on a Wednesday", + firedAt: "17 Nov 21 05:00 +1100", + expected: true, + }, + { + name: "Should mute even if we are in a different timezone (KST)", + firedAt: "14 Nov 21 20:00 +0900", + expected: true, + }, + { + name: "Should mute even if the timezone is UTC", + firedAt: "14 Nov 21 21:30 +0000", + expected: true, + }, + { + name: "Should not mute different timezone (KST)", + firedAt: "15 Nov 22 14:30 +0900", + expected: false, + }, + { + name: "Should mute in a different timezone (PET)", + firedAt: "15 Nov 21 02:00 -0500", + expected: true, + }, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + now, err := time.Parse(time.RFC822Z, tt.firedAt) + require.NoError(t, err) + + intervener := NewIntervener(m) + + expected, err := intervener.Mutes([]string{intervalName}, now) + if err != nil { + require.Error(t, tt.err) + require.False(t, tt.expected) + } + + require.NoError(t, err) + require.Equal(t, expected, tt.expected) + }) + } +} diff --git a/types/types.go b/types/types.go index f3101f8234..b427a3d1d3 100644 --- a/types/types.go +++ b/types/types.go @@ -381,6 +381,11 @@ type Muter interface { Mutes(model.LabelSet) bool } +// TimeMuter determines if alerts should be muted based on the specified current time and active time interval on the route. +type TimeMuter interface { + Mutes(timeIntervalName []string, now time.Time) (bool, error) +} + // A MuteFunc is a function that implements the Muter interface. type MuteFunc func(model.LabelSet) bool From 9e26979dc9239b88979d1a36b7c2cebf65596bfe Mon Sep 17 00:00:00 2001 From: Jayapriya Pai Date: Tue, 17 Oct 2023 07:35:50 +0530 Subject: [PATCH 14/20] fix: Bump golang.org/x/net to v0.17.0 Address CVE-2023-39325 Signed-off-by: Jayapriya Pai --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 9549cbb6c3..44173dfb17 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/xlab/treeprint v1.2.0 go.uber.org/atomic v1.11.0 golang.org/x/mod v0.12.0 - golang.org/x/net v0.15.0 + golang.org/x/net v0.17.0 golang.org/x/text v0.13.0 golang.org/x/tools v0.13.0 gopkg.in/telebot.v3 v3.1.3 @@ -84,10 +84,10 @@ require ( go.mongodb.org/mongo-driver v1.11.3 // indirect go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect - golang.org/x/crypto v0.13.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e572c6e1b6..1f510e8fe5 100644 --- a/go.sum +++ b/go.sum @@ -608,8 +608,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -699,8 +699,8 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -824,8 +824,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 98290c33491a24d8de07253b158c57418cb03bb1 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Tue, 17 Oct 2023 09:50:33 +0100 Subject: [PATCH 15/20] Add duration to the notify success message (#3559) This commit updates Alertmanager to add a duration to the notify success message. It complements the existing histogram to offer fine-grained information about notification attempts. This can be useful when debuggin duplicate notifications, for example, when the duration exceeds peer_timeout. Signed-off-by: George Robinson --- notify/notify.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/notify/notify.go b/notify/notify.go index 2c9f768a36..891f3c23ee 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -792,7 +792,8 @@ func (r RetryStage) exec(ctx context.Context, l log.Logger, alerts ...*types.Ale case <-tick.C: now := time.Now() retry, err := r.integration.Notify(ctx, sent...) - r.metrics.notificationLatencySeconds.WithLabelValues(r.labelValues...).Observe(time.Since(now).Seconds()) + dur := time.Since(now) + r.metrics.notificationLatencySeconds.WithLabelValues(r.labelValues...).Observe(dur.Seconds()) r.metrics.numNotificationRequestsTotal.WithLabelValues(r.labelValues...).Inc() if err != nil { r.metrics.numNotificationRequestsFailedTotal.WithLabelValues(r.labelValues...).Inc() @@ -813,7 +814,7 @@ func (r RetryStage) exec(ctx context.Context, l log.Logger, alerts ...*types.Ale lvl = level.Debug(log.With(l, "alerts", fmt.Sprintf("%v", alerts))) } - lvl.Log("msg", "Notify success", "attempts", i) + lvl.Log("msg", "Notify success", "attempts", i, "duration", dur) return ctx, alerts, nil } case <-ctx.Done(): From 412f06255a1c09b16eed91d22edbc6464c606008 Mon Sep 17 00:00:00 2001 From: Alexander Weaver Date: Tue, 17 Oct 2023 08:45:44 -0500 Subject: [PATCH 16/20] Separate and export BuildReceiverIntegrations (#3553) * Move and export BuildReceiverIntegrations Signed-off-by: Alex Weaver --------- Signed-off-by: Alex Weaver --- cmd/alertmanager/main.go | 78 +----------------------- cmd/alertmanager/main_test.go | 68 --------------------- config/receiver/receiver.go | 100 +++++++++++++++++++++++++++++++ config/receiver/receiver_test.go | 88 +++++++++++++++++++++++++++ 4 files changed, 190 insertions(+), 144 deletions(-) create mode 100644 config/receiver/receiver.go create mode 100644 config/receiver/receiver_test.go diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go index 2d4a58e254..bfb781fdea 100644 --- a/cmd/alertmanager/main.go +++ b/cmd/alertmanager/main.go @@ -45,24 +45,12 @@ import ( "github.com/prometheus/alertmanager/api" "github.com/prometheus/alertmanager/cluster" "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/config/receiver" "github.com/prometheus/alertmanager/dispatch" "github.com/prometheus/alertmanager/featurecontrol" "github.com/prometheus/alertmanager/inhibit" "github.com/prometheus/alertmanager/nflog" "github.com/prometheus/alertmanager/notify" - "github.com/prometheus/alertmanager/notify/discord" - "github.com/prometheus/alertmanager/notify/email" - "github.com/prometheus/alertmanager/notify/msteams" - "github.com/prometheus/alertmanager/notify/opsgenie" - "github.com/prometheus/alertmanager/notify/pagerduty" - "github.com/prometheus/alertmanager/notify/pushover" - "github.com/prometheus/alertmanager/notify/slack" - "github.com/prometheus/alertmanager/notify/sns" - "github.com/prometheus/alertmanager/notify/telegram" - "github.com/prometheus/alertmanager/notify/victorops" - "github.com/prometheus/alertmanager/notify/webex" - "github.com/prometheus/alertmanager/notify/webhook" - "github.com/prometheus/alertmanager/notify/wechat" "github.com/prometheus/alertmanager/provider/mem" "github.com/prometheus/alertmanager/silence" "github.com/prometheus/alertmanager/template" @@ -131,68 +119,6 @@ func instrumentHandler(handlerName string, handler http.HandlerFunc) http.Handle const defaultClusterAddr = "0.0.0.0:9094" -// buildReceiverIntegrations builds a list of integration notifiers off of a -// receiver config. -func buildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logger log.Logger) ([]notify.Integration, error) { - var ( - errs types.MultiError - integrations []notify.Integration - add = func(name string, i int, rs notify.ResolvedSender, f func(l log.Logger) (notify.Notifier, error)) { - n, err := f(log.With(logger, "integration", name)) - if err != nil { - errs.Add(err) - return - } - integrations = append(integrations, notify.NewIntegration(n, rs, name, i, nc.Name)) - } - ) - - for i, c := range nc.WebhookConfigs { - add("webhook", i, c, func(l log.Logger) (notify.Notifier, error) { return webhook.New(c, tmpl, l) }) - } - for i, c := range nc.EmailConfigs { - add("email", i, c, func(l log.Logger) (notify.Notifier, error) { return email.New(c, tmpl, l), nil }) - } - for i, c := range nc.PagerdutyConfigs { - add("pagerduty", i, c, func(l log.Logger) (notify.Notifier, error) { return pagerduty.New(c, tmpl, l) }) - } - for i, c := range nc.OpsGenieConfigs { - add("opsgenie", i, c, func(l log.Logger) (notify.Notifier, error) { return opsgenie.New(c, tmpl, l) }) - } - for i, c := range nc.WechatConfigs { - add("wechat", i, c, func(l log.Logger) (notify.Notifier, error) { return wechat.New(c, tmpl, l) }) - } - for i, c := range nc.SlackConfigs { - add("slack", i, c, func(l log.Logger) (notify.Notifier, error) { return slack.New(c, tmpl, l) }) - } - for i, c := range nc.VictorOpsConfigs { - add("victorops", i, c, func(l log.Logger) (notify.Notifier, error) { return victorops.New(c, tmpl, l) }) - } - for i, c := range nc.PushoverConfigs { - add("pushover", i, c, func(l log.Logger) (notify.Notifier, error) { return pushover.New(c, tmpl, l) }) - } - for i, c := range nc.SNSConfigs { - add("sns", i, c, func(l log.Logger) (notify.Notifier, error) { return sns.New(c, tmpl, l) }) - } - for i, c := range nc.TelegramConfigs { - add("telegram", i, c, func(l log.Logger) (notify.Notifier, error) { return telegram.New(c, tmpl, l) }) - } - for i, c := range nc.DiscordConfigs { - add("discord", i, c, func(l log.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l) }) - } - for i, c := range nc.WebexConfigs { - add("webex", i, c, func(l log.Logger) (notify.Notifier, error) { return webex.New(c, tmpl, l) }) - } - for i, c := range nc.MSTeamsConfigs { - add("msteams", i, c, func(l log.Logger) (notify.Notifier, error) { return msteams.New(c, tmpl, l) }) - } - - if errs.Len() > 0 { - return nil, &errs - } - return integrations, nil -} - func main() { os.Exit(run()) } @@ -459,7 +385,7 @@ func run() int { level.Info(configLogger).Log("msg", "skipping creation of receiver not referenced by any route", "receiver", rcv.Name) continue } - integrations, err := buildReceiverIntegrations(rcv, tmpl, logger) + integrations, err := receiver.BuildReceiverIntegrations(rcv, tmpl, logger) if err != nil { return err } diff --git a/cmd/alertmanager/main_test.go b/cmd/alertmanager/main_test.go index a45fa516f0..8939853608 100644 --- a/cmd/alertmanager/main_test.go +++ b/cmd/alertmanager/main_test.go @@ -18,77 +18,9 @@ import ( "testing" "github.com/go-kit/log" - commoncfg "github.com/prometheus/common/config" "github.com/stretchr/testify/require" - - "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/notify" ) -type sendResolved bool - -func (s sendResolved) SendResolved() bool { return bool(s) } - -func TestBuildReceiverIntegrations(t *testing.T) { - for _, tc := range []struct { - receiver config.Receiver - err bool - exp []notify.Integration - }{ - { - receiver: config.Receiver{ - Name: "foo", - WebhookConfigs: []*config.WebhookConfig{ - { - HTTPConfig: &commoncfg.HTTPClientConfig{}, - }, - { - HTTPConfig: &commoncfg.HTTPClientConfig{}, - NotifierConfig: config.NotifierConfig{ - VSendResolved: true, - }, - }, - }, - }, - exp: []notify.Integration{ - notify.NewIntegration(nil, sendResolved(false), "webhook", 0, "foo"), - notify.NewIntegration(nil, sendResolved(true), "webhook", 1, "foo"), - }, - }, - { - receiver: config.Receiver{ - Name: "foo", - WebhookConfigs: []*config.WebhookConfig{ - { - HTTPConfig: &commoncfg.HTTPClientConfig{ - TLSConfig: commoncfg.TLSConfig{ - CAFile: "not_existing", - }, - }, - }, - }, - }, - err: true, - }, - } { - tc := tc - t.Run("", func(t *testing.T) { - integrations, err := buildReceiverIntegrations(tc.receiver, nil, nil) - if tc.err { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Len(t, integrations, len(tc.exp)) - for i := range tc.exp { - require.Equal(t, tc.exp[i].SendResolved(), integrations[i].SendResolved()) - require.Equal(t, tc.exp[i].Name(), integrations[i].Name()) - require.Equal(t, tc.exp[i].Index(), integrations[i].Index()) - } - }) - } -} - func TestExternalURL(t *testing.T) { hostname := "foo" for _, tc := range []struct { diff --git a/config/receiver/receiver.go b/config/receiver/receiver.go new file mode 100644 index 0000000000..9bb039ef05 --- /dev/null +++ b/config/receiver/receiver.go @@ -0,0 +1,100 @@ +// Copyright 2023 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package receiver + +import ( + "github.com/go-kit/log" + + commoncfg "github.com/prometheus/common/config" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/notify/discord" + "github.com/prometheus/alertmanager/notify/email" + "github.com/prometheus/alertmanager/notify/msteams" + "github.com/prometheus/alertmanager/notify/opsgenie" + "github.com/prometheus/alertmanager/notify/pagerduty" + "github.com/prometheus/alertmanager/notify/pushover" + "github.com/prometheus/alertmanager/notify/slack" + "github.com/prometheus/alertmanager/notify/sns" + "github.com/prometheus/alertmanager/notify/telegram" + "github.com/prometheus/alertmanager/notify/victorops" + "github.com/prometheus/alertmanager/notify/webex" + "github.com/prometheus/alertmanager/notify/webhook" + "github.com/prometheus/alertmanager/notify/wechat" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" +) + +// BuildReceiverIntegrations builds a list of integration notifiers off of a +// receiver config. +func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logger log.Logger, httpOpts ...commoncfg.HTTPClientOption) ([]notify.Integration, error) { + var ( + errs types.MultiError + integrations []notify.Integration + add = func(name string, i int, rs notify.ResolvedSender, f func(l log.Logger) (notify.Notifier, error)) { + n, err := f(log.With(logger, "integration", name)) + if err != nil { + errs.Add(err) + return + } + integrations = append(integrations, notify.NewIntegration(n, rs, name, i, nc.Name)) + } + ) + + for i, c := range nc.WebhookConfigs { + add("webhook", i, c, func(l log.Logger) (notify.Notifier, error) { return webhook.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.EmailConfigs { + add("email", i, c, func(l log.Logger) (notify.Notifier, error) { return email.New(c, tmpl, l), nil }) + } + for i, c := range nc.PagerdutyConfigs { + add("pagerduty", i, c, func(l log.Logger) (notify.Notifier, error) { return pagerduty.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.OpsGenieConfigs { + add("opsgenie", i, c, func(l log.Logger) (notify.Notifier, error) { return opsgenie.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.WechatConfigs { + add("wechat", i, c, func(l log.Logger) (notify.Notifier, error) { return wechat.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.SlackConfigs { + add("slack", i, c, func(l log.Logger) (notify.Notifier, error) { return slack.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.VictorOpsConfigs { + add("victorops", i, c, func(l log.Logger) (notify.Notifier, error) { return victorops.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.PushoverConfigs { + add("pushover", i, c, func(l log.Logger) (notify.Notifier, error) { return pushover.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.SNSConfigs { + add("sns", i, c, func(l log.Logger) (notify.Notifier, error) { return sns.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.TelegramConfigs { + add("telegram", i, c, func(l log.Logger) (notify.Notifier, error) { return telegram.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.DiscordConfigs { + add("discord", i, c, func(l log.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.WebexConfigs { + add("webex", i, c, func(l log.Logger) (notify.Notifier, error) { return webex.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.MSTeamsConfigs { + add("msteams", i, c, func(l log.Logger) (notify.Notifier, error) { return msteams.New(c, tmpl, l, httpOpts...) }) + } + + if errs.Len() > 0 { + return nil, &errs + } + return integrations, nil +} diff --git a/config/receiver/receiver_test.go b/config/receiver/receiver_test.go new file mode 100644 index 0000000000..3d146a98d0 --- /dev/null +++ b/config/receiver/receiver_test.go @@ -0,0 +1,88 @@ +// Copyright 2023 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package receiver + +import ( + "testing" + + commoncfg "github.com/prometheus/common/config" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" +) + +type sendResolved bool + +func (s sendResolved) SendResolved() bool { return bool(s) } + +func TestBuildReceiverIntegrations(t *testing.T) { + for _, tc := range []struct { + receiver config.Receiver + err bool + exp []notify.Integration + }{ + { + receiver: config.Receiver{ + Name: "foo", + WebhookConfigs: []*config.WebhookConfig{ + { + HTTPConfig: &commoncfg.HTTPClientConfig{}, + }, + { + HTTPConfig: &commoncfg.HTTPClientConfig{}, + NotifierConfig: config.NotifierConfig{ + VSendResolved: true, + }, + }, + }, + }, + exp: []notify.Integration{ + notify.NewIntegration(nil, sendResolved(false), "webhook", 0, "foo"), + notify.NewIntegration(nil, sendResolved(true), "webhook", 1, "foo"), + }, + }, + { + receiver: config.Receiver{ + Name: "foo", + WebhookConfigs: []*config.WebhookConfig{ + { + HTTPConfig: &commoncfg.HTTPClientConfig{ + TLSConfig: commoncfg.TLSConfig{ + CAFile: "not_existing", + }, + }, + }, + }, + }, + err: true, + }, + } { + tc := tc + t.Run("", func(t *testing.T) { + integrations, err := BuildReceiverIntegrations(tc.receiver, nil, nil) + if tc.err { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Len(t, integrations, len(tc.exp)) + for i := range tc.exp { + require.Equal(t, tc.exp[i].SendResolved(), integrations[i].SendResolved()) + require.Equal(t, tc.exp[i].Name(), integrations[i].Name()) + require.Equal(t, tc.exp[i].Index(), integrations[i].Index()) + } + }) + } +} From 16aa996c4f7a89fdf2e5999171ef913977ff8adf Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 19 Oct 2023 12:00:01 +0100 Subject: [PATCH 17/20] Support UTF-8 label matchers: Add compat package with feature flag and use in amtool (#3483) * Add adapter package for parser feature flag This commit adds the compat package allowing users to switch between the new matchers/parse parser and the old pkg/labels parser. The new matchers/parse parser uses a fallback mechanism where if the input cannot be parsed in the new parser it then attempts to use the old parser. If an input is parsed in the old parser but not the new parser, then a warning log is emitted. --------- Signed-off-by: George Robinson --- cli/alert_add.go | 40 ++++++-- cli/alert_query.go | 4 +- cli/root.go | 27 ++++- cli/silence_add.go | 18 ++-- cli/silence_query.go | 7 +- cli/test_routing.go | 15 ++- cli/utils.go | 37 +------ featurecontrol/featurecontrol.go | 49 ++++++++- matchers/compat/parse.go | 170 +++++++++++++++++++++++++++++++ matchers/compat/parse_test.go | 112 ++++++++++++++++++++ test/cli/acceptance/cli_test.go | 2 +- 11 files changed, 416 insertions(+), 65 deletions(-) create mode 100644 matchers/compat/parse.go create mode 100644 matchers/compat/parse_test.go diff --git a/cli/alert_add.go b/cli/alert_add.go index e27d1522b3..6018b95654 100644 --- a/cli/alert_add.go +++ b/cli/alert_add.go @@ -15,7 +15,9 @@ package cli import ( "context" + "errors" "fmt" + "strconv" "time" "github.com/alecthomas/kingpin/v2" @@ -23,6 +25,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/alert" "github.com/prometheus/alertmanager/api/v2/models" + "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/pkg/labels" ) type alertAddCmd struct { @@ -73,29 +77,45 @@ func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) err if len(a.labels) > 0 { // Allow the alertname label to be defined implicitly as the first argument rather // than explicitly as a key=value pair. - if _, err := parseLabels([]string{a.labels[0]}); err != nil { - a.labels[0] = fmt.Sprintf("alertname=%s", a.labels[0]) + if _, err := compat.Matcher(a.labels[0]); err != nil { + a.labels[0] = fmt.Sprintf("alertname=%s", strconv.Quote(a.labels[0])) } } - labels, err := parseLabels(a.labels) - if err != nil { - return err + ls := make(models.LabelSet, len(a.labels)) + for _, l := range a.labels { + matcher, err := compat.Matcher(l) + if err != nil { + return err + } + if matcher.Type != labels.MatchEqual { + return errors.New("labels must be specified as key=value pairs") + } + ls[matcher.Name] = matcher.Value } - annotations, err := parseLabels(a.annotations) - if err != nil { - return err + annotations := make(models.LabelSet, len(a.annotations)) + for _, a := range a.annotations { + matcher, err := compat.Matcher(a) + if err != nil { + return err + } + if matcher.Type != labels.MatchEqual { + return errors.New("annotations must be specified as key=value pairs") + } + annotations[matcher.Name] = matcher.Value } var startsAt, endsAt time.Time if a.start != "" { + var err error startsAt, err = time.Parse(time.RFC3339, a.start) if err != nil { return err } } if a.end != "" { + var err error endsAt, err = time.Parse(time.RFC3339, a.end) if err != nil { return err @@ -105,7 +125,7 @@ func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) err pa := &models.PostableAlert{ Alert: models.Alert{ GeneratorURL: strfmt.URI(a.generatorURL), - Labels: labels, + Labels: ls, }, Annotations: annotations, StartsAt: strfmt.DateTime(startsAt), @@ -116,6 +136,6 @@ func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) err amclient := NewAlertmanagerClient(alertmanagerURL) - _, err = amclient.Alert.PostAlerts(alertParams) + _, err := amclient.Alert.PostAlerts(alertParams) return err } diff --git a/cli/alert_query.go b/cli/alert_query.go index ff06b439b7..219d76fdb9 100644 --- a/cli/alert_query.go +++ b/cli/alert_query.go @@ -22,7 +22,7 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/alert" "github.com/prometheus/alertmanager/cli/format" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matchers/compat" ) type alertQueryCmd struct { @@ -80,7 +80,7 @@ func (a *alertQueryCmd) queryAlerts(ctx context.Context, _ *kingpin.ParseContext // the user wants alertname= and prepend `alertname=` to // the front. m := a.matcherGroups[0] - _, err := labels.ParseMatcher(m) + _, err := compat.Matcher(m) if err != nil { a.matcherGroups[0] = fmt.Sprintf("alertname=%s", m) } diff --git a/cli/root.go b/cli/root.go index 09043159db..69c1022c6a 100644 --- a/cli/root.go +++ b/cli/root.go @@ -18,9 +18,13 @@ import ( "net/url" "os" "path" + "strings" "time" "github.com/alecthomas/kingpin/v2" + "github.com/go-kit/log" + "github.com/go-kit/log/level" + clientruntime "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" promconfig "github.com/prometheus/common/config" "github.com/prometheus/common/version" @@ -29,8 +33,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/client" "github.com/prometheus/alertmanager/cli/config" "github.com/prometheus/alertmanager/cli/format" - - clientruntime "github.com/go-openapi/runtime/client" + "github.com/prometheus/alertmanager/featurecontrol" + "github.com/prometheus/alertmanager/matchers/compat" ) var ( @@ -40,11 +44,27 @@ var ( timeout time.Duration httpConfigFile string versionCheck bool + featureFlags string configFiles = []string{os.ExpandEnv("$HOME/.config/amtool/config.yml"), "/etc/amtool/config.yml"} legacyFlags = map[string]string{"comment_required": "require-comment"} ) +func initMatchersCompat(_ *kingpin.ParseContext) error { + logger := log.NewLogfmtLogger(os.Stdout) + if verbose { + logger = level.NewFilter(logger, level.AllowDebug()) + } else { + logger = level.NewFilter(logger, level.AllowInfo()) + } + featureConfig, err := featurecontrol.NewFlags(logger, featureFlags) + if err != nil { + kingpin.Fatalf("error parsing the feature flag list: %v\n", err) + } + compat.InitFromFlags(logger, featureConfig) + return nil +} + func requireAlertManagerURL(pc *kingpin.ParseContext) error { // Return without error if any help flag is set. for _, elem := range pc.Elements { @@ -137,6 +157,7 @@ func Execute() { app.Flag("timeout", "Timeout for the executed command").Default("30s").DurationVar(&timeout) app.Flag("http.config.file", "HTTP client configuration file for amtool to connect to Alertmanager.").PlaceHolder("").ExistingFileVar(&httpConfigFile) app.Flag("version-check", "Check alertmanager version. Use --no-version-check to disable.").Default("true").BoolVar(&versionCheck) + app.Flag("enable-feature", fmt.Sprintf("Experimental features to enable. The flag can be repeated to enable multiple features. Valid options: %s", strings.Join(featurecontrol.AllowedFlags, ", "))).Default("").StringVar(&featureFlags) app.Version(version.Print("amtool")) app.GetFlag("help").Short('h') @@ -154,6 +175,8 @@ func Execute() { configureConfigCmd(app) configureTemplateCmd(app) + app.Action(initMatchersCompat) + err = resolver.Bind(app, os.Args[1:]) if err != nil { kingpin.Fatalf("%v\n", err) diff --git a/cli/silence_add.go b/cli/silence_add.go index 3125c512f8..d30a523431 100644 --- a/cli/silence_add.go +++ b/cli/silence_add.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "os/user" + "strconv" "time" "github.com/alecthomas/kingpin/v2" @@ -26,6 +27,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/silence" "github.com/prometheus/alertmanager/api/v2/models" + "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/pkg/labels" ) func username() string { @@ -92,17 +95,20 @@ func (c *silenceAddCmd) add(ctx context.Context, _ *kingpin.ParseContext) error // If the parser fails then we likely don't have a (=|=~|!=|!~) so lets // assume that the user wants alertname= and prepend `alertname=` // to the front. - _, err := parseMatchers([]string{c.matchers[0]}) + _, err := compat.Matcher(c.matchers[0]) if err != nil { - c.matchers[0] = fmt.Sprintf("alertname=%s", c.matchers[0]) + c.matchers[0] = fmt.Sprintf("alertname=%s", strconv.Quote(c.matchers[0])) } } - matchers, err := parseMatchers(c.matchers) - if err != nil { - return err + matchers := make([]labels.Matcher, 0, len(c.matchers)) + for _, s := range c.matchers { + m, err := compat.Matcher(s) + if err != nil { + return err + } + matchers = append(matchers, *m) } - if len(matchers) < 1 { return fmt.Errorf("no matchers specified") } diff --git a/cli/silence_query.go b/cli/silence_query.go index 89a2722be0..ddffca3b3c 100644 --- a/cli/silence_query.go +++ b/cli/silence_query.go @@ -17,6 +17,7 @@ import ( "context" "errors" "fmt" + "strconv" "time" kingpin "github.com/alecthomas/kingpin/v2" @@ -24,7 +25,7 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/silence" "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/cli/format" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matchers/compat" ) type silenceQueryCmd struct { @@ -98,9 +99,9 @@ func (c *silenceQueryCmd) query(ctx context.Context, _ *kingpin.ParseContext) er // If the parser fails then we likely don't have a (=|=~|!=|!~) so lets // assume that the user wants alertname= and prepend `alertname=` // to the front. - _, err := labels.ParseMatcher(c.matchers[0]) + _, err := compat.Matcher(c.matchers[0]) if err != nil { - c.matchers[0] = fmt.Sprintf("alertname=%s", c.matchers[0]) + c.matchers[0] = fmt.Sprintf("alertname=%s", strconv.Quote(c.matchers[0])) } } diff --git a/cli/test_routing.go b/cli/test_routing.go index e93f40b7a1..85c29a7e2f 100644 --- a/cli/test_routing.go +++ b/cli/test_routing.go @@ -24,6 +24,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/matchers/compat" + "github.com/prometheus/alertmanager/pkg/labels" ) const routingTestHelp = `Test alert routing @@ -80,9 +82,16 @@ func (c *routingShow) routingTestAction(ctx context.Context, _ *kingpin.ParseCon mainRoute := dispatch.NewRoute(cfg.Route, nil) // Parse labels to LabelSet. - ls, err := parseLabels(c.labels) - if err != nil { - kingpin.Fatalf("Failed to parse labels: %v\n", err) + ls := make(models.LabelSet, len(c.labels)) + for _, l := range c.labels { + matcher, err := compat.Matcher(l) + if err != nil { + kingpin.Fatalf("Failed to parse labels: %v\n", err) + } + if matcher.Type != labels.MatchEqual { + kingpin.Fatalf("%s\n", "Labels must be specified as key=value pairs") + } + ls[matcher.Name] = matcher.Value } if c.debugTree { diff --git a/cli/utils.go b/cli/utils.go index 60221e239a..dfe39dd867 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -20,7 +20,7 @@ import ( "net/url" "os" - kingpin "github.com/alecthomas/kingpin/v2" + "github.com/alecthomas/kingpin/v2" "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/api/v2/client/general" @@ -29,22 +29,6 @@ import ( "github.com/prometheus/alertmanager/pkg/labels" ) -// parseMatchers parses a list of matchers (cli arguments). -func parseMatchers(inputMatchers []string) ([]labels.Matcher, error) { - matchers := make([]labels.Matcher, 0, len(inputMatchers)) - - for _, v := range inputMatchers { - matcher, err := labels.ParseMatcher(v) - if err != nil { - return []labels.Matcher{}, err - } - - matchers = append(matchers, *matcher) - } - - return matchers, nil -} - // getRemoteAlertmanagerConfigStatus returns status responsecontaining configuration from remote Alertmanager func getRemoteAlertmanagerConfigStatus(ctx context.Context, alertmanagerURL *url.URL) (*models.AlertmanagerStatus, error) { amclient := NewAlertmanagerClient(alertmanagerURL) @@ -94,25 +78,6 @@ func convertClientToCommonLabelSet(cls models.LabelSet) model.LabelSet { return mls } -// parseLabels parses a list of labels (cli arguments). -func parseLabels(inputLabels []string) (models.LabelSet, error) { - labelSet := make(models.LabelSet, len(inputLabels)) - - for _, l := range inputLabels { - matcher, err := labels.ParseMatcher(l) - if err != nil { - return models.LabelSet{}, err - } - if matcher.Type != labels.MatchEqual { - return models.LabelSet{}, errors.New("labels must be specified as key=value pairs") - } - - labelSet[matcher.Name] = matcher.Value - } - - return labelSet, nil -} - // TypeMatchers only valid for when you are going to add a silence func TypeMatchers(matchers []labels.Matcher) models.Matchers { typeMatchers := make(models.Matchers, len(matchers)) diff --git a/featurecontrol/featurecontrol.go b/featurecontrol/featurecontrol.go index 7a04499545..0108a680e7 100644 --- a/featurecontrol/featurecontrol.go +++ b/featurecontrol/featurecontrol.go @@ -14,6 +14,7 @@ package featurecontrol import ( + "errors" "fmt" "strings" @@ -22,24 +23,42 @@ import ( ) const ( - fcReceiverNameInMetrics = "receiver-name-in-metrics" + fcReceiverNameInMetrics = "receiver-name-in-metrics" + fcClassicMatchersParsing = "classic-matchers-parsing" + fcUTF8MatchersParsing = "utf8-matchers-parsing" ) -var AllowedFlags = []string{fcReceiverNameInMetrics} +var AllowedFlags = []string{ + fcReceiverNameInMetrics, + fcClassicMatchersParsing, + fcUTF8MatchersParsing, +} type Flagger interface { EnableReceiverNamesInMetrics() bool + ClassicMatchersParsing() bool + UTF8MatchersParsing() bool } type Flags struct { logger log.Logger enableReceiverNamesInMetrics bool + classicMatchersParsing bool + utf8MatchersParsing bool } func (f *Flags) EnableReceiverNamesInMetrics() bool { return f.enableReceiverNamesInMetrics } +func (f *Flags) ClassicMatchersParsing() bool { + return f.classicMatchersParsing +} + +func (f *Flags) UTF8MatchersParsing() bool { + return f.utf8MatchersParsing +} + type flagOption func(flags *Flags) func enableReceiverNameInMetrics() flagOption { @@ -48,6 +67,18 @@ func enableReceiverNameInMetrics() flagOption { } } +func enableClassicMatchersParsing() flagOption { + return func(configs *Flags) { + configs.classicMatchersParsing = true + } +} + +func enableUTF8MatchersParsing() flagOption { + return func(configs *Flags) { + configs.utf8MatchersParsing = true + } +} + func NewFlags(logger log.Logger, features string) (Flagger, error) { fc := &Flags{logger: logger} opts := []flagOption{} @@ -61,6 +92,12 @@ func NewFlags(logger log.Logger, features string) (Flagger, error) { case fcReceiverNameInMetrics: opts = append(opts, enableReceiverNameInMetrics()) level.Warn(logger).Log("msg", "Experimental receiver name in metrics enabled") + case fcClassicMatchersParsing: + opts = append(opts, enableClassicMatchersParsing()) + level.Warn(logger).Log("msg", "Classic matchers parsing enabled") + case fcUTF8MatchersParsing: + opts = append(opts, enableUTF8MatchersParsing()) + level.Warn(logger).Log("msg", "UTF-8 matchers parsing enabled") default: return nil, fmt.Errorf("Unknown option '%s' for --enable-feature", feature) } @@ -70,9 +107,17 @@ func NewFlags(logger log.Logger, features string) (Flagger, error) { opt(fc) } + if fc.classicMatchersParsing && fc.utf8MatchersParsing { + return nil, errors.New("Both classic and UTF-8 matchers parsing is enabled, please choose one or remove the flag for both") + } + return fc, nil } type NoopFlags struct{} func (n NoopFlags) EnableReceiverNamesInMetrics() bool { return false } + +func (n NoopFlags) ClassicMatchersParsing() bool { return false } + +func (n NoopFlags) UTF8MatchersParsing() bool { return false } diff --git a/matchers/compat/parse.go b/matchers/compat/parse.go new file mode 100644 index 0000000000..4d2794d36e --- /dev/null +++ b/matchers/compat/parse.go @@ -0,0 +1,170 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compat + +import ( + "fmt" + "strings" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + + "github.com/prometheus/alertmanager/featurecontrol" + "github.com/prometheus/alertmanager/matchers/parse" + "github.com/prometheus/alertmanager/pkg/labels" +) + +var ( + parseMatcher = classicMatcherParser(log.NewNopLogger()) + parseMatchers = classicMatchersParser(log.NewNopLogger()) +) + +type matcherParser func(s string) (*labels.Matcher, error) + +type matchersParser func(s string) (labels.Matchers, error) + +// Matcher parses the matcher in the input string. It returns an error +// if the input is invalid or contains two or more matchers. +func Matcher(s string) (*labels.Matcher, error) { + return parseMatcher(s) +} + +// Matchers parses one or more matchers in the input string. It returns +// an error if the input is invalid. +func Matchers(s string) (labels.Matchers, error) { + return parseMatchers(s) +} + +// InitFromFlags initializes the compat package from the flagger. +func InitFromFlags(l log.Logger, f featurecontrol.Flagger) { + if f.ClassicMatchersParsing() { + parseMatcher = classicMatcherParser(l) + parseMatchers = classicMatchersParser(l) + } else if f.UTF8MatchersParsing() { + parseMatcher = utf8MatcherParser(l) + parseMatchers = utf8MatchersParser(l) + } else { + parseMatcher = fallbackMatcherParser(l) + parseMatchers = fallbackMatchersParser(l) + } +} + +// classicMatcherParser uses the old pkg/labels parser to parse the matcher in +// the input string. +func classicMatcherParser(l log.Logger) matcherParser { + return func(s string) (*labels.Matcher, error) { + level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", s) + return labels.ParseMatcher(s) + } +} + +// classicMatchersParser uses the old pkg/labels parser to parse zero or more +// matchers in the input string. It returns an error if the input is invalid. +func classicMatchersParser(l log.Logger) matchersParser { + return func(s string) (labels.Matchers, error) { + level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", s) + return labels.ParseMatchers(s) + } +} + +// utf8MatcherParser uses the new matchers/parse parser to parse +// the matcher in the input string. If this fails it does not fallback +// to the old pkg/labels parser. +func utf8MatcherParser(l log.Logger) matcherParser { + return func(s string) (*labels.Matcher, error) { + level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", s) + if strings.HasPrefix(s, "{") || strings.HasSuffix(s, "}") { + return nil, fmt.Errorf("unexpected open or close brace: %s", s) + } + return parse.Matcher(s) + } +} + +// utf8MatchersParser uses the new matchers/parse parser to parse +// zero or more matchers in the input string. If this fails it +// does not fallback to the old pkg/labels parser. +func utf8MatchersParser(l log.Logger) matchersParser { + return func(s string) (labels.Matchers, error) { + level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", s) + return parse.Matchers(s) + } +} + +// fallbackMatcherParser uses the new matchers/parse parser to parse +// zero or more matchers in the string. If this fails it falls back to +// the old pkg/labels parser and emits a warning log line. +func fallbackMatcherParser(l log.Logger) matcherParser { + return func(s string) (*labels.Matcher, error) { + var ( + m *labels.Matcher + err error + invalidErr error + ) + level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", s) + if strings.HasPrefix(s, "{") || strings.HasSuffix(s, "}") { + return nil, fmt.Errorf("unexpected open or close brace: %s", s) + } + m, err = parse.Matcher(s) + if err != nil { + m, invalidErr = labels.ParseMatcher(s) + if invalidErr != nil { + // The input is not valid in the old pkg/labels parser either, + // it cannot be valid input. + return nil, invalidErr + } + // The input is valid in the old pkg/labels parser, but not the + // new matchers/parse parser. + suggestion := m.String() + level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the old matchers parser as a fallback. To make this input compatible with the new parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", s, "err", err, "suggestion", suggestion) + } + return m, nil + } +} + +// fallbackMatchersParser uses the new matchers/parse parser to parse the +// matcher in the input string. If this fails it falls back to the old +// pkg/labels parser and emits a warning log line. +func fallbackMatchersParser(l log.Logger) matchersParser { + return func(s string) (labels.Matchers, error) { + var ( + m []*labels.Matcher + err error + invalidErr error + ) + level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", s) + m, err = parse.Matchers(s) + if err != nil { + m, invalidErr = labels.ParseMatchers(s) + if invalidErr != nil { + // The input is not valid in the old pkg/labels parser either, + // it cannot be valid input. + return nil, invalidErr + } + var sb strings.Builder + sb.WriteRune('{') + for i, n := range m { + sb.WriteString(n.String()) + if i < len(m)-1 { + sb.WriteRune(',') + } + } + sb.WriteRune('}') + suggestion := sb.String() + // The input is valid in the old pkg/labels parser, but not the + // new matchers/parse parser. + level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the old matchers parser as a fallback. To make this input compatible with the new parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", s, "err", err, "suggestion", suggestion) + } + return m, nil + } +} diff --git a/matchers/compat/parse_test.go b/matchers/compat/parse_test.go new file mode 100644 index 0000000000..b3d1f15a35 --- /dev/null +++ b/matchers/compat/parse_test.go @@ -0,0 +1,112 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compat + +import ( + "testing" + + "github.com/go-kit/log" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/pkg/labels" +) + +func TestFallbackMatcherParser(t *testing.T) { + tests := []struct { + name string + input string + expected *labels.Matcher + err string + }{{ + name: "is accepted in both", + input: "foo=bar", + expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + }, { + name: "is accepted in new parser but not old", + input: "foo🙂=bar", + expected: mustNewMatcher(t, labels.MatchEqual, "foo🙂", "bar"), + }, { + name: "is accepted in old parser but not new", + input: "foo=!bar", + expected: mustNewMatcher(t, labels.MatchEqual, "foo", "!bar"), + }, { + name: "is accepted in neither", + input: "foo!bar", + err: "bad matcher format: foo!bar", + }} + f := fallbackMatcherParser(log.NewNopLogger()) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + matcher, err := f(test.input) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.Nil(t, err) + require.EqualValues(t, test.expected, matcher) + } + }) + } +} + +func TestFallbackMatchersParser(t *testing.T) { + tests := []struct { + name string + input string + expected labels.Matchers + err string + }{{ + name: "is accepted in both", + input: "{foo=bar,bar=baz}", + expected: labels.Matchers{ + mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + mustNewMatcher(t, labels.MatchEqual, "bar", "baz"), + }, + }, { + name: "is accepted in new parser but not old", + input: "{foo🙂=bar,bar=baz🙂}", + expected: labels.Matchers{ + mustNewMatcher(t, labels.MatchEqual, "foo🙂", "bar"), + mustNewMatcher(t, labels.MatchEqual, "bar", "baz🙂"), + }, + }, { + name: "is accepted in old parser but not new", + input: "{foo=!bar,bar=$baz}", + expected: labels.Matchers{ + mustNewMatcher(t, labels.MatchEqual, "foo", "!bar"), + mustNewMatcher(t, labels.MatchEqual, "bar", "$baz"), + }, + }, { + name: "is accepted in neither", + input: "{foo!bar}", + err: "bad matcher format: foo!bar", + }} + f := fallbackMatchersParser(log.NewNopLogger()) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + matchers, err := f(test.input) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.Nil(t, err) + require.EqualValues(t, test.expected, matchers) + } + }) + } +} + +func mustNewMatcher(t *testing.T, op labels.MatchType, name, value string) *labels.Matcher { + m, err := labels.NewMatcher(op, name, value) + require.NoError(t, err) + return m +} diff --git a/test/cli/acceptance/cli_test.go b/test/cli/acceptance/cli_test.go index bc6af58599..93afe7884f 100644 --- a/test/cli/acceptance/cli_test.go +++ b/test/cli/acceptance/cli_test.go @@ -269,5 +269,5 @@ receivers: // Bad labels should return error out, err := am.TestRoute("{foo=bar}") require.EqualError(t, err, "exit status 1") - require.Equal(t, "amtool: error: Failed to parse labels: bad matcher format: {foo=bar}\n\n", string(out)) + require.Equal(t, "amtool: error: Failed to parse labels: unexpected open or close brace: {foo=bar}\n\n", string(out)) } From 2ec6b89aa74a5cb67717a095d4c5fd66395eb614 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Thu, 19 Oct 2023 12:10:32 +0100 Subject: [PATCH 18/20] Bump: go-swagger to latest stable Bumps the API spec/client generation util of `go-swagger` from 0.30.2 to 0.30.5 which is the latest stable. Signed-off-by: gotjosh --- Makefile | 2 +- api/v2/client/alert/get_alerts_responses.go | 17 ++++++++++++++++- api/v2/client/alert/post_alerts_responses.go | 17 ++++++++++++++++- .../alertgroup/get_alert_groups_responses.go | 17 ++++++++++++++++- api/v2/client/general/get_status_responses.go | 7 ++++++- .../client/receiver/get_receivers_responses.go | 7 ++++++- .../client/silence/delete_silence_responses.go | 17 ++++++++++++++++- api/v2/client/silence/get_silence_responses.go | 17 ++++++++++++++++- api/v2/client/silence/get_silences_responses.go | 12 +++++++++++- .../client/silence/post_silences_responses.go | 17 ++++++++++++++++- api/v2/models/alert_group.go | 6 ++++++ api/v2/models/alert_groups.go | 5 +++++ api/v2/models/alertmanager_status.go | 3 +++ api/v2/models/cluster_status.go | 5 +++++ api/v2/models/gettable_alert.go | 6 ++++++ api/v2/models/gettable_alerts.go | 5 +++++ api/v2/models/gettable_silence.go | 1 + api/v2/models/gettable_silences.go | 5 +++++ api/v2/models/matchers.go | 5 +++++ api/v2/models/postable_alert.go | 4 ++++ api/v2/models/postable_alerts.go | 5 +++++ api/v2/restapi/operations/alertmanager_api.go | 2 +- 22 files changed, 171 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index e9da94c762..f1e7c0adae 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ SWAGGER = docker run \ --user=$(shell id -u $(USER)):$(shell id -g $(USER)) \ --rm \ -v $(shell pwd):/go/src/github.com/prometheus/alertmanager \ - -w /go/src/github.com/prometheus/alertmanager quay.io/goswagger/swagger:v0.30.3 + -w /go/src/github.com/prometheus/alertmanager quay.io/goswagger/swagger:v0.30.5 api/v2/models api/v2/restapi api/v2/client: api/v2/openapi.yaml -rm -r api/v2/{client,models,restapi} diff --git a/api/v2/client/alert/get_alerts_responses.go b/api/v2/client/alert/get_alerts_responses.go index 516dbc31d9..54fcb9c355 100644 --- a/api/v2/client/alert/get_alerts_responses.go +++ b/api/v2/client/alert/get_alerts_responses.go @@ -56,7 +56,7 @@ func (o *GetAlertsReader) ReadResponse(response runtime.ClientResponse, consumer } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[GET /alerts] getAlerts", response, response.Code()) } } @@ -99,6 +99,11 @@ func (o *GetAlertsOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the get alerts o k response +func (o *GetAlertsOK) Code() int { + return 200 +} + func (o *GetAlertsOK) Error() string { return fmt.Sprintf("[GET /alerts][%d] getAlertsOK %+v", 200, o.Payload) } @@ -160,6 +165,11 @@ func (o *GetAlertsBadRequest) IsCode(code int) bool { return code == 400 } +// Code gets the status code for the get alerts bad request response +func (o *GetAlertsBadRequest) Code() int { + return 400 +} + func (o *GetAlertsBadRequest) Error() string { return fmt.Sprintf("[GET /alerts][%d] getAlertsBadRequest %+v", 400, o.Payload) } @@ -221,6 +231,11 @@ func (o *GetAlertsInternalServerError) IsCode(code int) bool { return code == 500 } +// Code gets the status code for the get alerts internal server error response +func (o *GetAlertsInternalServerError) Code() int { + return 500 +} + func (o *GetAlertsInternalServerError) Error() string { return fmt.Sprintf("[GET /alerts][%d] getAlertsInternalServerError %+v", 500, o.Payload) } diff --git a/api/v2/client/alert/post_alerts_responses.go b/api/v2/client/alert/post_alerts_responses.go index 67be66c6af..7a7955ce79 100644 --- a/api/v2/client/alert/post_alerts_responses.go +++ b/api/v2/client/alert/post_alerts_responses.go @@ -54,7 +54,7 @@ func (o *PostAlertsReader) ReadResponse(response runtime.ClientResponse, consume } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[POST /alerts] postAlerts", response, response.Code()) } } @@ -96,6 +96,11 @@ func (o *PostAlertsOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the post alerts o k response +func (o *PostAlertsOK) Code() int { + return 200 +} + func (o *PostAlertsOK) Error() string { return fmt.Sprintf("[POST /alerts][%d] postAlertsOK ", 200) } @@ -148,6 +153,11 @@ func (o *PostAlertsBadRequest) IsCode(code int) bool { return code == 400 } +// Code gets the status code for the post alerts bad request response +func (o *PostAlertsBadRequest) Code() int { + return 400 +} + func (o *PostAlertsBadRequest) Error() string { return fmt.Sprintf("[POST /alerts][%d] postAlertsBadRequest %+v", 400, o.Payload) } @@ -209,6 +219,11 @@ func (o *PostAlertsInternalServerError) IsCode(code int) bool { return code == 500 } +// Code gets the status code for the post alerts internal server error response +func (o *PostAlertsInternalServerError) Code() int { + return 500 +} + func (o *PostAlertsInternalServerError) Error() string { return fmt.Sprintf("[POST /alerts][%d] postAlertsInternalServerError %+v", 500, o.Payload) } diff --git a/api/v2/client/alertgroup/get_alert_groups_responses.go b/api/v2/client/alertgroup/get_alert_groups_responses.go index f348da944e..2cd5a18eb7 100644 --- a/api/v2/client/alertgroup/get_alert_groups_responses.go +++ b/api/v2/client/alertgroup/get_alert_groups_responses.go @@ -56,7 +56,7 @@ func (o *GetAlertGroupsReader) ReadResponse(response runtime.ClientResponse, con } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[GET /alerts/groups] getAlertGroups", response, response.Code()) } } @@ -99,6 +99,11 @@ func (o *GetAlertGroupsOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the get alert groups o k response +func (o *GetAlertGroupsOK) Code() int { + return 200 +} + func (o *GetAlertGroupsOK) Error() string { return fmt.Sprintf("[GET /alerts/groups][%d] getAlertGroupsOK %+v", 200, o.Payload) } @@ -160,6 +165,11 @@ func (o *GetAlertGroupsBadRequest) IsCode(code int) bool { return code == 400 } +// Code gets the status code for the get alert groups bad request response +func (o *GetAlertGroupsBadRequest) Code() int { + return 400 +} + func (o *GetAlertGroupsBadRequest) Error() string { return fmt.Sprintf("[GET /alerts/groups][%d] getAlertGroupsBadRequest %+v", 400, o.Payload) } @@ -221,6 +231,11 @@ func (o *GetAlertGroupsInternalServerError) IsCode(code int) bool { return code == 500 } +// Code gets the status code for the get alert groups internal server error response +func (o *GetAlertGroupsInternalServerError) Code() int { + return 500 +} + func (o *GetAlertGroupsInternalServerError) Error() string { return fmt.Sprintf("[GET /alerts/groups][%d] getAlertGroupsInternalServerError %+v", 500, o.Payload) } diff --git a/api/v2/client/general/get_status_responses.go b/api/v2/client/general/get_status_responses.go index ddd6b2a2d7..0cb6f36af3 100644 --- a/api/v2/client/general/get_status_responses.go +++ b/api/v2/client/general/get_status_responses.go @@ -44,7 +44,7 @@ func (o *GetStatusReader) ReadResponse(response runtime.ClientResponse, consumer } return result, nil default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[GET /status] getStatus", response, response.Code()) } } @@ -87,6 +87,11 @@ func (o *GetStatusOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the get status o k response +func (o *GetStatusOK) Code() int { + return 200 +} + func (o *GetStatusOK) Error() string { return fmt.Sprintf("[GET /status][%d] getStatusOK %+v", 200, o.Payload) } diff --git a/api/v2/client/receiver/get_receivers_responses.go b/api/v2/client/receiver/get_receivers_responses.go index c8fe599913..ef97632773 100644 --- a/api/v2/client/receiver/get_receivers_responses.go +++ b/api/v2/client/receiver/get_receivers_responses.go @@ -44,7 +44,7 @@ func (o *GetReceiversReader) ReadResponse(response runtime.ClientResponse, consu } return result, nil default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[GET /receivers] getReceivers", response, response.Code()) } } @@ -87,6 +87,11 @@ func (o *GetReceiversOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the get receivers o k response +func (o *GetReceiversOK) Code() int { + return 200 +} + func (o *GetReceiversOK) Error() string { return fmt.Sprintf("[GET /receivers][%d] getReceiversOK %+v", 200, o.Payload) } diff --git a/api/v2/client/silence/delete_silence_responses.go b/api/v2/client/silence/delete_silence_responses.go index c8b2bba1c7..40c87fc9bf 100644 --- a/api/v2/client/silence/delete_silence_responses.go +++ b/api/v2/client/silence/delete_silence_responses.go @@ -54,7 +54,7 @@ func (o *DeleteSilenceReader) ReadResponse(response runtime.ClientResponse, cons } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[DELETE /silence/{silenceID}] deleteSilence", response, response.Code()) } } @@ -96,6 +96,11 @@ func (o *DeleteSilenceOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the delete silence o k response +func (o *DeleteSilenceOK) Code() int { + return 200 +} + func (o *DeleteSilenceOK) Error() string { return fmt.Sprintf("[DELETE /silence/{silenceID}][%d] deleteSilenceOK ", 200) } @@ -147,6 +152,11 @@ func (o *DeleteSilenceNotFound) IsCode(code int) bool { return code == 404 } +// Code gets the status code for the delete silence not found response +func (o *DeleteSilenceNotFound) Code() int { + return 404 +} + func (o *DeleteSilenceNotFound) Error() string { return fmt.Sprintf("[DELETE /silence/{silenceID}][%d] deleteSilenceNotFound ", 404) } @@ -199,6 +209,11 @@ func (o *DeleteSilenceInternalServerError) IsCode(code int) bool { return code == 500 } +// Code gets the status code for the delete silence internal server error response +func (o *DeleteSilenceInternalServerError) Code() int { + return 500 +} + func (o *DeleteSilenceInternalServerError) Error() string { return fmt.Sprintf("[DELETE /silence/{silenceID}][%d] deleteSilenceInternalServerError %+v", 500, o.Payload) } diff --git a/api/v2/client/silence/get_silence_responses.go b/api/v2/client/silence/get_silence_responses.go index 6cfdb73bf2..b11ded0429 100644 --- a/api/v2/client/silence/get_silence_responses.go +++ b/api/v2/client/silence/get_silence_responses.go @@ -56,7 +56,7 @@ func (o *GetSilenceReader) ReadResponse(response runtime.ClientResponse, consume } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[GET /silence/{silenceID}] getSilence", response, response.Code()) } } @@ -99,6 +99,11 @@ func (o *GetSilenceOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the get silence o k response +func (o *GetSilenceOK) Code() int { + return 200 +} + func (o *GetSilenceOK) Error() string { return fmt.Sprintf("[GET /silence/{silenceID}][%d] getSilenceOK %+v", 200, o.Payload) } @@ -161,6 +166,11 @@ func (o *GetSilenceNotFound) IsCode(code int) bool { return code == 404 } +// Code gets the status code for the get silence not found response +func (o *GetSilenceNotFound) Code() int { + return 404 +} + func (o *GetSilenceNotFound) Error() string { return fmt.Sprintf("[GET /silence/{silenceID}][%d] getSilenceNotFound ", 404) } @@ -213,6 +223,11 @@ func (o *GetSilenceInternalServerError) IsCode(code int) bool { return code == 500 } +// Code gets the status code for the get silence internal server error response +func (o *GetSilenceInternalServerError) Code() int { + return 500 +} + func (o *GetSilenceInternalServerError) Error() string { return fmt.Sprintf("[GET /silence/{silenceID}][%d] getSilenceInternalServerError %+v", 500, o.Payload) } diff --git a/api/v2/client/silence/get_silences_responses.go b/api/v2/client/silence/get_silences_responses.go index 6578dd399e..2de3d80cb4 100644 --- a/api/v2/client/silence/get_silences_responses.go +++ b/api/v2/client/silence/get_silences_responses.go @@ -50,7 +50,7 @@ func (o *GetSilencesReader) ReadResponse(response runtime.ClientResponse, consum } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[GET /silences] getSilences", response, response.Code()) } } @@ -93,6 +93,11 @@ func (o *GetSilencesOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the get silences o k response +func (o *GetSilencesOK) Code() int { + return 200 +} + func (o *GetSilencesOK) Error() string { return fmt.Sprintf("[GET /silences][%d] getSilencesOK %+v", 200, o.Payload) } @@ -154,6 +159,11 @@ func (o *GetSilencesInternalServerError) IsCode(code int) bool { return code == 500 } +// Code gets the status code for the get silences internal server error response +func (o *GetSilencesInternalServerError) Code() int { + return 500 +} + func (o *GetSilencesInternalServerError) Error() string { return fmt.Sprintf("[GET /silences][%d] getSilencesInternalServerError %+v", 500, o.Payload) } diff --git a/api/v2/client/silence/post_silences_responses.go b/api/v2/client/silence/post_silences_responses.go index 9cf1f2e500..7d14642978 100644 --- a/api/v2/client/silence/post_silences_responses.go +++ b/api/v2/client/silence/post_silences_responses.go @@ -56,7 +56,7 @@ func (o *PostSilencesReader) ReadResponse(response runtime.ClientResponse, consu } return nil, result default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + return nil, runtime.NewAPIError("[POST /silences] postSilences", response, response.Code()) } } @@ -99,6 +99,11 @@ func (o *PostSilencesOK) IsCode(code int) bool { return code == 200 } +// Code gets the status code for the post silences o k response +func (o *PostSilencesOK) Code() int { + return 200 +} + func (o *PostSilencesOK) Error() string { return fmt.Sprintf("[POST /silences][%d] postSilencesOK %+v", 200, o.Payload) } @@ -162,6 +167,11 @@ func (o *PostSilencesBadRequest) IsCode(code int) bool { return code == 400 } +// Code gets the status code for the post silences bad request response +func (o *PostSilencesBadRequest) Code() int { + return 400 +} + func (o *PostSilencesBadRequest) Error() string { return fmt.Sprintf("[POST /silences][%d] postSilencesBadRequest %+v", 400, o.Payload) } @@ -223,6 +233,11 @@ func (o *PostSilencesNotFound) IsCode(code int) bool { return code == 404 } +// Code gets the status code for the post silences not found response +func (o *PostSilencesNotFound) Code() int { + return 404 +} + func (o *PostSilencesNotFound) Error() string { return fmt.Sprintf("[POST /silences][%d] postSilencesNotFound %+v", 404, o.Payload) } diff --git a/api/v2/models/alert_group.go b/api/v2/models/alert_group.go index c943e68330..fbb5e283b1 100644 --- a/api/v2/models/alert_group.go +++ b/api/v2/models/alert_group.go @@ -163,6 +163,11 @@ func (m *AlertGroup) contextValidateAlerts(ctx context.Context, formats strfmt.R for i := 0; i < len(m.Alerts); i++ { if m.Alerts[i] != nil { + + if swag.IsZero(m.Alerts[i]) { // not required + return nil + } + if err := m.Alerts[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("alerts" + "." + strconv.Itoa(i)) @@ -195,6 +200,7 @@ func (m *AlertGroup) contextValidateLabels(ctx context.Context, formats strfmt.R func (m *AlertGroup) contextValidateReceiver(ctx context.Context, formats strfmt.Registry) error { if m.Receiver != nil { + if err := m.Receiver.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("receiver") diff --git a/api/v2/models/alert_groups.go b/api/v2/models/alert_groups.go index 31ccb2172b..338b22127a 100644 --- a/api/v2/models/alert_groups.go +++ b/api/v2/models/alert_groups.go @@ -68,6 +68,11 @@ func (m AlertGroups) ContextValidate(ctx context.Context, formats strfmt.Registr for i := 0; i < len(m); i++ { if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + if err := m[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) diff --git a/api/v2/models/alertmanager_status.go b/api/v2/models/alertmanager_status.go index 0d5370edfb..2ab11ec461 100644 --- a/api/v2/models/alertmanager_status.go +++ b/api/v2/models/alertmanager_status.go @@ -175,6 +175,7 @@ func (m *AlertmanagerStatus) ContextValidate(ctx context.Context, formats strfmt func (m *AlertmanagerStatus) contextValidateCluster(ctx context.Context, formats strfmt.Registry) error { if m.Cluster != nil { + if err := m.Cluster.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("cluster") @@ -191,6 +192,7 @@ func (m *AlertmanagerStatus) contextValidateCluster(ctx context.Context, formats func (m *AlertmanagerStatus) contextValidateConfig(ctx context.Context, formats strfmt.Registry) error { if m.Config != nil { + if err := m.Config.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("config") @@ -207,6 +209,7 @@ func (m *AlertmanagerStatus) contextValidateConfig(ctx context.Context, formats func (m *AlertmanagerStatus) contextValidateVersionInfo(ctx context.Context, formats strfmt.Registry) error { if m.VersionInfo != nil { + if err := m.VersionInfo.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("versionInfo") diff --git a/api/v2/models/cluster_status.go b/api/v2/models/cluster_status.go index 0078320f15..f470bc010f 100644 --- a/api/v2/models/cluster_status.go +++ b/api/v2/models/cluster_status.go @@ -156,6 +156,11 @@ func (m *ClusterStatus) contextValidatePeers(ctx context.Context, formats strfmt for i := 0; i < len(m.Peers); i++ { if m.Peers[i] != nil { + + if swag.IsZero(m.Peers[i]) { // not required + return nil + } + if err := m.Peers[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("peers" + "." + strconv.Itoa(i)) diff --git a/api/v2/models/gettable_alert.go b/api/v2/models/gettable_alert.go index f7db3321c1..195bb53764 100644 --- a/api/v2/models/gettable_alert.go +++ b/api/v2/models/gettable_alert.go @@ -366,6 +366,11 @@ func (m *GettableAlert) contextValidateReceivers(ctx context.Context, formats st for i := 0; i < len(m.Receivers); i++ { if m.Receivers[i] != nil { + + if swag.IsZero(m.Receivers[i]) { // not required + return nil + } + if err := m.Receivers[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("receivers" + "." + strconv.Itoa(i)) @@ -384,6 +389,7 @@ func (m *GettableAlert) contextValidateReceivers(ctx context.Context, formats st func (m *GettableAlert) contextValidateStatus(ctx context.Context, formats strfmt.Registry) error { if m.Status != nil { + if err := m.Status.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("status") diff --git a/api/v2/models/gettable_alerts.go b/api/v2/models/gettable_alerts.go index 4efe8cd5ec..db78dcc471 100644 --- a/api/v2/models/gettable_alerts.go +++ b/api/v2/models/gettable_alerts.go @@ -68,6 +68,11 @@ func (m GettableAlerts) ContextValidate(ctx context.Context, formats strfmt.Regi for i := 0; i < len(m); i++ { if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + if err := m[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) diff --git a/api/v2/models/gettable_silence.go b/api/v2/models/gettable_silence.go index fe9d178d7f..9d60f6cad0 100644 --- a/api/v2/models/gettable_silence.go +++ b/api/v2/models/gettable_silence.go @@ -202,6 +202,7 @@ func (m *GettableSilence) ContextValidate(ctx context.Context, formats strfmt.Re func (m *GettableSilence) contextValidateStatus(ctx context.Context, formats strfmt.Registry) error { if m.Status != nil { + if err := m.Status.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("status") diff --git a/api/v2/models/gettable_silences.go b/api/v2/models/gettable_silences.go index cda5ef6497..fed9d0b886 100644 --- a/api/v2/models/gettable_silences.go +++ b/api/v2/models/gettable_silences.go @@ -68,6 +68,11 @@ func (m GettableSilences) ContextValidate(ctx context.Context, formats strfmt.Re for i := 0; i < len(m); i++ { if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + if err := m[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) diff --git a/api/v2/models/matchers.go b/api/v2/models/matchers.go index 4e2061872e..fbff9875eb 100644 --- a/api/v2/models/matchers.go +++ b/api/v2/models/matchers.go @@ -75,6 +75,11 @@ func (m Matchers) ContextValidate(ctx context.Context, formats strfmt.Registry) for i := 0; i < len(m); i++ { if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + if err := m[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) diff --git a/api/v2/models/postable_alert.go b/api/v2/models/postable_alert.go index dcec7f0a19..105b8b30cd 100644 --- a/api/v2/models/postable_alert.go +++ b/api/v2/models/postable_alert.go @@ -203,6 +203,10 @@ func (m *PostableAlert) ContextValidate(ctx context.Context, formats strfmt.Regi func (m *PostableAlert) contextValidateAnnotations(ctx context.Context, formats strfmt.Registry) error { + if swag.IsZero(m.Annotations) { // not required + return nil + } + if err := m.Annotations.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("annotations") diff --git a/api/v2/models/postable_alerts.go b/api/v2/models/postable_alerts.go index ed4d7fb9ba..3df968820d 100644 --- a/api/v2/models/postable_alerts.go +++ b/api/v2/models/postable_alerts.go @@ -68,6 +68,11 @@ func (m PostableAlerts) ContextValidate(ctx context.Context, formats strfmt.Regi for i := 0; i < len(m); i++ { if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + if err := m[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) diff --git a/api/v2/restapi/operations/alertmanager_api.go b/api/v2/restapi/operations/alertmanager_api.go index 8cbd9a6efb..e28c76b32e 100644 --- a/api/v2/restapi/operations/alertmanager_api.go +++ b/api/v2/restapi/operations/alertmanager_api.go @@ -412,6 +412,6 @@ func (o *AlertmanagerAPI) AddMiddlewareFor(method, path string, builder middlewa } o.Init() if h, ok := o.handlers[um][path]; ok { - o.handlers[method][path] = builder(h) + o.handlers[um][path] = builder(h) } } From d2501323d266b59d4658576be5695b900c93a005 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Tue, 24 Oct 2023 18:00:38 +0100 Subject: [PATCH 19/20] Add debug logs for muted alerts (#3558) This commit adds debug logs to MuteStage that logs when an alert is muted. This can help operators root cause missing notifications when alerts are silenced by mistake or purpose but then forgotten about. Signed-off-by: George Robinson --- notify/notify.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/notify/notify.go b/notify/notify.go index 891f3c23ee..33d499af30 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -518,16 +518,24 @@ func NewMuteStage(m types.Muter) *MuteStage { } // Exec implements the Stage interface. -func (n *MuteStage) Exec(ctx context.Context, _ log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { - var filtered []*types.Alert +func (n *MuteStage) Exec(ctx context.Context, logger log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { + var ( + filtered []*types.Alert + muted []*types.Alert + ) for _, a := range alerts { // TODO(fabxc): increment total alerts counter. // Do not send the alert if muted. - if !n.muter.Mutes(a.Labels) { + if n.muter.Mutes(a.Labels) { + muted = append(muted, a) + } else { filtered = append(filtered, a) } // TODO(fabxc): increment muted alerts counter if muted. } + if len(muted) > 0 { + level.Debug(logger).Log("msg", "Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted)) + } return ctx, filtered, nil } From 8512285e54f33199a454f0ccfa29a096c9f0072a Mon Sep 17 00:00:00 2001 From: George Robinson Date: Wed, 25 Oct 2023 09:52:17 +0100 Subject: [PATCH 20/20] Support UTF-8 label matchers: Update compliance tests (#3569) * Update compliance tests This commit updates compliance tests to include openmetrics escape sequences that are not valid in the UTF-8 matchers parser. Signed-off-by: George Robinson * Add tests for openmetrics escape sequences Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- matchers/compliance/compliance_test.go | 64 +++++++++++++++++++++++++- pkg/labels/parse_test.go | 24 ++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/matchers/compliance/compliance_test.go b/matchers/compliance/compliance_test.go index 9178e058b2..705c4c4315 100644 --- a/matchers/compliance/compliance_test.go +++ b/matchers/compliance/compliance_test.go @@ -52,7 +52,58 @@ func TestCompliance(t *testing.T) { skip: true, }, { - input: "{foo=\\\"}", + input: `{foo=\n}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\n") + return append(ms, m) + }(), + skip: true, + }, + { + input: `{foo=bar\n}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\n") + return append(ms, m) + }(), + skip: true, + }, + { + input: `{foo=\t}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\\t") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\t}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\\t") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\\") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\\}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\\") + return append(ms, m) + }(), + skip: true, + }, + { + input: `{foo=\"}`, want: func() labels.Matchers { ms := labels.Matchers{} m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\"") @@ -60,6 +111,15 @@ func TestCompliance(t *testing.T) { }(), skip: true, }, + { + input: `{foo=bar\"}`, + want: func() labels.Matchers { + ms := labels.Matchers{} + m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\"") + return append(ms, m) + }(), + skip: true, + }, { input: `{foo=bar}`, want: func() labels.Matchers { @@ -386,7 +446,7 @@ func TestCompliance(t *testing.T) { t.Fatalf("expected error but got none: %v", tc.err) } if !reflect.DeepEqual(got, tc.want) { - t.Fatalf("labels not equal:\ngot %#v\nwant %#v", got, tc.want) + t.Fatalf("matchers not equal:\ngot %s\nwant %s", got, tc.want) } }) } diff --git a/pkg/labels/parse_test.go b/pkg/labels/parse_test.go index b4b1d631d4..dd1731c9b7 100644 --- a/pkg/labels/parse_test.go +++ b/pkg/labels/parse_test.go @@ -298,6 +298,30 @@ func TestMatchers(t *testing.T) { return append(ms, m) }(), }, + { + input: `{foo=bar\}`, + want: func() []*Matcher { + ms := []*Matcher{} + m, _ := NewMatcher(MatchEqual, "foo", "bar\\") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\\}`, + want: func() []*Matcher { + ms := []*Matcher{} + m, _ := NewMatcher(MatchEqual, "foo", "bar\\") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\"}`, + want: func() []*Matcher { + ms := []*Matcher{} + m, _ := NewMatcher(MatchEqual, "foo", "bar\"") + return append(ms, m) + }(), + }, { input: `job=`, want: func() []*Matcher {