mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 13:22:21 +08:00

* update RenameReceiverInNotificationSettings in DbStore to check for provisioning * implement renaming in receiver service and provisioning * do not patch route when stitching * fix bug in stitching because it returned new name but the old one was expected * update receiver service to always return result converted from storage model this makes sure that UID and version are consistent with GET\LIST operations * use provided metadata.name for UID of domain model because rename changes UID and request fails * remove rename guard * update UI to not disable receiver name when k8s api enabled * create should calculate uid from name because new receiver does not have UID yet.
244 lines
9.1 KiB
Go
244 lines
9.1 KiB
Go
package provisioning
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
mock "github.com/stretchr/testify/mock"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
apimodels "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/notifier"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
|
)
|
|
|
|
const defaultAlertmanagerConfigJSON = `
|
|
{
|
|
"template_files": null,
|
|
"alertmanager_config": {
|
|
"route": {
|
|
"receiver": "grafana-default-email",
|
|
"group_by": [
|
|
"..."
|
|
],
|
|
"routes": [{
|
|
"receiver": "grafana-default-email",
|
|
"object_matchers": [["a", "=", "b"]]
|
|
}]
|
|
},
|
|
"templates": null,
|
|
"receivers": [{
|
|
"name": "grafana-default-email",
|
|
"grafana_managed_receiver_configs": [{
|
|
"uid": "UID1",
|
|
"name": "grafana-default-email",
|
|
"type": "email",
|
|
"disableResolveMessage": false,
|
|
"settings": {
|
|
"addresses": "\u003cexample@email.com\u003e"
|
|
},
|
|
"secureFields": {}
|
|
}]
|
|
}, {
|
|
"name": "slack receiver",
|
|
"grafana_managed_receiver_configs": [{
|
|
"uid": "UID2",
|
|
"name": "slack receiver",
|
|
"type": "slack",
|
|
"disableResolveMessage": false,
|
|
"settings": {},
|
|
"secureSettings": {"url":"secure url"}
|
|
}]
|
|
}]
|
|
}
|
|
}
|
|
`
|
|
|
|
type NopTransactionManager struct{}
|
|
|
|
func newNopTransactionManager() *NopTransactionManager {
|
|
return &NopTransactionManager{}
|
|
}
|
|
|
|
func assertInTransaction(t *testing.T, ctx context.Context) {
|
|
assert.Truef(t, ctx.Value(NopTransactionManager{}) != nil, "Expected to be executed in transaction but there is none")
|
|
}
|
|
|
|
func (n *NopTransactionManager) InTransaction(ctx context.Context, work func(ctx context.Context) error) error {
|
|
return work(context.WithValue(ctx, NopTransactionManager{}, struct{}{}))
|
|
}
|
|
|
|
func (m *MockProvisioningStore_Expecter) GetReturns(p models.Provenance) *MockProvisioningStore_Expecter {
|
|
m.GetProvenance(mock.Anything, mock.Anything, mock.Anything).Return(p, nil)
|
|
m.GetProvenances(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
|
|
return m
|
|
}
|
|
|
|
func (m *MockProvisioningStore_Expecter) SaveSucceeds() *MockProvisioningStore_Expecter {
|
|
m.SetProvenance(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
m.DeleteProvenance(mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
return m
|
|
}
|
|
|
|
func (m *MockQuotaChecker_Expecter) LimitOK() *MockQuotaChecker_Expecter {
|
|
m.CheckQuotaReached(mock.Anything, mock.Anything, mock.Anything).Return(false, nil)
|
|
return m
|
|
}
|
|
|
|
func (m *MockQuotaChecker_Expecter) LimitExceeded() *MockQuotaChecker_Expecter {
|
|
m.CheckQuotaReached(mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
return m
|
|
}
|
|
|
|
type NotificationSettingsValidatorProviderFake struct {
|
|
}
|
|
|
|
func (n *NotificationSettingsValidatorProviderFake) Validator(ctx context.Context, orgID int64) (notifier.NotificationSettingsValidator, error) {
|
|
return notifier.NoValidation{}, nil
|
|
}
|
|
|
|
type call struct {
|
|
Method string
|
|
Args []interface{}
|
|
}
|
|
|
|
type fakeRuleAccessControlService struct {
|
|
mu sync.Mutex
|
|
Calls []call
|
|
AuthorizeAccessToRuleGroupFunc func(ctx context.Context, user identity.Requester, rules models.RulesGroup) error
|
|
AuthorizeAccessInFolderFunc func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error
|
|
AuthorizeRuleChangesFunc func(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
|
|
CanReadAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)
|
|
CanWriteAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)
|
|
}
|
|
|
|
func (s *fakeRuleAccessControlService) RecordCall(method string, args ...interface{}) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
call := call{
|
|
Method: method,
|
|
Args: args,
|
|
}
|
|
|
|
s.Calls = append(s.Calls, call)
|
|
}
|
|
|
|
func (s *fakeRuleAccessControlService) AuthorizeRuleGroupRead(ctx context.Context, user identity.Requester, rules models.RulesGroup) error {
|
|
s.RecordCall("AuthorizeRuleGroupRead", ctx, user, rules)
|
|
if s.AuthorizeAccessToRuleGroupFunc != nil {
|
|
return s.AuthorizeAccessToRuleGroupFunc(ctx, user, rules)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *fakeRuleAccessControlService) AuthorizeRuleRead(ctx context.Context, user identity.Requester, rule *models.AlertRule) error {
|
|
s.RecordCall("AuthorizeRuleRead", ctx, user, rule)
|
|
if s.AuthorizeAccessInFolderFunc != nil {
|
|
return s.AuthorizeAccessInFolderFunc(ctx, user, rule)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *fakeRuleAccessControlService) AuthorizeRuleGroupWrite(ctx context.Context, user identity.Requester, change *store.GroupDelta) error {
|
|
s.RecordCall("AuthorizeRuleGroupWrite", ctx, user, change)
|
|
if s.AuthorizeRuleChangesFunc != nil {
|
|
return s.AuthorizeRuleChangesFunc(ctx, user, change)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *fakeRuleAccessControlService) CanReadAllRules(ctx context.Context, user identity.Requester) (bool, error) {
|
|
s.RecordCall("CanReadAllRules", ctx, user)
|
|
if s.CanReadAllRulesFunc != nil {
|
|
return s.CanReadAllRulesFunc(ctx, user)
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (s *fakeRuleAccessControlService) CanWriteAllRules(ctx context.Context, user identity.Requester) (bool, error) {
|
|
s.RecordCall("CanWriteAllRules", ctx, user)
|
|
if s.CanWriteAllRulesFunc != nil {
|
|
return s.CanWriteAllRulesFunc(ctx, user)
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
type fakeAlertRuleNotificationStore struct {
|
|
Calls []call
|
|
|
|
RenameReceiverInNotificationSettingsFn func(ctx context.Context, orgID int64, oldReceiver, newReceiver string, validateProvenance func(models.Provenance) bool, dryRun bool) ([]models.AlertRuleKey, []models.AlertRuleKey, error)
|
|
RenameTimeIntervalInNotificationSettingsFn func(ctx context.Context, orgID int64, old, new string, validate func(models.Provenance) bool, dryRun bool) ([]models.AlertRuleKey, []models.AlertRuleKey, error)
|
|
ListNotificationSettingsFn func(ctx context.Context, q models.ListNotificationSettingsQuery) (map[models.AlertRuleKey][]models.NotificationSettings, error)
|
|
}
|
|
|
|
func (f *fakeAlertRuleNotificationStore) RenameReceiverInNotificationSettings(ctx context.Context, orgID int64, oldReceiver, newReceiver string, validateProvenance func(models.Provenance) bool, dryRun bool) ([]models.AlertRuleKey, []models.AlertRuleKey, error) {
|
|
call := call{
|
|
Method: "RenameReceiverInNotificationSettings",
|
|
Args: []interface{}{ctx, orgID, oldReceiver, newReceiver, validateProvenance, dryRun},
|
|
}
|
|
f.Calls = append(f.Calls, call)
|
|
|
|
if f.RenameReceiverInNotificationSettingsFn != nil {
|
|
return f.RenameReceiverInNotificationSettingsFn(ctx, orgID, oldReceiver, newReceiver, validateProvenance, dryRun)
|
|
}
|
|
|
|
// Default values when no function hook is provided
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (f *fakeAlertRuleNotificationStore) RenameTimeIntervalInNotificationSettings(ctx context.Context, orgID int64, oldTimeInterval, newTimeInterval string, validate func(models.Provenance) bool, dryRun bool) ([]models.AlertRuleKey, []models.AlertRuleKey, error) {
|
|
call := call{
|
|
Method: "RenameTimeIntervalInNotificationSettings",
|
|
Args: []interface{}{ctx, orgID, oldTimeInterval, newTimeInterval, validate, dryRun},
|
|
}
|
|
f.Calls = append(f.Calls, call)
|
|
|
|
if f.RenameTimeIntervalInNotificationSettingsFn != nil {
|
|
return f.RenameTimeIntervalInNotificationSettingsFn(ctx, orgID, oldTimeInterval, newTimeInterval, validate, dryRun)
|
|
}
|
|
|
|
// Default values when no function hook is provided
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (f *fakeAlertRuleNotificationStore) ListNotificationSettings(ctx context.Context, q models.ListNotificationSettingsQuery) (map[models.AlertRuleKey][]models.NotificationSettings, error) {
|
|
call := call{
|
|
Method: "ListNotificationSettings",
|
|
Args: []interface{}{ctx, q},
|
|
}
|
|
f.Calls = append(f.Calls, call)
|
|
|
|
if f.ListNotificationSettingsFn != nil {
|
|
return f.ListNotificationSettingsFn(ctx, q)
|
|
}
|
|
|
|
// Default values when no function hook is provided
|
|
return nil, nil
|
|
}
|
|
|
|
type fakeReceiverService struct {
|
|
Calls []call
|
|
GetReceiversFunc func(ctx context.Context, query models.GetReceiversQuery, user identity.Requester) ([]*models.Receiver, error)
|
|
RenameReceiverInDependentResourcesFunc func(ctx context.Context, orgID int64, route *apimodels.Route, oldName, newName string, receiverProvenance models.Provenance) error
|
|
}
|
|
|
|
func (f *fakeReceiverService) GetReceivers(ctx context.Context, query models.GetReceiversQuery, user identity.Requester) ([]*models.Receiver, error) {
|
|
f.Calls = append(f.Calls, call{Method: "GetReceivers", Args: []interface{}{ctx, query, user}})
|
|
if f.GetReceiversFunc != nil {
|
|
return f.GetReceiversFunc(ctx, query, user)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (f *fakeReceiverService) RenameReceiverInDependentResources(ctx context.Context, orgID int64, route *apimodels.Route, oldName, newName string, receiverProvenance models.Provenance) error {
|
|
f.Calls = append(f.Calls, call{Method: "RenameReceiverInDependentResources", Args: []interface{}{ctx, orgID, route, oldName, newName, receiverProvenance}})
|
|
if f.RenameReceiverInDependentResourcesFunc != nil {
|
|
return f.RenameReceiverInDependentResourcesFunc(ctx, orgID, route, oldName, newName, receiverProvenance)
|
|
}
|
|
return nil
|
|
}
|