The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/tests/api/alerting/api_alertmanager_test.go

689 lines
20 KiB

package alerting
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"regexp"
"testing"
"time"
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
"github.com/go-openapi/strfmt"
amv2 "github.com/prometheus/alertmanager/api/v2/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/errutil"
"github.com/grafana/grafana/pkg/services/featuremgmt"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/tests/testinfra"
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
"github.com/grafana/grafana/pkg/util"
)
func TestIntegrationAMConfigAccess(t *testing.T) {
testinfra.SQLiteIntegrationTest(t)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
DisableAnonymous: true,
AppModeProduction: true,
DisableFeatureToggles: []string{
featuremgmt.FlagAlertingApiServer,
},
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
// Create a users to make authenticated requests
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleViewer),
Password: "viewer",
Login: "viewer",
})
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Password: "editor",
Login: "editor",
})
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleAdmin),
Password: "admin",
Login: "admin",
})
type testCase struct {
desc string
url string
expStatus int
expBody string
}
t.Run("when creating alertmanager configuration", func(t *testing.T) {
body := `
{
"alertmanager_config": {
"route": {
"receiver": "grafana-default-email"
},
"receivers": [{
"name": "grafana-default-email",
"grafana_managed_receiver_configs": [{
"uid": "",
"name": "email receiver",
"type": "email",
"isDefault": true,
"settings": {
"addresses": "<example@email.com>"
}
}]
}]
}
}
`
testCases := []testCase{
{
desc: "un-authenticated request should fail",
url: "http://%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusUnauthorized,
expBody: `"message":"Unauthorized"`,
},
{
desc: "viewer request should fail",
url: "http://viewer:viewer@%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusForbidden,
expBody: `"title":"Access denied"`,
},
{
desc: "editor request should succeed",
url: "http://editor:editor@%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusAccepted,
expBody: `{"message":"configuration created"}`,
},
{
desc: "admin request should succeed",
url: "http://admin:admin@%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusAccepted,
expBody: `{"message":"configuration created"}`,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
url := fmt.Sprintf(tc.url, grafanaListedAddr)
buf := bytes.NewReader([]byte(body))
// nolint:gosec
resp, err := http.Post(url, "application/json", buf)
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.NoError(t, err)
require.Equal(t, tc.expStatus, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(b), tc.expBody)
})
}
})
t.Run("when retrieve alertmanager configuration", func(t *testing.T) {
cfgTemplate := `
{
"template_files": null,
"alertmanager_config": {
"route": %s,
"receivers": [{
"name": "grafana-default-email",
"grafana_managed_receiver_configs": [{
"disableResolveMessage": false,
"uid": "",
"name": "email receiver",
"type": "email",
"secureFields": {},
"settings": {
"addresses": "<example@email.com>"
}
}]
}]
}
}
`
cfgWithoutAutogen := fmt.Sprintf(cfgTemplate, `{
"receiver": "grafana-default-email"
}`)
cfgWithAutogen := fmt.Sprintf(cfgTemplate, `{
"receiver": "grafana-default-email",
"routes": [{
"receiver": "grafana-default-email",
"object_matchers": [["__grafana_autogenerated__", "=", "true"]],
"routes": [{
"receiver": "grafana-default-email",
"group_by": ["grafana_folder", "alertname"],
"object_matchers": [["__grafana_receiver__", "=", "grafana-default-email"]]
}]
}]
}`)
testCases := []testCase{
{
desc: "un-authenticated request should fail",
url: "http://%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusUnauthorized,
expBody: `{"extra":null,"message":"Unauthorized","messageId":"auth.unauthorized","statusCode":401,"traceID":""}`,
},
{
desc: "viewer request should succeed",
url: "http://viewer:viewer@%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusOK,
expBody: cfgWithoutAutogen,
},
{
desc: "editor request should succeed",
url: "http://editor:editor@%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusOK,
expBody: cfgWithoutAutogen,
},
{
desc: "admin request should succeed",
url: "http://admin:admin@%s/api/alertmanager/grafana/config/api/v1/alerts",
expStatus: http.StatusOK,
expBody: cfgWithAutogen,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
resp, err := http.Get(fmt.Sprintf(tc.url, grafanaListedAddr))
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.NoError(t, err)
require.Equal(t, tc.expStatus, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
if tc.expStatus == http.StatusOK {
re := regexp.MustCompile(`"uid":"([\w|-]+)"`)
b = re.ReplaceAll(b, []byte(`"uid":""`))
}
require.NoError(t, err)
require.JSONEq(t, tc.expBody, string(b))
})
}
})
t.Run("when creating silence", func(t *testing.T) {
now := time.Now()
body := fmt.Sprintf(`
{
"comment": "string",
"createdBy": "string",
"matchers": [
{
"isRegex": true,
"name": "string",
"value": "string"
}
],
"startsAt": "%s",
"endsAt": "%s"
}
`, now.Format(time.RFC3339), now.Add(10*time.Second).Format(time.RFC3339))
testCases := []testCase{
{
desc: "un-authenticated request should fail",
url: "http://%s/api/alertmanager/grafana/config/api/v2/silences",
expStatus: http.StatusUnauthorized,
expBody: `"message":"Unauthorized"`,
},
{
desc: "viewer request should fail",
url: "http://viewer:viewer@%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusForbidden,
expBody: `"title":"Access denied"`,
},
{
desc: "editor request should succeed",
url: "http://editor:editor@%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusAccepted,
},
{
desc: "admin request should succeed",
url: "http://admin:admin@%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusAccepted,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
url := fmt.Sprintf(tc.url, grafanaListedAddr)
buf := bytes.NewReader([]byte(body))
// nolint:gosec
resp, err := http.Post(url, "application/json", buf)
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.NoError(t, err)
require.Equal(t, tc.expStatus, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
if tc.expStatus == http.StatusAccepted {
response := apimodels.PostSilencesOKBody{}
require.NoError(t, json.Unmarshal(b, &response))
require.NotEmpty(t, response.SilenceID)
return
}
require.Contains(t, string(b), tc.expBody)
})
}
})
var blob []byte
t.Run("when getting silences", func(t *testing.T) {
testCases := []testCase{
{
desc: "un-authenticated request should fail",
url: "http://%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusUnauthorized,
expBody: `"message": "Unauthorized"`,
},
{
desc: "viewer request should succeed",
url: "http://viewer:viewer@%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusOK,
},
{
desc: "editor request should succeed",
url: "http://editor:editor@%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusOK,
},
{
desc: "admin request should succeed",
url: "http://admin:admin@%s/api/alertmanager/grafana/api/v2/silences",
expStatus: http.StatusOK,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
url := fmt.Sprintf(tc.url, grafanaListedAddr)
// nolint:gosec
resp, err := http.Get(url)
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.NoError(t, err)
require.Equal(t, tc.expStatus, resp.StatusCode)
require.NoError(t, err)
if tc.expStatus == http.StatusOK {
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
blob = b
}
})
}
})
var silences apimodels.GettableSilences
err := json.Unmarshal(blob, &silences)
require.NoError(t, err)
assert.Len(t, silences, 2)
silenceIDs := make([]string, 0, len(silences))
for _, s := range silences {
silenceIDs = append(silenceIDs, *s.ID)
}
unconsumedSilenceIdx := 0
t.Run("when deleting a silence", func(t *testing.T) {
testCases := []testCase{
{
desc: "un-authenticated request should fail",
url: "http://%s/api/alertmanager/grafana/api/v2/silence/%s",
expStatus: http.StatusUnauthorized,
expBody: `"message":"Unauthorized"`,
},
{
desc: "viewer request should fail",
url: "http://viewer:viewer@%s/api/alertmanager/grafana/api/v2/silence/%s",
expStatus: http.StatusForbidden,
expBody: `"title":"Access denied"`,
},
{
desc: "editor request should succeed",
url: "http://editor:editor@%s/api/alertmanager/grafana/api/v2/silence/%s",
expStatus: http.StatusOK,
expBody: `{"message":"silence deleted"}`,
},
{
desc: "admin request should succeed",
url: "http://admin:admin@%s/api/alertmanager/grafana/api/v2/silence/%s",
expStatus: http.StatusOK,
expBody: `{"message":"silence deleted"}`,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
url := fmt.Sprintf(tc.url, grafanaListedAddr, silenceIDs[unconsumedSilenceIdx])
// Create client
client := &http.Client{}
// Create request
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
fmt.Println(err)
return
}
// Fetch Request
resp, err := client.Do(req)
if err != nil {
return
}
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.NoError(t, err)
require.Equal(t, tc.expStatus, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
if tc.expStatus == http.StatusOK {
unconsumedSilenceIdx++
}
require.Contains(t, string(b), tc.expBody)
})
}
})
}
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
func TestIntegrationAlertmanagerCreateSilence(t *testing.T) {
testinfra.SQLiteIntegrationTest(t)
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
DefaultOrgRole: string(org.RoleAdmin),
Password: "admin",
Login: "admin",
})
client := newAlertingApiClient(grafanaListedAddr, "admin", "admin")
cases := []struct {
name string
silence apimodels.PostableSilence
expErr string
}{{
name: "can create silence for foo=bar",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer("foo"),
Value: util.Pointer("bar"),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
}, {
name: "can create silence for _foo1=bar",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer("_foo1"),
Value: util.Pointer("bar"),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
}, {
name: "can create silence for 0foo=bar",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer("0foo"),
Value: util.Pointer("bar"),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
}, {
name: "can create silence for foo=🙂bar",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer("foo"),
Value: util.Pointer("🙂bar"),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
}, {
name: "can create silence for foo🙂=bar",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer("foo🙂"),
Value: util.Pointer("bar"),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
}, {
name: "can't create silence for missing label name",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer(""),
Value: util.Pointer("bar"),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
expErr: "unable to upsert silence: invalid silence: invalid label matcher 0: invalid label name \"\": unable to create silence",
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
}, {
name: "can't create silence for missing label value",
silence: apimodels.PostableSilence{
Silence: amv2.Silence{
Comment: util.Pointer("This is a comment"),
CreatedBy: util.Pointer("test"),
EndsAt: util.Pointer(strfmt.DateTime(time.Now().Add(time.Minute))),
Matchers: amv2.Matchers{{
IsEqual: util.Pointer(true),
IsRegex: util.Pointer(false),
Name: util.Pointer("foo"),
Value: util.Pointer(""),
}},
StartsAt: util.Pointer(strfmt.DateTime(time.Now())),
},
},
expErr: "unable to upsert silence: invalid silence: at least one matcher must not match the empty string: unable to create silence",
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
}}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
silenceOkBody, status, body := client.PostSilence(t, tc.silence)
t.Log(body)
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
if tc.expErr != "" {
assert.NotEqual(t, http.StatusAccepted, status)
var validationError errutil.PublicError
assert.NoError(t, json.Unmarshal([]byte(body), &validationError))
assert.Contains(t, validationError.Message, tc.expErr)
assert.Empty(t, silenceOkBody.SilenceID)
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
} else {
assert.Equal(t, http.StatusAccepted, status)
assert.NotEmpty(t, silenceOkBody.SilenceID)
Alerting: Support UTF-8 (#81512) This pull request updates our fork of Alertmanager to commit 65bdab0, which is based on commit 5658f8c in Prometheus Alertmanager. It applies the changes from grafana/alerting#155 which removes the overrides for validation of alerts, labels and silences that we had put in place to allow alerts and silences to work for non-Prometheus datasources. However, as this is now supported in Alertmanager with the UTF-8 work, we can use the new upstream functions and remove these overrides. The compat package is a package in Alertmanager that takes care of backwards compatibility when parsing matchers, validating alerts, labels and silences. It has three modes: classic mode, UTF-8 strict mode, fallback mode. These modes are controlled via compat.InitFromFlags. Grafana initializes the compat package without any feature flags, which is the equivalent of fallback mode. Classic and UTF-8 strict mode are used in Mimir. While Grafana Managed Alerts have no need for fallback mode, Grafana can still be used as an interface to manage the configurations of Mimir Alertmanagers and view configurations of Prometheus Alertmanager, and those installations might not have migrated or being running on older versions. Such installations behave as if in classic mode, and Grafana must be able to parse their configurations to interact with them for some period of time. As such, Grafana uses fallback mode until we are ready to drop support for outdated installations of Mimir and the Prometheus Alertmanager.
1 year ago
}
})
}
}
func TestIntegrationAlertmanagerStatus(t *testing.T) {
testinfra.SQLiteIntegrationTest(t)
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
DisableAnonymous: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
// Create a users to make authenticated requests
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleViewer),
Password: "viewer",
Login: "viewer",
})
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Password: "editor",
Login: "editor",
})
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleAdmin),
Password: "admin",
Login: "admin",
})
type testCase struct {
desc string
url string
expStatus int
expBody string
}
cfgTemplate := `
{
"cluster": {
"peers": [],
"status": "disabled"
},
"config": {
"route": %s,
"receivers": [{
"name": "grafana-default-email",
"grafana_managed_receiver_configs": [{
"uid": "",
"name": "email receiver",
"type": "email",
"disableResolveMessage": false,
"settings": {
"addresses": "\u003cexample@email.com\u003e"
}
}]
}]
},
"uptime": null,
"versionInfo": {
"branch": "N/A",
"buildDate": "N/A",
"buildUser": "N/A",
"goVersion": "N/A",
"revision": "N/A",
"version": "N/A"
}
}
`
cfgWithoutAutogen := fmt.Sprintf(cfgTemplate, `{
"receiver": "grafana-default-email",
"group_by": ["grafana_folder", "alertname"]
}`)
cfgWithAutogen := fmt.Sprintf(cfgTemplate, `{
"receiver": "grafana-default-email",
"routes": [{
"receiver": "grafana-default-email",
"object_matchers": [["__grafana_autogenerated__", "=", "true"]],
"routes": [{
"receiver": "grafana-default-email",
"group_by": ["grafana_folder", "alertname"],
"object_matchers": [["__grafana_receiver__", "=", "grafana-default-email"]]
}]
}],
"group_by": ["grafana_folder", "alertname"]
}`)
testCases := []testCase{
{
desc: "un-authenticated request should fail",
url: "http://%s/api/alertmanager/grafana/api/v2/status",
expStatus: http.StatusUnauthorized,
expBody: `{"extra":null,"message":"Unauthorized","messageId":"auth.unauthorized","statusCode":401,"traceID":""}`,
},
{
desc: "viewer request should succeed",
url: "http://viewer:viewer@%s/api/alertmanager/grafana/api/v2/status",
expStatus: http.StatusOK,
expBody: cfgWithoutAutogen,
},
{
desc: "editor request should succeed",
url: "http://editor:editor@%s/api/alertmanager/grafana/api/v2/status",
expStatus: http.StatusOK,
expBody: cfgWithoutAutogen,
},
{
desc: "admin request should succeed",
url: "http://admin:admin@%s/api/alertmanager/grafana/api/v2/status",
expStatus: http.StatusOK,
expBody: cfgWithAutogen,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
resp, err := http.Get(fmt.Sprintf(tc.url, grafanaListedAddr))
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.NoError(t, err)
require.Equal(t, tc.expStatus, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
if tc.expStatus == http.StatusOK {
re := regexp.MustCompile(`"uid":"([\w|-]+)"`)
b = re.ReplaceAll(b, []byte(`"uid":""`))
}
require.NoError(t, err)
require.JSONEq(t, tc.expBody, string(b))
})
}
}