From 5a462df83ab1d22ed0e5b806756be83dce64bb13 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 21 Sep 2023 13:57:11 +0100 Subject: [PATCH] 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 f3c20984..74fd1447 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 c047cdb4..bc6af585 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)) }