Alerting: Remove vendored models in migration service (#74503)

This PR replaces the vendored models in the migration with their equivalent ngalert models. It also replaces the raw SQL selects and inserts with service calls.

It also fills in some gaps in the testing suite around:

    - Migration of alert rules: verifying that the actual data model (queries, conditions) are correct 9a7cfa9
    - Secure settings migration: verifying that secure fields remain encrypted for all available notifiers and certain fields migrate from plain text to encrypted secure settings correctly e7d3993

Replacing the checks for custom dashboard ACLs will be replaced in a separate targeted PR as it will be complex enough alone.
This commit is contained in:
Matthew Jacobson
2023-10-11 17:22:09 +01:00
committed by GitHub
parent 046e9b7672
commit 6a8649d544
49 changed files with 4564 additions and 3647 deletions

View File

@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
"github.com/grafana/grafana/pkg/services/secrets/database"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/setting"
@@ -37,7 +38,7 @@ func setupAMTest(t *testing.T) *alertmanager {
DashboardService: dashboards.NewFakeDashboardService(t),
}
kvStore := NewFakeKVStore(t)
kvStore := fakes.NewFakeKVStore(t)
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
decryptFn := secretsService.GetDecryptedValue
am, err := newAlertmanager(context.Background(), 1, cfg, s, kvStore, &NilPeer{}, decryptFn, nil, m)

View File

@@ -1,6 +1,7 @@
package channels_config
import (
"fmt"
"os"
alertingOpsgenie "github.com/grafana/alerting/receivers/opsgenie"
@@ -1340,3 +1341,20 @@ func GetAvailableNotifiers() []*NotifierPlugin {
},
}
}
// GetSecretKeysForContactPointType returns settings keys of contact point of the given type that are expected to be secrets. Returns error is contact point type is not known.
func GetSecretKeysForContactPointType(contactPointType string) ([]string, error) {
notifiers := GetAvailableNotifiers()
for _, n := range notifiers {
if n.Type == contactPointType {
var secureFields []string
for _, field := range n.Options {
if field.Secure {
secureFields = append(secureFields, field.PropertyName)
}
}
return secureFields, nil
}
}
return nil, fmt.Errorf("no secrets configured for type '%s'", contactPointType)
}

View File

@@ -7,10 +7,12 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
)
func TestFileStore_FilepathFor_DirectoryNotExist(t *testing.T) {
store := NewFakeKVStore(t)
store := fakes.NewFakeKVStore(t)
workingDir := filepath.Join(t.TempDir(), "notexistdir")
fs := NewFileStore(1, store, workingDir)
filekey := "silences"
@@ -31,7 +33,7 @@ func TestFileStore_FilepathFor_DirectoryNotExist(t *testing.T) {
}
}
func TestFileStore_FilepathFor(t *testing.T) {
store := NewFakeKVStore(t)
store := fakes.NewFakeKVStore(t)
workingDir := t.TempDir()
fs := NewFileStore(1, store, workingDir)
filekey := "silences"
@@ -73,7 +75,7 @@ func TestFileStore_FilepathFor(t *testing.T) {
}
func TestFileStore_Persist(t *testing.T) {
store := NewFakeKVStore(t)
store := fakes.NewFakeKVStore(t)
state := &fakeState{data: "something to marshal"}
workingDir := t.TempDir()
fs := NewFileStore(1, store, workingDir)
@@ -82,9 +84,9 @@ func TestFileStore_Persist(t *testing.T) {
size, err := fs.Persist(context.Background(), filekey, state)
require.NoError(t, err)
require.Equal(t, int64(20), size)
store.mtx.Lock()
require.Len(t, store.store, 1)
store.mtx.Unlock()
store.Mtx.Lock()
require.Len(t, store.Store, 1)
store.Mtx.Unlock()
v, ok, err := store.Get(context.Background(), 1, KVNamespace, filekey)
require.NoError(t, err)
require.True(t, ok)

View File

@@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
"github.com/grafana/grafana/pkg/services/ngalert/store"
ngfakes "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/setting"
@@ -31,7 +32,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgs(t *testing.T) {
}
tmpDir := t.TempDir()
kvStore := NewFakeKVStore(t)
kvStore := ngfakes.NewFakeKVStore(t)
provStore := provisioning.NewFakeProvisioningStore()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
@@ -165,7 +166,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
}
tmpDir := t.TempDir()
kvStore := NewFakeKVStore(t)
kvStore := ngfakes.NewFakeKVStore(t)
provStore := provisioning.NewFakeProvisioningStore()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
@@ -259,7 +260,7 @@ func TestMultiOrgAlertmanager_AlertmanagerFor(t *testing.T) {
DataPath: tmpDir,
UnifiedAlerting: setting.UnifiedAlertingSettings{AlertmanagerConfigPollInterval: 3 * time.Minute, DefaultConfiguration: setting.GetAlertmanagerDefaultConfiguration()}, // do not poll in tests.
}
kvStore := NewFakeKVStore(t)
kvStore := ngfakes.NewFakeKVStore(t)
provStore := provisioning.NewFakeProvisioningStore()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
@@ -310,7 +311,7 @@ func TestMultiOrgAlertmanager_ActivateHistoricalConfiguration(t *testing.T) {
DataPath: tmpDir,
UnifiedAlerting: setting.UnifiedAlertingSettings{AlertmanagerConfigPollInterval: 3 * time.Minute, DefaultConfiguration: defaultConfig}, // do not poll in tests.
}
kvStore := NewFakeKVStore(t)
kvStore := ngfakes.NewFakeKVStore(t)
provStore := provisioning.NewFakeProvisioningStore()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue

View File

@@ -5,12 +5,9 @@ import (
"crypto/md5"
"errors"
"fmt"
"strings"
"sync"
"testing"
"time"
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
@@ -195,98 +192,6 @@ func (f *FakeOrgStore) GetOrgs(_ context.Context) ([]int64, error) {
return f.orgs, nil
}
type FakeKVStore struct {
mtx sync.Mutex
store map[int64]map[string]map[string]string
}
func NewFakeKVStore(t *testing.T) *FakeKVStore {
t.Helper()
return &FakeKVStore{
store: map[int64]map[string]map[string]string{},
}
}
func (fkv *FakeKVStore) Get(_ context.Context, orgId int64, namespace string, key string) (string, bool, error) {
fkv.mtx.Lock()
defer fkv.mtx.Unlock()
org, ok := fkv.store[orgId]
if !ok {
return "", false, nil
}
k, ok := org[namespace]
if !ok {
return "", false, nil
}
v, ok := k[key]
if !ok {
return "", false, nil
}
return v, true, nil
}
func (fkv *FakeKVStore) Set(_ context.Context, orgId int64, namespace string, key string, value string) error {
fkv.mtx.Lock()
defer fkv.mtx.Unlock()
org, ok := fkv.store[orgId]
if !ok {
fkv.store[orgId] = map[string]map[string]string{}
}
_, ok = org[namespace]
if !ok {
fkv.store[orgId][namespace] = map[string]string{}
}
fkv.store[orgId][namespace][key] = value
return nil
}
func (fkv *FakeKVStore) Del(_ context.Context, orgId int64, namespace string, key string) error {
fkv.mtx.Lock()
defer fkv.mtx.Unlock()
org, ok := fkv.store[orgId]
if !ok {
return nil
}
_, ok = org[namespace]
if !ok {
return nil
}
delete(fkv.store[orgId][namespace], key)
return nil
}
func (fkv *FakeKVStore) Keys(ctx context.Context, orgID int64, namespace string, keyPrefix string) ([]kvstore.Key, error) {
fkv.mtx.Lock()
defer fkv.mtx.Unlock()
var keys []kvstore.Key
for orgIDFromStore, namespaceMap := range fkv.store {
if orgID != kvstore.AllOrganizations && orgID != orgIDFromStore {
continue
}
if keyMap, exists := namespaceMap[namespace]; exists {
for k := range keyMap {
if strings.HasPrefix(k, keyPrefix) {
keys = append(keys, kvstore.Key{
OrgId: orgIDFromStore,
Namespace: namespace,
Key: keyPrefix,
})
}
}
}
}
return keys, nil
}
func (fkv *FakeKVStore) GetAll(ctx context.Context, orgId int64, namespace string) (map[int64]map[string]string, error) {
return nil, nil
}
type fakeState struct {
data string
}