package alerting import ( "bytes" "encoding/json" "fmt" "io" "net/http" "sort" "testing" "github.com/stretchr/testify/require" "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" ) func TestIntegrationProvisioning(t *testing.T) { testinfra.SQLiteIntegrationTest(t) dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ DisableLegacyAlerting: true, EnableUnifiedAlerting: true, DisableAnonymous: true, AppModeProduction: true, }) grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path) // Create a users to make authenticated requests createUser(t, store, user.CreateUserCommand{ DefaultOrgRole: string(org.RoleViewer), Password: "viewer", Login: "viewer", }) createUser(t, store, user.CreateUserCommand{ DefaultOrgRole: string(org.RoleEditor), Password: "editor", Login: "editor", }) createUser(t, store, user.CreateUserCommand{ DefaultOrgRole: string(org.RoleAdmin), Password: "admin", Login: "admin", }) apiClient := newAlertingApiClient(grafanaListedAddr, "editor", "editor") // Create the namespace we'll save our alerts to. namespaceUID := "default" apiClient.CreateFolder(t, namespaceUID, namespaceUID) t.Run("when provisioning notification policies", func(t *testing.T) { url := fmt.Sprintf("http://%s/api/v1/provisioning/policies", grafanaListedAddr) body := ` { "receiver": "test-receiver", "group_by": [ "..." ], "routes": [] }` // As we check if the receiver exists that is referenced in the policy, // we first need to create it, so the tests passes correctly. urlReceiver := fmt.Sprintf("http://%s/api/v1/provisioning/contact-points", grafanaListedAddr) bodyReceiver := ` { "name": "test-receiver", "type": "slack", "settings": { "recipient": "value_recipient", "token": "value_token" } }` req := createTestRequest("POST", urlReceiver, "admin", bodyReceiver) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 202, resp.StatusCode) t.Run("un-authenticated GET should 401", func(t *testing.T) { req := createTestRequest("GET", url, "", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 401, resp.StatusCode) }) t.Run("viewer GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "viewer", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("editor GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "editor", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("admin GET should succeed", func(t *testing.T) { req := createTestRequest("GET", url, "admin", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 200, resp.StatusCode) }) t.Run("un-authenticated PUT should 401", func(t *testing.T) { req := createTestRequest("PUT", url, "", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 401, resp.StatusCode) }) t.Run("viewer PUT should 403", func(t *testing.T) { req := createTestRequest("PUT", url, "viewer", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("editor PUT should 403", func(t *testing.T) { req := createTestRequest("PUT", url, "editor", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("admin PUT should succeed", func(t *testing.T) { req := createTestRequest("PUT", url, "admin", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 202, resp.StatusCode) }) }) t.Run("when provisioning contactpoints", func(t *testing.T) { url := fmt.Sprintf("http://%s/api/v1/provisioning/contact-points", grafanaListedAddr) body := ` { "name": "my-contact-point", "type": "slack", "settings": { "recipient": "value_recipient", "token": "value_token" } }` t.Run("un-authenticated GET should 401", func(t *testing.T) { req := createTestRequest("GET", url, "", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 401, resp.StatusCode) }) t.Run("viewer GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "viewer", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("editor GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "editor", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("admin GET should succeed", func(t *testing.T) { req := createTestRequest("GET", url, "admin", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 200, resp.StatusCode) }) t.Run("un-authenticated POST should 401", func(t *testing.T) { req := createTestRequest("POST", url, "", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 401, resp.StatusCode) }) t.Run("viewer POST should 403", func(t *testing.T) { req := createTestRequest("POST", url, "viewer", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("editor POST should 403", func(t *testing.T) { req := createTestRequest("POST", url, "editor", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("admin POST should succeed", func(t *testing.T) { req := createTestRequest("POST", url, "admin", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 202, resp.StatusCode) }) }) t.Run("when provisioning templates", func(t *testing.T) { url := fmt.Sprintf("http://%s/api/v1/provisioning/templates", grafanaListedAddr) t.Run("un-authenticated GET should 401", func(t *testing.T) { req := createTestRequest("GET", url, "", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 401, resp.StatusCode) }) t.Run("viewer GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "viewer", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("editor GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "editor", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("admin GET should succeed", func(t *testing.T) { req := createTestRequest("GET", url, "admin", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 200, resp.StatusCode) }) }) t.Run("when provisioning mute timings", func(t *testing.T) { url := fmt.Sprintf("http://%s/api/v1/provisioning/mute-timings", grafanaListedAddr) t.Run("un-authenticated GET should 401", func(t *testing.T) { req := createTestRequest("GET", url, "", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 401, resp.StatusCode) }) t.Run("viewer GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "viewer", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("editor GET should 403", func(t *testing.T) { req := createTestRequest("GET", url, "editor", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 403, resp.StatusCode) }) t.Run("admin GET should succeed", func(t *testing.T) { req := createTestRequest("GET", url, "admin", "") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 200, resp.StatusCode) }) }) t.Run("when provisioning alert rules", func(t *testing.T) { url := fmt.Sprintf("http://%s/api/v1/provisioning/alert-rules", grafanaListedAddr) body := `{"orgID":1,"folderUID":"default","ruleGroup":"Test Group","title":"Provisioned","condition":"A","data":[{"refId":"A","queryType":"","relativeTimeRange":{"from":600,"to":0},"datasourceUid":"f558c85f-66ad-4fd1-b31d-7979e6c93db4","model":{"editorMode":"code","exemplar":false,"expr":"sum(rate(low_card[5m])) \u003e 0","format":"time_series","instant":true,"intervalMs":1000,"legendFormat":"__auto","maxDataPoints":43200,"range":false,"refId":"A"}}],"noDataState":"NoData","execErrState":"Error","for":"0s"}` req := createTestRequest("POST", url, "admin", body) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) require.NoError(t, resp.Body.Close()) require.Equal(t, 201, resp.StatusCode) // We want to check the provenances of both provisioned and non-provisioned rules createRule(t, apiClient, namespaceUID) req = createTestRequest("GET", url, "admin", "") resp, err = http.DefaultClient.Do(req) require.NoError(t, err) var rules definitions.ProvisionedAlertRules require.NoError(t, json.NewDecoder(resp.Body).Decode(&rules)) require.NoError(t, resp.Body.Close()) require.Len(t, rules, 2) sort.Slice(rules, func(i, j int) bool { return rules[i].ID < rules[j].ID }) require.Equal(t, definitions.Provenance("api"), rules[0].Provenance) require.Equal(t, definitions.Provenance(""), rules[1].Provenance) }) } func createTestRequest(method string, url string, user string, body string) *http.Request { var bodyBuf io.Reader if body != "" { bodyBuf = bytes.NewReader([]byte(body)) } req, _ := http.NewRequest(method, url, bodyBuf) if bodyBuf != nil { req.Header.Set("Content-Type", "application/json") } if user != "" { req.SetBasicAuth(user, user) } return req }