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 <george.robinson@grafana.com>
---------

Signed-off-by: George Robinson <george.robinson@grafana.com>
This commit is contained in:
George Robinson 2023-09-21 13:57:11 +01:00 committed by GitHub
parent 5ba9b9c7c3
commit 5a462df83a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 18 deletions

View File

@ -457,27 +457,46 @@ func Version() (string, error) {
// AddAlertsAt declares alerts that are to be added to the Alertmanager // AddAlertsAt declares alerts that are to be added to the Alertmanager
// server at a relative point in time. // 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.t.Do(at, func() {
am.AddAlerts(alerts...) am.AddAlerts(omitEquals, alerts...)
}) })
} }
// AddAlerts declares alerts that are to be added to the Alertmanager server. // 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 { for _, alert := range alerts {
out, err := am.addAlertCommand(alert) out, err := am.addAlertCommand(omitEquals, alert)
if err != nil { if err != nil {
am.t.Errorf("Error adding alert: %v\nOutput: %s", err, string(out)) 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("/") amURLFlag := "--alertmanager.url=" + am.getURL("/")
args := []string{amURLFlag, "alert", "add"} args := []string{amURLFlag, "alert", "add"}
for key, val := range alert.labels { // Make a copy of the labels
args = append(args, key+"="+val) 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)) startsAt := strfmt.DateTime(am.opts.expandTime(alert.startsAt))
args = append(args, "--start="+startsAt.String()) args = append(args, "--start="+startsAt.String())
@ -522,7 +541,7 @@ func parseAlertQueryResponse(data []byte) ([]TestAlert, error) {
} }
summary := strings.TrimSpace(line[summPos:]) summary := strings.TrimSpace(line[summPos:])
alert := TestAlert{ alert := TestAlert{
labels: models.LabelSet{"name": alertName}, labels: models.LabelSet{"alertname": alertName},
startsAt: float64(startsAt.Unix()), startsAt: float64(startsAt.Unix()),
summary: summary, summary: summary,
} }
@ -670,13 +689,13 @@ func (am *Alertmanager) showRouteCommand() ([]byte, error) {
return cmd.CombinedOutput() return cmd.CombinedOutput()
} }
func (am *Alertmanager) TestRoute() ([]byte, error) { func (am *Alertmanager) TestRoute(labels ...string) ([]byte, error) {
return am.testRouteCommand() return am.testRouteCommand(labels...)
} }
func (am *Alertmanager) testRouteCommand() ([]byte, error) { func (am *Alertmanager) testRouteCommand(labels ...string) ([]byte, error) {
amURLFlag := "--alertmanager.url=" + am.getURL("/") amURLFlag := "--alertmanager.url=" + am.getURL("/")
args := []string{amURLFlag, "config", "routes", "test"} args := append([]string{amURLFlag, "config", "routes", "test"}, labels...)
cmd := exec.Command(amtool, args...) cmd := exec.Command(amtool, args...)
return cmd.CombinedOutput() return cmd.CombinedOutput()
} }

View File

@ -72,7 +72,7 @@ receivers:
am := amc.Members()[0] am := amc.Members()[0]
alert1 := Alert("alertname", "test1").Active(1, 2) 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)) co.Want(Between(1, 2), Alert("alertname", "test1").Active(1))
at.Run() at.Run()
@ -111,12 +111,13 @@ receivers:
am := amc.Members()[0] am := amc.Members()[0]
alert1 := Alert("alertname", "test1", "severity", "warning").Active(1) alert1 := Alert("alertname", "test1", "severity", "warning").Active(1)
alert2 := Alert("alertname", "test2", "severity", "info").Active(1) alert2 := Alert("alertname", "alertname=test2", "severity", "info").Active(1)
am.AddAlerts(alert1, alert2) alert3 := Alert("alertname", "{alertname=test3}", "severity", "info").Active(1)
am.AddAlerts(true, alert1, alert2, alert3)
alerts, err := am.QueryAlerts() alerts, err := am.QueryAlerts()
require.NoError(t, err) require.NoError(t, err)
require.Len(t, alerts, 2) require.Len(t, alerts, 3)
// Get the first alert using the alertname heuristic // Get the first alert using the alertname heuristic
alerts, err = am.QueryAlerts("test1") alerts, err = am.QueryAlerts("test1")
@ -126,14 +127,21 @@ receivers:
// QueryAlerts uses the simple output option, which means just the alertname // 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 // label is printed. We can assert that querying works as expected as we know
// there are two alerts called "test1" and "test2". // 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)) require.True(t, alerts[0].HasLabels(expectedLabels))
// Get the second alert // Get the second alert
alerts, err = am.QueryAlerts("alertname=test2") alerts, err = am.QueryAlerts("alertname=test2")
require.NoError(t, err) require.NoError(t, err)
require.Len(t, alerts, 1) 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)) require.True(t, alerts[0].HasLabels(expectedLabels))
} }
@ -257,4 +265,9 @@ receivers:
am := amc.Members()[0] am := amc.Members()[0]
_, err := am.TestRoute() _, err := am.TestRoute()
require.NoError(t, err) 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))
} }