mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 12:14:08 -06:00
1136 lines
47 KiB
Go
1136 lines
47 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
prometheus "github.com/prometheus/alertmanager/config"
|
|
"github.com/prometheus/alertmanager/timeinterval"
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
|
"github.com/grafana/grafana/pkg/services/secrets"
|
|
secrets_fakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
func TestProvisioningApi(t *testing.T) {
|
|
t.Run("policies", func(t *testing.T) {
|
|
t.Run("successful GET returns 200", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
|
|
response := sut.RouteGetPolicyTree(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
})
|
|
|
|
t.Run("successful PUT returns 202", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
tree := definitions.Route{}
|
|
|
|
response := sut.RoutePutPolicyTree(&rc, tree)
|
|
|
|
require.Equal(t, 202, response.Status())
|
|
})
|
|
|
|
t.Run("successful DELETE returns 202", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
|
|
response := sut.RouteResetPolicyTree(&rc)
|
|
|
|
require.Equal(t, 202, response.Status())
|
|
})
|
|
|
|
t.Run("when new policy tree is invalid", func(t *testing.T) {
|
|
t.Run("PUT returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
sut.policies = &fakeRejectingNotificationPolicyService{}
|
|
rc := createTestRequestCtx()
|
|
tree := definitions.Route{}
|
|
|
|
response := sut.RoutePutPolicyTree(&rc, tree)
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
expBody := `{"error":"invalid object specification: invalid policy tree","message":"invalid object specification: invalid policy tree"}`
|
|
require.Equal(t, expBody, string(response.Body()))
|
|
})
|
|
})
|
|
|
|
t.Run("when org has no AM config", func(t *testing.T) {
|
|
t.Run("GET returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
rc.SignedInUser.OrgID = 2
|
|
|
|
response := sut.RouteGetPolicyTree(&rc)
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
|
|
t.Run("POST returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
rc.SignedInUser.OrgID = 2
|
|
|
|
response := sut.RouteGetPolicyTree(&rc)
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
})
|
|
|
|
t.Run("when an unspecified error occurs", func(t *testing.T) {
|
|
t.Run("GET returns 500", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
sut.policies = &fakeFailingNotificationPolicyService{}
|
|
rc := createTestRequestCtx()
|
|
|
|
response := sut.RouteGetPolicyTree(&rc)
|
|
|
|
require.Equal(t, 500, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "something went wrong")
|
|
})
|
|
|
|
t.Run("PUT returns 500", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
sut.policies = &fakeFailingNotificationPolicyService{}
|
|
rc := createTestRequestCtx()
|
|
tree := definitions.Route{}
|
|
|
|
response := sut.RoutePutPolicyTree(&rc, tree)
|
|
|
|
require.Equal(t, 500, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "something went wrong")
|
|
})
|
|
|
|
t.Run("DELETE returns 500", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
sut.policies = &fakeFailingNotificationPolicyService{}
|
|
rc := createTestRequestCtx()
|
|
|
|
response := sut.RouteResetPolicyTree(&rc)
|
|
|
|
require.Equal(t, 500, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "something went wrong")
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("contact points", func(t *testing.T) {
|
|
t.Run("are invalid", func(t *testing.T) {
|
|
t.Run("POST returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
cp := createInvalidContactPoint()
|
|
|
|
response := sut.RoutePostContactPoint(&rc, cp)
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "recipient must be specified")
|
|
})
|
|
|
|
t.Run("PUT returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
cp := createInvalidContactPoint()
|
|
|
|
response := sut.RoutePutContactPoint(&rc, cp, "email-uid")
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "recipient must be specified")
|
|
})
|
|
})
|
|
|
|
t.Run("are missing, PUT returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
cp := createInvalidContactPoint()
|
|
|
|
response := sut.RoutePutContactPoint(&rc, cp, "does not exist")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
})
|
|
|
|
t.Run("templates", func(t *testing.T) {
|
|
t.Run("are invalid", func(t *testing.T) {
|
|
t.Run("PUT returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
tmpl := definitions.NotificationTemplateContent{Template: ""}
|
|
|
|
response := sut.RoutePutTemplate(&rc, tmpl, "test")
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "template must have content")
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("mute timings", func(t *testing.T) {
|
|
t.Run("are invalid", func(t *testing.T) {
|
|
t.Run("POST returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
mti := createInvalidMuteTiming()
|
|
|
|
response := sut.RoutePostMuteTiming(&rc, mti)
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "invalid")
|
|
})
|
|
|
|
t.Run("PUT returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
mti := createInvalidMuteTiming()
|
|
|
|
response := sut.RoutePutMuteTiming(&rc, mti, "interval")
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "invalid")
|
|
})
|
|
})
|
|
|
|
t.Run("are missing, PUT returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
mti := definitions.MuteTimeInterval{}
|
|
|
|
response := sut.RoutePutMuteTiming(&rc, mti, "does not exist")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
})
|
|
|
|
t.Run("alert rules", func(t *testing.T) {
|
|
t.Run("are invalid", func(t *testing.T) {
|
|
t.Run("POST returns 400 on wrong body params", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
rule := createInvalidAlertRule()
|
|
|
|
response := sut.RoutePostAlertRule(&rc, rule)
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "invalid alert rule")
|
|
})
|
|
|
|
t.Run("PUT returns 400 on wrong body params", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
uid := "123123"
|
|
rule := createTestAlertRule("rule", 1)
|
|
rule.UID = uid
|
|
insertRule(t, sut, rule)
|
|
rule = createInvalidAlertRule()
|
|
|
|
response := sut.RoutePutAlertRule(&rc, rule, uid)
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "invalid alert rule")
|
|
})
|
|
})
|
|
|
|
t.Run("exist in non-default orgs", func(t *testing.T) {
|
|
t.Run("POST sets expected fields with no provenance", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
rc.Req.Header = map[string][]string{"X-Disable-Provenance": {"true"}}
|
|
rc.OrgID = 3
|
|
rule := createTestAlertRule("rule", 1)
|
|
|
|
response := sut.RoutePostAlertRule(&rc, rule)
|
|
|
|
require.Equal(t, 201, response.Status())
|
|
created := deserializeRule(t, response.Body())
|
|
require.Equal(t, int64(3), created.OrgID)
|
|
require.Equal(t, definitions.Provenance(models.ProvenanceNone), created.Provenance)
|
|
})
|
|
|
|
t.Run("PUT sets expected fields with no provenance", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
uid := t.Name()
|
|
rule := createTestAlertRule("rule", 1)
|
|
rule.UID = uid
|
|
insertRuleInOrg(t, sut, rule, 3)
|
|
rc := createTestRequestCtx()
|
|
rc.Req.Header = map[string][]string{"X-Disable-Provenance": {"hello"}}
|
|
rc.OrgID = 3
|
|
rule.OrgID = 1 // Set the org back to something wrong, we should still prefer the value from the req context.
|
|
|
|
response := sut.RoutePutAlertRule(&rc, rule, rule.UID)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
created := deserializeRule(t, response.Body())
|
|
require.Equal(t, int64(3), created.OrgID)
|
|
require.Equal(t, definitions.Provenance(models.ProvenanceNone), created.Provenance)
|
|
})
|
|
})
|
|
|
|
t.Run("are missing, PUT returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
rule := createTestAlertRule("rule", 1)
|
|
|
|
response := sut.RoutePutAlertRule(&rc, rule, "does not exist")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
|
|
t.Run("are missing, GET returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
response := sut.RouteRouteGetAlertRule(&rc, "does not exist")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
|
|
t.Run("have reached the rule quota, POST returns 403", func(t *testing.T) {
|
|
env := createTestEnv(t)
|
|
quotas := provisioning.MockQuotaChecker{}
|
|
quotas.EXPECT().LimitExceeded()
|
|
env.quotas = "as
|
|
sut := createProvisioningSrvSutFromEnv(t, &env)
|
|
rule := createTestAlertRule("rule", 1)
|
|
rc := createTestRequestCtx()
|
|
|
|
response := sut.RoutePostAlertRule(&rc, rule)
|
|
|
|
require.Equal(t, 403, response.Status())
|
|
})
|
|
})
|
|
|
|
t.Run("alert rule groups", func(t *testing.T) {
|
|
t.Run("are present, GET returns 200", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRuleGroup(&rc, "folder-uid", "my-cool-group")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
})
|
|
|
|
t.Run("are missing, GET returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRuleGroup(&rc, "folder-uid", "does not exist")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
|
|
t.Run("are invalid at group level", func(t *testing.T) {
|
|
t.Run("PUT returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
group := createInvalidAlertRuleGroup()
|
|
group.Interval = 0
|
|
|
|
response := sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "invalid alert rule")
|
|
})
|
|
})
|
|
|
|
t.Run("are invalid at rule level", func(t *testing.T) {
|
|
t.Run("PUT returns 400", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
group := createInvalidAlertRuleGroup()
|
|
|
|
response := sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
|
|
|
|
require.Equal(t, 400, response.Status())
|
|
require.NotEmpty(t, response.Body())
|
|
require.Contains(t, string(response.Body()), "invalid alert rule")
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("exports", func(t *testing.T) {
|
|
t.Run("alert rule group", func(t *testing.T) {
|
|
t.Run("are present, GET returns 200", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
})
|
|
|
|
t.Run("are missing, GET returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "does not exist")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
|
|
t.Run("accept header contains yaml, GET returns text yaml", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/yaml")
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "text/yaml", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("query format contains yaml, GET returns text yaml", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Form.Set("format", "yaml")
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "text/yaml", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("query format contains unknown value, GET returns text yaml", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Form.Set("format", "foo")
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "text/yaml", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("accept header contains json, GET returns json", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json")
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "application/json", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("accept header contains json and yaml, GET returns json", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json, application/yaml")
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "application/json", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("query param download=true, GET returns content disposition attachment", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Form.Set("download", "true")
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Contains(t, rc.Context.Resp.Header().Get("Content-Disposition"), "attachment")
|
|
})
|
|
|
|
t.Run("query param download=false, GET returns empty content disposition", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Form.Set("download", "false")
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "", rc.Context.Resp.Header().Get("Content-Disposition"))
|
|
})
|
|
|
|
t.Run("query param download not set, GET returns empty content disposition", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "", rc.Context.Resp.Header().Get("Content-Disposition"))
|
|
})
|
|
|
|
t.Run("yaml body content is the default", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
insertRule(t, sut, createTestAlertRule("rule2", 1))
|
|
|
|
expectedResponse := "apiVersion: 1\ngroups:\n - orgId: 1\n name: my-cool-group\n folder" +
|
|
": Folder Title\n interval: 1m\n rules:\n - uid: rule1\n title: rule1\n" +
|
|
" condition: A\n data:\n - refId: A\n datasourceUid" +
|
|
": \"\"\n model:\n conditions:\n - evaluator:\n" +
|
|
" params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n - uid: rule2\n title: rule2\n condition: A\n data:\n - refId: A\n datasourceUid: \"\"\n model:\n conditions:\n - evaluator:\n params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n"
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
|
|
t.Run("json body content is as expected", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
insertRule(t, sut, createTestAlertRule("rule2", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json")
|
|
expectedResponse := `{"apiVersion":1,"groups":[{"orgId":1,"name":"my-cool-group","folder":"Folder Title","interval":"1m","rules":[{"uid":"rule1","title":"rule1","condition":"A","data":[{"refId":"A","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"","model":{"conditions":[{"evaluator":{"params":[3],"type":"gt"},"operator":{"type":"and"},"query":{"params":["A"]},"reducer":{"type":"last"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1==0","intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"}}],"noDataState":"OK","execErrState":"OK","for":"0s","isPaused":false},{"uid":"rule2","title":"rule2","condition":"A","data":[{"refId":"A","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"","model":{"conditions":[{"evaluator":{"params":[3],"type":"gt"},"operator":{"type":"and"},"query":{"params":["A"]},"reducer":{"type":"last"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1==0","intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"}}],"noDataState":"OK","execErrState":"OK","for":"0s","isPaused":false}]}]}`
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
|
|
t.Run("yaml body content is as expected", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
insertRule(t, sut, createTestAlertRule("rule2", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/yaml")
|
|
expectedResponse := "apiVersion: 1\ngroups:\n - orgId: 1\n name: my-cool-group\n folder" +
|
|
": Folder Title\n interval: 1m\n rules:\n - uid: rule1\n title: rule1\n" +
|
|
" condition: A\n data:\n - refId: A\n datasourceUid" +
|
|
": \"\"\n model:\n conditions:\n - evaluator:\n" +
|
|
" params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n - uid: rule2\n title: rule2\n condition: A\n data:\n - refId: A\n datasourceUid: \"\"\n model:\n conditions:\n - evaluator:\n params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n"
|
|
|
|
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
})
|
|
|
|
t.Run("alert rule", func(t *testing.T) {
|
|
t.Run("are present, GET returns 200", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
})
|
|
|
|
t.Run("are missing, GET returns 404", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule404")
|
|
|
|
require.Equal(t, 404, response.Status())
|
|
})
|
|
|
|
t.Run("accept header contains yaml, GET returns text yaml", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/yaml")
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "text/yaml", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("accept header contains json, GET returns json", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json")
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "application/json", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("accept header contains json and yaml, GET returns json", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json, application/yaml")
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "application/json", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("query param download=true, GET returns content disposition attachment", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
rc.Context.Req.Form.Set("download", "true")
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Contains(t, rc.Context.Resp.Header().Get("Content-Disposition"), "attachment")
|
|
})
|
|
|
|
t.Run("query param download=false, GET returns empty content disposition", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
rc.Context.Req.Form.Set("download", "false")
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "", rc.Context.Resp.Header().Get("Content-Disposition"))
|
|
})
|
|
|
|
t.Run("query param download not set, GET returns empty content disposition", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "", rc.Context.Resp.Header().Get("Content-Disposition"))
|
|
})
|
|
|
|
t.Run("json body content is as expected", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
expectedResponse := `{"apiVersion":1,"groups":[{"orgId":1,"name":"my-cool-group","folder":"Folder Title","interval":"1m","rules":[{"uid":"rule1","title":"rule1","condition":"A","data":[{"refId":"A","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"","model":{"conditions":[{"evaluator":{"params":[3],"type":"gt"},"operator":{"type":"and"},"query":{"params":["A"]},"reducer":{"type":"last"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1==0","intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"}}],"noDataState":"OK","execErrState":"OK","for":"0s","isPaused":false}]}]}`
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json")
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
|
|
t.Run("yaml body content is as expected", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule1", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/yaml")
|
|
expectedResponse := "apiVersion: 1\ngroups:\n - orgId: 1\n name: my-cool-group\n folder: Folder Title\n interval: 1m\n rules:\n - uid: rule1\n title: rule1\n condition: A\n data:\n - refId: A\n datasourceUid: \"\"\n model:\n conditions:\n - evaluator:\n params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n"
|
|
|
|
response := sut.RouteGetAlertRuleExport(&rc, "rule1")
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
})
|
|
|
|
t.Run("all alert rules", func(t *testing.T) {
|
|
t.Run("are present, GET returns 200", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
})
|
|
|
|
t.Run("accept header contains yaml, GET returns text yaml", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/yaml")
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "text/yaml", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("accept header contains json, GET returns json", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json")
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "application/json", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("accept header contains json and yaml, GET returns json", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json, application/yaml")
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "application/json", rc.Context.Resp.Header().Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("query param download=true, GET returns content disposition attachment", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Form.Set("download", "true")
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Contains(t, rc.Context.Resp.Header().Get("Content-Disposition"), "attachment")
|
|
})
|
|
|
|
t.Run("query param download=false, GET returns empty content disposition", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
rc.Context.Req.Form.Set("download", "false")
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "", rc.Context.Resp.Header().Get("Content-Disposition"))
|
|
})
|
|
|
|
t.Run("query param download not set, GET returns empty content disposition", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRule("rule", 1))
|
|
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
response.WriteTo(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, "", rc.Context.Resp.Header().Get("Content-Disposition"))
|
|
})
|
|
|
|
t.Run("json body content is as expected", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRuleWithFolderAndGroup("rule1", 1, "folder-uid", "groupa"))
|
|
insertRule(t, sut, createTestAlertRuleWithFolderAndGroup("rule2", 1, "folder-uid", "groupb"))
|
|
insertRule(t, sut, createTestAlertRuleWithFolderAndGroup("rule3", 1, "folder-uid2", "groupb"))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/json")
|
|
expectedResponse := `{"apiVersion":1,"groups":[{"orgId":1,"name":"groupa","folder":"Folder Title","interval":"1m","rules":[{"uid":"rule1","title":"rule1","condition":"A","data":[{"refId":"A","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"","model":{"conditions":[{"evaluator":{"params":[3],"type":"gt"},"operator":{"type":"and"},"query":{"params":["A"]},"reducer":{"type":"last"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1==0","intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"}}],"noDataState":"OK","execErrState":"OK","for":"0s","isPaused":false}]},{"orgId":1,"name":"groupb","folder":"Folder Title","interval":"1m","rules":[{"uid":"rule2","title":"rule2","condition":"A","data":[{"refId":"A","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"","model":{"conditions":[{"evaluator":{"params":[3],"type":"gt"},"operator":{"type":"and"},"query":{"params":["A"]},"reducer":{"type":"last"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1==0","intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"}}],"noDataState":"OK","execErrState":"OK","for":"0s","isPaused":false}]},{"orgId":1,"name":"groupb","folder":"Folder Title2","interval":"1m","rules":[{"uid":"rule3","title":"rule3","condition":"A","data":[{"refId":"A","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"","model":{"conditions":[{"evaluator":{"params":[3],"type":"gt"},"operator":{"type":"and"},"query":{"params":["A"]},"reducer":{"type":"last"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1==0","intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"}}],"noDataState":"OK","execErrState":"OK","for":"0s","isPaused":false}]}]}`
|
|
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
|
|
t.Run("yaml body content is as expected", func(t *testing.T) {
|
|
sut := createProvisioningSrvSut(t)
|
|
rc := createTestRequestCtx()
|
|
insertRule(t, sut, createTestAlertRuleWithFolderAndGroup("rule1", 1, "folder-uid", "groupa"))
|
|
insertRule(t, sut, createTestAlertRuleWithFolderAndGroup("rule2", 1, "folder-uid", "groupb"))
|
|
insertRule(t, sut, createTestAlertRuleWithFolderAndGroup("rule3", 1, "folder-uid2", "groupb"))
|
|
|
|
rc.Context.Req.Header.Add("Accept", "application/yaml")
|
|
expectedResponse := "apiVersion: 1\ngroups:\n - orgId: 1\n name: groupa\n folder: Folder Title\n interval: 1m\n rules:\n - uid: rule1\n title: rule1\n condition: A\n data:\n - refId: A\n datasourceUid: \"\"\n model:\n conditions:\n - evaluator:\n params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n - orgId: 1\n name: groupb\n folder: Folder Title\n interval: 1m\n rules:\n - uid: rule2\n title: rule2\n condition: A\n data:\n - refId: A\n datasourceUid: \"\"\n model:\n conditions:\n - evaluator:\n params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n - orgId: 1\n name: groupb\n folder: Folder Title2\n interval: 1m\n rules:\n - uid: rule3\n title: rule3\n condition: A\n data:\n - refId: A\n datasourceUid: \"\"\n model:\n conditions:\n - evaluator:\n params:\n - 3\n type: gt\n operator:\n type: and\n query:\n params:\n - A\n reducer:\n type: last\n type: query\n datasource:\n type: __expr__\n uid: __expr__\n expression: 1==0\n intervalMs: 1000\n maxDataPoints: 43200\n refId: A\n type: math\n noDataState: OK\n execErrState: OK\n for: 0s\n isPaused: false\n"
|
|
|
|
response := sut.RouteGetAlertRulesExport(&rc)
|
|
|
|
require.Equal(t, 200, response.Status())
|
|
require.Equal(t, expectedResponse, string(response.Body()))
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
// testEnvironment binds together common dependencies for testing alerting APIs.
|
|
type testEnvironment struct {
|
|
secrets secrets.Service
|
|
log log.Logger
|
|
store store.DBstore
|
|
dashboardService dashboards.DashboardService
|
|
configs provisioning.AMConfigStore
|
|
xact provisioning.TransactionManager
|
|
quotas provisioning.QuotaChecker
|
|
prov provisioning.ProvisioningStore
|
|
}
|
|
|
|
func createTestEnv(t *testing.T) testEnvironment {
|
|
t.Helper()
|
|
|
|
secrets := secrets_fakes.NewFakeSecretsService()
|
|
log := log.NewNopLogger()
|
|
configs := &provisioning.MockAMConfigStore{}
|
|
configs.EXPECT().
|
|
GetsConfig(models.AlertConfiguration{
|
|
AlertmanagerConfiguration: testConfig,
|
|
})
|
|
sqlStore := db.InitTestDB(t)
|
|
store := store.DBstore{
|
|
SQLStore: sqlStore,
|
|
Cfg: setting.UnifiedAlertingSettings{
|
|
BaseInterval: time.Second * 10,
|
|
},
|
|
}
|
|
quotas := &provisioning.MockQuotaChecker{}
|
|
quotas.EXPECT().LimitOK()
|
|
xact := &provisioning.NopTransactionManager{}
|
|
prov := &provisioning.MockProvisioningStore{}
|
|
prov.EXPECT().SaveSucceeds()
|
|
prov.EXPECT().GetReturns(models.ProvenanceNone)
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(&dashboards.Dashboard{
|
|
UID: "folder-uid",
|
|
Title: "Folder Title",
|
|
}, nil).Maybe()
|
|
dashboardService.On("GetDashboards", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardsQuery")).Return([]*dashboards.Dashboard{{
|
|
UID: "folder-uid",
|
|
Title: "Folder Title",
|
|
},
|
|
{
|
|
UID: "folder-uid2",
|
|
Title: "Folder Title2",
|
|
}}, nil).Maybe()
|
|
|
|
return testEnvironment{
|
|
secrets: secrets,
|
|
log: log,
|
|
configs: configs,
|
|
store: store,
|
|
dashboardService: dashboardService,
|
|
xact: xact,
|
|
prov: prov,
|
|
quotas: quotas,
|
|
}
|
|
}
|
|
|
|
func createProvisioningSrvSut(t *testing.T) ProvisioningSrv {
|
|
t.Helper()
|
|
|
|
env := createTestEnv(t)
|
|
return createProvisioningSrvSutFromEnv(t, &env)
|
|
}
|
|
|
|
func createProvisioningSrvSutFromEnv(t *testing.T, env *testEnvironment) ProvisioningSrv {
|
|
t.Helper()
|
|
|
|
return ProvisioningSrv{
|
|
log: env.log,
|
|
policies: newFakeNotificationPolicyService(),
|
|
contactPointService: provisioning.NewContactPointService(env.configs, env.secrets, env.prov, env.xact, env.log),
|
|
templates: provisioning.NewTemplateService(env.configs, env.prov, env.xact, env.log),
|
|
muteTimings: provisioning.NewMuteTimingService(env.configs, env.prov, env.xact, env.log),
|
|
alertRules: provisioning.NewAlertRuleService(env.store, env.prov, env.dashboardService, env.quotas, env.xact, 60, 10, env.log),
|
|
}
|
|
}
|
|
|
|
func createTestRequestCtx() contextmodel.ReqContext {
|
|
return contextmodel.ReqContext{
|
|
Context: &web.Context{
|
|
Req: &http.Request{
|
|
Header: make(http.Header),
|
|
Form: make(url.Values),
|
|
},
|
|
Resp: web.NewResponseWriter("GET", httptest.NewRecorder()),
|
|
},
|
|
SignedInUser: &user.SignedInUser{
|
|
OrgID: 1,
|
|
},
|
|
}
|
|
}
|
|
|
|
type fakeNotificationPolicyService struct {
|
|
tree definitions.Route
|
|
prov models.Provenance
|
|
}
|
|
|
|
func newFakeNotificationPolicyService() *fakeNotificationPolicyService {
|
|
return &fakeNotificationPolicyService{
|
|
tree: definitions.Route{
|
|
Receiver: "some-receiver",
|
|
},
|
|
prov: models.ProvenanceNone,
|
|
}
|
|
}
|
|
|
|
func (f *fakeNotificationPolicyService) GetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
|
if orgID != 1 {
|
|
return definitions.Route{}, store.ErrNoAlertmanagerConfiguration
|
|
}
|
|
result := f.tree
|
|
result.Provenance = definitions.Provenance(f.prov)
|
|
return result, nil
|
|
}
|
|
|
|
func (f *fakeNotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgID int64, tree definitions.Route, p models.Provenance) error {
|
|
if orgID != 1 {
|
|
return store.ErrNoAlertmanagerConfiguration
|
|
}
|
|
f.tree = tree
|
|
f.prov = p
|
|
return nil
|
|
}
|
|
|
|
func (f *fakeNotificationPolicyService) ResetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
|
f.tree = definitions.Route{} // TODO
|
|
return f.tree, nil
|
|
}
|
|
|
|
type fakeFailingNotificationPolicyService struct{}
|
|
|
|
func (f *fakeFailingNotificationPolicyService) GetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
|
return definitions.Route{}, fmt.Errorf("something went wrong")
|
|
}
|
|
|
|
func (f *fakeFailingNotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgID int64, tree definitions.Route, p models.Provenance) error {
|
|
return fmt.Errorf("something went wrong")
|
|
}
|
|
|
|
func (f *fakeFailingNotificationPolicyService) ResetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
|
return definitions.Route{}, fmt.Errorf("something went wrong")
|
|
}
|
|
|
|
type fakeRejectingNotificationPolicyService struct{}
|
|
|
|
func (f *fakeRejectingNotificationPolicyService) GetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
|
return definitions.Route{}, nil
|
|
}
|
|
|
|
func (f *fakeRejectingNotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgID int64, tree definitions.Route, p models.Provenance) error {
|
|
return fmt.Errorf("%w: invalid policy tree", provisioning.ErrValidation)
|
|
}
|
|
|
|
func (f *fakeRejectingNotificationPolicyService) ResetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
|
return definitions.Route{}, nil
|
|
}
|
|
|
|
func createInvalidContactPoint() definitions.EmbeddedContactPoint {
|
|
settings, _ := simplejson.NewJson([]byte(`{}`))
|
|
return definitions.EmbeddedContactPoint{
|
|
Name: "test-contact-point",
|
|
Type: "slack",
|
|
Settings: settings,
|
|
}
|
|
}
|
|
|
|
func createInvalidMuteTiming() definitions.MuteTimeInterval {
|
|
return definitions.MuteTimeInterval{
|
|
MuteTimeInterval: prometheus.MuteTimeInterval{
|
|
Name: "interval",
|
|
TimeIntervals: []timeinterval.TimeInterval{
|
|
{
|
|
Weekdays: []timeinterval.WeekdayRange{
|
|
{
|
|
InclusiveRange: timeinterval.InclusiveRange{
|
|
Begin: -1,
|
|
End: 7,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func createInvalidAlertRule() definitions.ProvisionedAlertRule {
|
|
return definitions.ProvisionedAlertRule{}
|
|
}
|
|
|
|
func createInvalidAlertRuleGroup() definitions.AlertRuleGroup {
|
|
return definitions.AlertRuleGroup{
|
|
Title: "invalid",
|
|
Interval: 10,
|
|
Rules: []definitions.ProvisionedAlertRule{{}},
|
|
}
|
|
}
|
|
|
|
func createTestAlertRuleWithFolderAndGroup(title string, orgID int64, folderUid string, group string) definitions.ProvisionedAlertRule {
|
|
rule := createTestAlertRule(title, orgID)
|
|
rule.FolderUID = folderUid
|
|
rule.RuleGroup = group
|
|
return rule
|
|
}
|
|
|
|
func createTestAlertRule(title string, orgID int64) definitions.ProvisionedAlertRule {
|
|
return definitions.ProvisionedAlertRule{
|
|
UID: title,
|
|
OrgID: orgID,
|
|
Title: title,
|
|
Condition: "A",
|
|
Data: []definitions.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
Model: json.RawMessage(testModel),
|
|
RelativeTimeRange: definitions.RelativeTimeRange{
|
|
From: definitions.Duration(60),
|
|
To: definitions.Duration(0),
|
|
},
|
|
},
|
|
},
|
|
RuleGroup: "my-cool-group",
|
|
FolderUID: "folder-uid",
|
|
For: model.Duration(60),
|
|
NoDataState: definitions.OK,
|
|
ExecErrState: definitions.OkErrState,
|
|
}
|
|
}
|
|
|
|
func insertRule(t *testing.T, srv ProvisioningSrv, rule definitions.ProvisionedAlertRule) {
|
|
insertRuleInOrg(t, srv, rule, 1)
|
|
}
|
|
|
|
func insertRuleInOrg(t *testing.T, srv ProvisioningSrv, rule definitions.ProvisionedAlertRule, orgID int64) {
|
|
t.Helper()
|
|
|
|
rc := createTestRequestCtx()
|
|
rc.OrgID = orgID
|
|
resp := srv.RoutePostAlertRule(&rc, rule)
|
|
require.Equal(t, 201, resp.Status())
|
|
}
|
|
|
|
func deserializeRule(t *testing.T, data []byte) definitions.ProvisionedAlertRule {
|
|
t.Helper()
|
|
|
|
var rule definitions.ProvisionedAlertRule
|
|
err := json.Unmarshal(data, &rule)
|
|
require.NoError(t, err)
|
|
return rule
|
|
}
|
|
|
|
var testModel = `
|
|
{
|
|
"conditions": [
|
|
{
|
|
"evaluator": {
|
|
"params": [
|
|
3
|
|
],
|
|
"type": "gt"
|
|
},
|
|
"operator": {
|
|
"type": "and"
|
|
},
|
|
"query": {
|
|
"params": [
|
|
"A"
|
|
]
|
|
},
|
|
"reducer": {
|
|
"type": "last"
|
|
},
|
|
"type": "query"
|
|
}
|
|
],
|
|
"datasource": {
|
|
"type": "__expr__",
|
|
"uid": "__expr__"
|
|
},
|
|
"expression": "1==0",
|
|
"intervalMs": 1000,
|
|
"maxDataPoints": 43200,
|
|
"refId": "A",
|
|
"type": "math"
|
|
}
|
|
`
|
|
|
|
var testConfig = `
|
|
{
|
|
"template_files": {
|
|
"a": "template"
|
|
},
|
|
"alertmanager_config": {
|
|
"route": {
|
|
"receiver": "grafana-default-email"
|
|
},
|
|
"receivers": [{
|
|
"name": "grafana-default-email",
|
|
"grafana_managed_receiver_configs": [{
|
|
"uid": "email-uid",
|
|
"name": "email receiver",
|
|
"type": "email",
|
|
"isDefault": true,
|
|
"settings": {
|
|
"addresses": "<example@email.com>"
|
|
}
|
|
}]
|
|
}],
|
|
"mute_time_intervals": [{
|
|
"name": "interval",
|
|
"time_intervals": []
|
|
}]
|
|
}
|
|
}
|
|
`
|