Alerting: Receiver API complete core implementation (#91738)
* Replace global authz abstraction with one compatible with uid scope * Replace GettableApiReceiver with models.Receiver in receiver_svc * GrafanaIntegrationConfig -> models.Integration * Implement Create/Update methods * Add optimistic concurrency to receiver API * Add scope to ReceiversRead & ReceiversReadSecrets migrates existing permissions to include implicit global scope * Add receiver create, update, delete actions * Check if receiver is used by rules before delete * On receiver name change update in routes and notification settings * Improve errors * Linting * Include read permissions are requirements for create/update/delete * Alias ngalert/models to ngmodels to differentiate from v0alpha1 model * Ensure integration UIDs are valid, unique, and generated if empty * Validate integration settings on create/update * Leverage UidToName to GetReceiver instead of GetReceivers * Remove some unnecessary uses of simplejson * alerting.notifications.receiver -> alerting.notifications.receivers * validator -> provenanceValidator * Only validate the modified receiver stops existing invalid receivers from preventing modification of a valid receiver. * Improve error in Integration.Encrypt * Remove scope from alert.notifications.receivers:create * Add todos for receiver renaming * Use receiverAC precondition checks in k8s api * Linting * Optional optimistic concurrency for delete * make update-workspace * More specific auth checks in k8s authorize.go * Add debug log when delete optimistic concurrency is skipped * Improve error message on authorizer.DecisionDeny * Keep error for non-forbidden errutil errors
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
@@ -19,8 +18,8 @@ type NotificationSrv struct {
|
||||
}
|
||||
|
||||
type ReceiverService interface {
|
||||
GetReceiver(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error)
|
||||
ListReceivers(ctx context.Context, q models.ListReceiversQuery, user identity.Requester) ([]definitions.GettableApiReceiver, error)
|
||||
GetReceiver(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (*models.Receiver, error)
|
||||
ListReceivers(ctx context.Context, q models.ListReceiversQuery, user identity.Requester) ([]*models.Receiver, error)
|
||||
}
|
||||
|
||||
func (srv *NotificationSrv) RouteGetTimeInterval(c *contextmodel.ReqContext, name string) response.Response {
|
||||
@@ -51,7 +50,12 @@ func (srv *NotificationSrv) RouteGetReceiver(c *contextmodel.ReqContext, name st
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get receiver", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, receiver)
|
||||
gettable, err := GettableApiReceiverFromReceiver(receiver)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to convert receiver", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, gettable)
|
||||
}
|
||||
|
||||
func (srv *NotificationSrv) RouteGetReceivers(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -67,5 +71,10 @@ func (srv *NotificationSrv) RouteGetReceivers(c *contextmodel.ReqContext) respon
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get receiver groups", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, receivers)
|
||||
gettables, err := GettableApiReceiversFromReceivers(receivers)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to convert receivers", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, gettables)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
|
||||
am_config "github.com/prometheus/alertmanager/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -33,35 +32,33 @@ func TestRouteGetReceiver(t *testing.T) {
|
||||
fakeReceiverSvc := fakes.NewFakeReceiverService()
|
||||
|
||||
t.Run("returns expected model", func(t *testing.T) {
|
||||
expected := definitions.GettableApiReceiver{
|
||||
Receiver: am_config.Receiver{
|
||||
Name: "receiver1",
|
||||
},
|
||||
GettableGrafanaReceivers: definitions.GettableGrafanaReceivers{
|
||||
GrafanaManagedReceivers: []*definitions.GettableGrafanaReceiver{
|
||||
{
|
||||
UID: "uid1",
|
||||
Name: "receiver1",
|
||||
Type: "slack",
|
||||
},
|
||||
expected := &models.Receiver{
|
||||
Name: "receiver1",
|
||||
Integrations: []*models.Integration{
|
||||
{
|
||||
UID: "uid1",
|
||||
Name: "receiver1",
|
||||
Config: models.IntegrationConfig{Type: "slack"},
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error) {
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (*models.Receiver, error) {
|
||||
return expected, nil
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
rc := testReqCtx("GET")
|
||||
resp := handler.handleRouteGetReceiver(&rc, "receiver1")
|
||||
require.Equal(t, http.StatusOK, resp.Status())
|
||||
json, err := json.Marshal(expected)
|
||||
gettables, err := GettableApiReceiverFromReceiver(expected)
|
||||
require.NoError(t, err)
|
||||
json, err := json.Marshal(gettables)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, json, resp.Body())
|
||||
})
|
||||
|
||||
t.Run("builds query from request context and url param", func(t *testing.T) {
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error) {
|
||||
return definitions.GettableApiReceiver{}, nil
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (*models.Receiver, error) {
|
||||
return &models.Receiver{}, nil
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
rc := testReqCtx("GET")
|
||||
@@ -80,8 +77,8 @@ func TestRouteGetReceiver(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should pass along not found response", func(t *testing.T) {
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error) {
|
||||
return definitions.GettableApiReceiver{}, notifier.ErrReceiverNotFound.Errorf("")
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (*models.Receiver, error) {
|
||||
return nil, legacy_storage.ErrReceiverNotFound.Errorf("")
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
rc := testReqCtx("GET")
|
||||
@@ -90,8 +87,8 @@ func TestRouteGetReceiver(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should pass along permission denied response", func(t *testing.T) {
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error) {
|
||||
return definitions.GettableApiReceiver{}, ac.ErrAuthorizationBase.Errorf("")
|
||||
fakeReceiverSvc.GetReceiverFn = func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (*models.Receiver, error) {
|
||||
return nil, ac.ErrAuthorizationBase.Errorf("")
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
rc := testReqCtx("GET")
|
||||
@@ -104,23 +101,19 @@ func TestRouteGetReceivers(t *testing.T) {
|
||||
fakeReceiverSvc := fakes.NewFakeReceiverService()
|
||||
|
||||
t.Run("returns expected model", func(t *testing.T) {
|
||||
expected := []definitions.GettableApiReceiver{
|
||||
expected := []*models.Receiver{
|
||||
{
|
||||
Receiver: am_config.Receiver{
|
||||
Name: "receiver1",
|
||||
},
|
||||
GettableGrafanaReceivers: definitions.GettableGrafanaReceivers{
|
||||
GrafanaManagedReceivers: []*definitions.GettableGrafanaReceiver{
|
||||
{
|
||||
UID: "uid1",
|
||||
Name: "receiver1",
|
||||
Type: "slack",
|
||||
},
|
||||
Name: "receiver1",
|
||||
Integrations: []*models.Integration{
|
||||
{
|
||||
UID: "uid1",
|
||||
Name: "receiver1",
|
||||
Config: models.IntegrationConfig{Type: "slack"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
|
||||
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]*models.Receiver, error) {
|
||||
return expected, nil
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
@@ -128,14 +121,16 @@ func TestRouteGetReceivers(t *testing.T) {
|
||||
rc.Context.Req.Form.Set("names", "receiver1")
|
||||
resp := handler.handleRouteGetReceivers(&rc)
|
||||
require.Equal(t, http.StatusOK, resp.Status())
|
||||
json, err := json.Marshal(expected)
|
||||
gettables, err := GettableApiReceiversFromReceivers(expected)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, json, resp.Body())
|
||||
jsonBody, err := json.Marshal(gettables)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(jsonBody), string(resp.Body()))
|
||||
})
|
||||
|
||||
t.Run("builds query from request context", func(t *testing.T) {
|
||||
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
|
||||
return []definitions.GettableApiReceiver{}, nil
|
||||
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]*models.Receiver, error) {
|
||||
return nil, nil
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
rc := testReqCtx("GET")
|
||||
@@ -159,7 +154,7 @@ func TestRouteGetReceivers(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should pass along permission denied response", func(t *testing.T) {
|
||||
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
|
||||
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]*models.Receiver, error) {
|
||||
return nil, ac.ErrAuthorizationBase.Errorf("")
|
||||
}
|
||||
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
|
||||
@@ -221,7 +216,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
|
||||
{limit: 4, offset: 0, expected: expected[:4]},
|
||||
{limit: 1, offset: 1, expected: expected[1:2]},
|
||||
{limit: 2, offset: 2, expected: expected[2:4]},
|
||||
{limit: 2, offset: 99, expected: nil},
|
||||
{limit: 2, offset: 99, expected: []definitions.GettableApiReceiver{}},
|
||||
{limit: 0, offset: 0, expected: expected},
|
||||
{limit: 0, offset: 1, expected: expected[1:]},
|
||||
}
|
||||
@@ -237,7 +232,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
|
||||
err := json.Unmarshal(response.Body(), &configs)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, configs, tc.expected)
|
||||
require.Equal(t, tc.expected, configs)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -331,8 +326,8 @@ func TestRouteGetReceiversResponses(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("json body content is as expected", func(t *testing.T) {
|
||||
expectedRedactedResponse := `{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":true,"settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"secureFields":{"basicAuthPassword":true}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"secureFields":{}}]}`
|
||||
expectedDecryptedResponse := `{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":true,"settings":{"basicAuthPassword":"testpass","basicAuthUser":"test","url":"http://localhost:9093"},"secureFields":{"basicAuthPassword":true}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"secureFields":{}}]}`
|
||||
expectedRedactedResponse := `{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":true,"settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"secureFields":{"basicAuthPassword":true}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"settings":{"avatar_url":"some avatar","url":"[REDACTED]","use_discord_username":true},"secureFields":{"url":true}}]}`
|
||||
expectedDecryptedResponse := `{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":true,"settings":{"basicAuthPassword":"testpass","basicAuthUser":"test","url":"http://localhost:9093"},"secureFields":{"basicAuthPassword":true}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"secureFields":{"url":true}}]}`
|
||||
t.Run("decrypt false", func(t *testing.T) {
|
||||
env := createTestEnv(t, testContactPointConfig)
|
||||
sut := createNotificationSrvSutFromEnv(t, &env)
|
||||
@@ -375,6 +370,7 @@ func createNotificationSrvSutFromEnv(t *testing.T, env *testEnvironment) Notific
|
||||
ac.NewReceiverAccess[*models.Receiver](env.ac, false),
|
||||
legacy_storage.NewAlertmanagerConfigStore(env.configs),
|
||||
env.prov,
|
||||
env.store,
|
||||
env.secrets,
|
||||
env.xact,
|
||||
env.log,
|
||||
|
||||
@@ -1632,7 +1632,7 @@ func TestProvisioningApiContactPointExport(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("json body content is as expected", func(t *testing.T) {
|
||||
expectedRedactedResponse := `{"apiVersion":1,"contactPoints":[{"orgId":1,"name":"grafana-default-email","receivers":[{"uid":"ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b","type":"email","settings":{"addresses":"\u003cexample@email.com\u003e"},"disableResolveMessage":false}]},{"orgId":1,"name":"multiple integrations","receivers":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","type":"prometheus-alertmanager","settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"disableResolveMessage":true},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","type":"discord","settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"disableResolveMessage":false}]},{"orgId":1,"name":"pagerduty test","receivers":[{"uid":"b9bf06f8-bde2-4438-9d4a-bba0522dcd4d","type":"pagerduty","settings":{"client":"some client","integrationKey":"[REDACTED]","severity":"criticalish"},"disableResolveMessage":false}]},{"orgId":1,"name":"slack test","receivers":[{"uid":"cbfd0976-8228-4126-b672-4419f30a9e50","type":"slack","settings":{"text":"title body test","title":"title test","url":"[REDACTED]"},"disableResolveMessage":true}]}]}`
|
||||
expectedRedactedResponse := `{"apiVersion":1,"contactPoints":[{"orgId":1,"name":"grafana-default-email","receivers":[{"uid":"ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b","type":"email","settings":{"addresses":"\u003cexample@email.com\u003e"},"disableResolveMessage":false}]},{"orgId":1,"name":"multiple integrations","receivers":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","type":"prometheus-alertmanager","settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"disableResolveMessage":true},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","type":"discord","settings":{"avatar_url":"some avatar","url":"[REDACTED]","use_discord_username":true},"disableResolveMessage":false}]},{"orgId":1,"name":"pagerduty test","receivers":[{"uid":"b9bf06f8-bde2-4438-9d4a-bba0522dcd4d","type":"pagerduty","settings":{"client":"some client","integrationKey":"[REDACTED]","severity":"criticalish"},"disableResolveMessage":false}]},{"orgId":1,"name":"slack test","receivers":[{"uid":"cbfd0976-8228-4126-b672-4419f30a9e50","type":"slack","settings":{"text":"title body test","title":"title test","url":"[REDACTED]"},"disableResolveMessage":true}]}]}`
|
||||
t.Run("decrypt false", func(t *testing.T) {
|
||||
env := createTestEnv(t, testContactPointConfig)
|
||||
sut := createProvisioningSrvSutFromEnv(t, &env)
|
||||
@@ -1685,14 +1685,14 @@ func TestProvisioningApiContactPointExport(t *testing.T) {
|
||||
|
||||
response := sut.RouteGetContactPointsExport(&rc)
|
||||
|
||||
expectedResponse := `{"apiVersion":1,"contactPoints":[{"orgId":1,"name":"multiple integrations","receivers":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","type":"prometheus-alertmanager","settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"disableResolveMessage":true},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","type":"discord","settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"disableResolveMessage":false}]}]}`
|
||||
expectedResponse := `{"apiVersion":1,"contactPoints":[{"orgId":1,"name":"multiple integrations","receivers":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","type":"prometheus-alertmanager","settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"disableResolveMessage":true},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","type":"discord","settings":{"avatar_url":"some avatar","url":"[REDACTED]","use_discord_username":true},"disableResolveMessage":false}]}]}`
|
||||
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) {
|
||||
expectedRedactedResponse := "apiVersion: 1\ncontactPoints:\n - orgId: 1\n name: grafana-default-email\n receivers:\n - uid: ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b\n type: email\n settings:\n addresses: <example@email.com>\n disableResolveMessage: false\n - orgId: 1\n name: multiple integrations\n receivers:\n - uid: c2090fda-f824-4add-b545-5a4d5c2ef082\n type: prometheus-alertmanager\n settings:\n basicAuthPassword: '[REDACTED]'\n basicAuthUser: test\n url: http://localhost:9093\n disableResolveMessage: true\n - uid: c84539ec-f87e-4fc5-9a91-7a687d34bbd1\n type: discord\n settings:\n avatar_url: some avatar\n url: some url\n use_discord_username: true\n disableResolveMessage: false\n - orgId: 1\n name: pagerduty test\n receivers:\n - uid: b9bf06f8-bde2-4438-9d4a-bba0522dcd4d\n type: pagerduty\n settings:\n client: some client\n integrationKey: '[REDACTED]'\n severity: criticalish\n disableResolveMessage: false\n - orgId: 1\n name: slack test\n receivers:\n - uid: cbfd0976-8228-4126-b672-4419f30a9e50\n type: slack\n settings:\n text: title body test\n title: title test\n url: '[REDACTED]'\n disableResolveMessage: true\n"
|
||||
expectedRedactedResponse := "apiVersion: 1\ncontactPoints:\n - orgId: 1\n name: grafana-default-email\n receivers:\n - uid: ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b\n type: email\n settings:\n addresses: <example@email.com>\n disableResolveMessage: false\n - orgId: 1\n name: multiple integrations\n receivers:\n - uid: c2090fda-f824-4add-b545-5a4d5c2ef082\n type: prometheus-alertmanager\n settings:\n basicAuthPassword: '[REDACTED]'\n basicAuthUser: test\n url: http://localhost:9093\n disableResolveMessage: true\n - uid: c84539ec-f87e-4fc5-9a91-7a687d34bbd1\n type: discord\n settings:\n avatar_url: some avatar\n url: '[REDACTED]'\n use_discord_username: true\n disableResolveMessage: false\n - orgId: 1\n name: pagerduty test\n receivers:\n - uid: b9bf06f8-bde2-4438-9d4a-bba0522dcd4d\n type: pagerduty\n settings:\n client: some client\n integrationKey: '[REDACTED]'\n severity: criticalish\n disableResolveMessage: false\n - orgId: 1\n name: slack test\n receivers:\n - uid: cbfd0976-8228-4126-b672-4419f30a9e50\n type: slack\n settings:\n text: title body test\n title: title test\n url: '[REDACTED]'\n disableResolveMessage: true\n"
|
||||
t.Run("decrypt false", func(t *testing.T) {
|
||||
env := createTestEnv(t, testContactPointConfig)
|
||||
sut := createProvisioningSrvSutFromEnv(t, &env)
|
||||
@@ -1745,7 +1745,7 @@ func TestProvisioningApiContactPointExport(t *testing.T) {
|
||||
|
||||
response := sut.RouteGetContactPointsExport(&rc)
|
||||
|
||||
expectedResponse := "apiVersion: 1\ncontactPoints:\n - orgId: 1\n name: multiple integrations\n receivers:\n - uid: c2090fda-f824-4add-b545-5a4d5c2ef082\n type: prometheus-alertmanager\n settings:\n basicAuthPassword: '[REDACTED]'\n basicAuthUser: test\n url: http://localhost:9093\n disableResolveMessage: true\n - uid: c84539ec-f87e-4fc5-9a91-7a687d34bbd1\n type: discord\n settings:\n avatar_url: some avatar\n url: some url\n use_discord_username: true\n disableResolveMessage: false\n"
|
||||
expectedResponse := "apiVersion: 1\ncontactPoints:\n - orgId: 1\n name: multiple integrations\n receivers:\n - uid: c2090fda-f824-4add-b545-5a4d5c2ef082\n type: prometheus-alertmanager\n settings:\n basicAuthPassword: '[REDACTED]'\n basicAuthUser: test\n url: http://localhost:9093\n disableResolveMessage: true\n - uid: c84539ec-f87e-4fc5-9a91-7a687d34bbd1\n type: discord\n settings:\n avatar_url: some avatar\n url: '[REDACTED]'\n use_discord_username: true\n disableResolveMessage: false\n"
|
||||
require.Equal(t, 200, response.Status())
|
||||
require.Equal(t, expectedResponse, string(response.Body()))
|
||||
})
|
||||
@@ -1897,6 +1897,7 @@ func createProvisioningSrvSutFromEnv(t *testing.T, env *testEnvironment) Provisi
|
||||
ac.NewReceiverAccess[*models.Receiver](env.ac, true),
|
||||
configStore,
|
||||
env.prov,
|
||||
env.store,
|
||||
env.secrets,
|
||||
env.xact,
|
||||
env.log,
|
||||
@@ -2301,10 +2302,11 @@ var testContactPointConfig = `
|
||||
"disableResolveMessage":false,
|
||||
"settings":{
|
||||
"avatar_url":"some avatar",
|
||||
"url":"some url",
|
||||
"use_discord_username":true
|
||||
},
|
||||
"secureSettings":{}
|
||||
"secureSettings":{
|
||||
"url":"some url"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -78,7 +78,6 @@ func (api *API) authorize(method, path string) web.Handler {
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets),
|
||||
)
|
||||
case http.MethodGet + "/api/v1/notifications/receivers/{Name}":
|
||||
// TODO: scope to :Name
|
||||
eval = ac.EvalAny(
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversRead),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets),
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
amConfig "github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
@@ -501,3 +502,56 @@ func ApiRecordFromModelRecord(r *models.Record) *definitions.Record {
|
||||
From: r.From,
|
||||
}
|
||||
}
|
||||
|
||||
func GettableGrafanaReceiverFromReceiver(r *models.Integration, provenance models.Provenance) (definitions.GettableGrafanaReceiver, error) {
|
||||
out := definitions.GettableGrafanaReceiver{
|
||||
UID: r.UID,
|
||||
Name: r.Name,
|
||||
Type: r.Config.Type,
|
||||
Provenance: definitions.Provenance(provenance),
|
||||
DisableResolveMessage: r.DisableResolveMessage,
|
||||
SecureFields: r.SecureFields(),
|
||||
}
|
||||
|
||||
if len(r.Settings) > 0 {
|
||||
jsonBytes, err := json.Marshal(r.Settings)
|
||||
if err != nil {
|
||||
return definitions.GettableGrafanaReceiver{}, err
|
||||
}
|
||||
out.Settings = jsonBytes
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func GettableApiReceiverFromReceiver(r *models.Receiver) (*definitions.GettableApiReceiver, error) {
|
||||
out := definitions.GettableApiReceiver{
|
||||
Receiver: amConfig.Receiver{
|
||||
Name: r.Name,
|
||||
},
|
||||
GettableGrafanaReceivers: definitions.GettableGrafanaReceivers{
|
||||
GrafanaManagedReceivers: make([]*definitions.GettableGrafanaReceiver, 0, len(r.Integrations)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, integration := range r.Integrations {
|
||||
gettable, err := GettableGrafanaReceiverFromReceiver(integration, r.Provenance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.GrafanaManagedReceivers = append(out.GrafanaManagedReceivers, &gettable)
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func GettableApiReceiversFromReceivers(recvs []*models.Receiver) ([]*definitions.GettableApiReceiver, error) {
|
||||
out := make([]*definitions.GettableApiReceiver, 0, len(recvs))
|
||||
for _, r := range recvs {
|
||||
gettables, err := GettableApiReceiverFromReceiver(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, gettables)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user