Alerting: Migrate to integration schema (#111643)
* update tests to assert against snapshot * remove channel_config package replaced by schemas from alerting module * update references to use new schema
This commit is contained in:
@@ -13,8 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
||||
"github.com/grafana/alerting/receivers/schema"
|
||||
)
|
||||
|
||||
// GetReceiverQuery represents a query for a single receiver.
|
||||
@@ -228,30 +227,35 @@ func (f IntegrationFieldPath) With(segment string) IntegrationFieldPath {
|
||||
// IntegrationConfig - The integration configuration
|
||||
// error - Error if integration type not found or invalid version specified
|
||||
func IntegrationConfigFromType(integrationType string, version *string) (IntegrationConfig, error) {
|
||||
versionConfig, err := channels_config.ConfigForIntegrationType(integrationType)
|
||||
if err != nil {
|
||||
return IntegrationConfig{}, err
|
||||
typeSchema, ok := alertingNotify.GetSchemaForIntegration(schema.IntegrationType(integrationType))
|
||||
if !ok {
|
||||
return IntegrationConfig{}, fmt.Errorf("integration type %s not found", integrationType)
|
||||
}
|
||||
// if particular version is requested and the version returned does not match, try to get the correct version
|
||||
if version != nil && *version != string(versionConfig.Version) {
|
||||
exists := false
|
||||
versionConfig, exists = versionConfig.Plugin.GetVersion(channels_config.NotifierVersion(*version))
|
||||
if !exists {
|
||||
return IntegrationConfig{}, fmt.Errorf("version %s not found in config", *version)
|
||||
}
|
||||
if version == nil {
|
||||
return IntegrationConfigFromSchema(typeSchema, typeSchema.CurrentVersion)
|
||||
}
|
||||
return IntegrationConfigFromSchema(typeSchema, schema.Version(*version))
|
||||
}
|
||||
|
||||
// IntegrationConfigFromSchema returns an integration configuration for a given version of the integration type schema.
|
||||
// Returns an error if the schema does not have such version
|
||||
func IntegrationConfigFromSchema(typeSchema schema.IntegrationTypeSchema, version schema.Version) (IntegrationConfig, error) {
|
||||
typeVersion, ok := typeSchema.GetVersion(version)
|
||||
if !ok {
|
||||
return IntegrationConfig{}, fmt.Errorf("version %s not found in config", version)
|
||||
}
|
||||
integrationConfig := IntegrationConfig{
|
||||
Type: versionConfig.Plugin.Type,
|
||||
Version: string(versionConfig.Version),
|
||||
Fields: make(map[string]IntegrationField, len(versionConfig.Options)),
|
||||
Type: string(typeSchema.Type),
|
||||
Version: string(typeVersion.Version),
|
||||
Fields: make(map[string]IntegrationField, len(typeVersion.Options)),
|
||||
}
|
||||
for _, option := range versionConfig.Options {
|
||||
for _, option := range typeVersion.Options {
|
||||
integrationConfig.Fields[option.PropertyName] = notifierOptionToIntegrationField(option)
|
||||
}
|
||||
return integrationConfig, nil
|
||||
}
|
||||
|
||||
func notifierOptionToIntegrationField(option channels_config.NotifierOption) IntegrationField {
|
||||
func notifierOptionToIntegrationField(option schema.Field) IntegrationField {
|
||||
f := IntegrationField{
|
||||
Name: option.PropertyName,
|
||||
Secure: option.Secure,
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"testing"
|
||||
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers/schema"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
@@ -41,14 +40,14 @@ func TestReceiver_EncryptDecrypt(t *testing.T) {
|
||||
encryptFn := Base64Enrypt
|
||||
decryptnFn := Base64Decrypt
|
||||
// Test that all known integration types encrypt and decrypt their secrets.
|
||||
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
|
||||
t.Run(integrationType, func(t *testing.T) {
|
||||
for it := range alertingNotify.AllKnownConfigsForTesting {
|
||||
integrationType := schema.IntegrationType(it)
|
||||
t.Run(string(integrationType), func(t *testing.T) {
|
||||
decrypedIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
|
||||
|
||||
encrypted := decrypedIntegration.Clone()
|
||||
secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1)
|
||||
assert.NoError(t, err)
|
||||
for _, key := range secrets {
|
||||
typeVersion, ok := alertingNotify.GetSchemaVersionForIntegration(integrationType, schema.V1)
|
||||
require.True(t, ok)
|
||||
for _, key := range typeVersion.GetSecretFieldsPaths() {
|
||||
val, ok, err := extractField(encrypted.Settings, NewIntegrationFieldPath(key))
|
||||
assert.NoError(t, err)
|
||||
if ok {
|
||||
@@ -59,7 +58,7 @@ func TestReceiver_EncryptDecrypt(t *testing.T) {
|
||||
}
|
||||
|
||||
testIntegration := decrypedIntegration.Clone()
|
||||
err = testIntegration.Encrypt(encryptFn)
|
||||
err := testIntegration.Encrypt(encryptFn)
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, encrypted, testIntegration)
|
||||
|
||||
@@ -77,14 +76,15 @@ func TestIntegration_Redact(t *testing.T) {
|
||||
return "TESTREDACTED"
|
||||
}
|
||||
// Test that all known integration types redact their secrets.
|
||||
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
|
||||
t.Run(integrationType, func(t *testing.T) {
|
||||
for it := range alertingNotify.AllKnownConfigsForTesting {
|
||||
integrationType := schema.IntegrationType(it)
|
||||
t.Run(string(integrationType), func(t *testing.T) {
|
||||
validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
|
||||
|
||||
expected := validIntegration.Clone()
|
||||
secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1)
|
||||
assert.NoError(t, err)
|
||||
for _, key := range secrets {
|
||||
version, ok := alertingNotify.GetSchemaVersionForIntegration(integrationType, schema.V1)
|
||||
require.True(t, ok)
|
||||
for _, key := range version.GetSecretFieldsPaths() {
|
||||
err := setField(expected.Settings, NewIntegrationFieldPath(key), func(current any) any {
|
||||
if s, isString := current.(string); isString && s != "" {
|
||||
delete(expected.SecureSettings, key)
|
||||
@@ -106,8 +106,9 @@ func TestIntegration_Validate(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
// Test that all known integration types are valid.
|
||||
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
|
||||
t.Run(integrationType, func(t *testing.T) {
|
||||
for it := range alertingNotify.AllKnownConfigsForTesting {
|
||||
integrationType := schema.IntegrationType(it)
|
||||
t.Run(string(integrationType), func(t *testing.T) {
|
||||
validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
|
||||
assert.NoError(t, validIntegration.Encrypt(Base64Enrypt))
|
||||
assert.NoErrorf(t, validIntegration.Validate(Base64Decrypt), "integration should be valid")
|
||||
@@ -241,19 +242,19 @@ func TestIntegration_WithExistingSecureFields(t *testing.T) {
|
||||
|
||||
func TestSecretsIntegrationConfig(t *testing.T) {
|
||||
// Test that all known integration types have a config and correctly mark their secrets as secure.
|
||||
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
|
||||
t.Run(integrationType, func(t *testing.T) {
|
||||
config, err := IntegrationConfigFromType(integrationType, nil)
|
||||
for it := range alertingNotify.AllKnownConfigsForTesting {
|
||||
integrationType := schema.IntegrationType(it)
|
||||
t.Run(string(integrationType), func(t *testing.T) {
|
||||
schemaType, ok := alertingNotify.GetSchemaForIntegration(integrationType)
|
||||
require.True(t, ok)
|
||||
|
||||
config, err := IntegrationConfigFromSchema(schemaType, schema.V1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("v1 is current", func(t *testing.T) {
|
||||
configv1, err := IntegrationConfigFromType(integrationType, util.Pointer(string(channels_config.V1)))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, config, configv1)
|
||||
})
|
||||
version, ok := schemaType.GetVersion(schema.V1)
|
||||
require.True(t, ok)
|
||||
|
||||
secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1)
|
||||
assert.NoError(t, err)
|
||||
secrets := version.GetSecretFieldsPaths()
|
||||
allSecrets := make(map[string]struct{}, len(secrets))
|
||||
for _, key := range secrets {
|
||||
allSecrets[key] = struct{}{}
|
||||
@@ -270,17 +271,12 @@ func TestSecretsIntegrationConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Unknown type returns error", func(t *testing.T) {
|
||||
_, err := IntegrationConfigFromType("__--**unknown_type**--__", nil)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Unknown version returns error", func(t *testing.T) {
|
||||
version := util.Pointer("__--**unknown_version**--__")
|
||||
types := maps.Keys(alertingNotify.AllKnownConfigsForTesting)
|
||||
for itype := range types {
|
||||
_, err := IntegrationConfigFromType(itype, version)
|
||||
assert.Errorf(t, err, "unknown version for integration type %s did not return error but should", itype)
|
||||
for s := range maps.Keys(alertingNotify.AllKnownConfigsForTesting) {
|
||||
schemaType, _ := alertingNotify.GetSchemaForIntegration(schema.IntegrationType(s))
|
||||
_, err := IntegrationConfigFromSchema(schemaType, "unknown")
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -289,8 +285,9 @@ func TestIntegration_SecureFields(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
// Test that all known integration types have a config and correctly mark their secrets as secure.
|
||||
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
|
||||
t.Run(integrationType, func(t *testing.T) {
|
||||
for it := range alertingNotify.AllKnownConfigsForTesting {
|
||||
integrationType := schema.IntegrationType(it)
|
||||
t.Run(string(integrationType), func(t *testing.T) {
|
||||
t.Run("contains SecureSettings", func(t *testing.T) {
|
||||
validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
|
||||
expected := make(map[string]bool, len(validIntegration.SecureSettings))
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/google/uuid"
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers/schema"
|
||||
"github.com/grafana/alerting/receivers/webex"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
@@ -1209,7 +1211,7 @@ func (n ReceiverMutators) WithProvenance(provenance Provenance) Mutator[Receiver
|
||||
}
|
||||
}
|
||||
|
||||
func (n ReceiverMutators) WithValidIntegration(integrationType string) Mutator[Receiver] {
|
||||
func (n ReceiverMutators) WithValidIntegration(integrationType schema.IntegrationType) Mutator[Receiver] {
|
||||
return func(r *Receiver) {
|
||||
// TODO add support for v0
|
||||
integration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
|
||||
@@ -1217,7 +1219,7 @@ func (n ReceiverMutators) WithValidIntegration(integrationType string) Mutator[R
|
||||
}
|
||||
}
|
||||
|
||||
func (n ReceiverMutators) WithInvalidIntegration(integrationType string) Mutator[Receiver] {
|
||||
func (n ReceiverMutators) WithInvalidIntegration(integrationType schema.IntegrationType) Mutator[Receiver] {
|
||||
return func(r *Receiver) {
|
||||
// TODO add support for v0
|
||||
integration := IntegrationGen(IntegrationMuts.WithInvalidConfig(integrationType))()
|
||||
@@ -1278,7 +1280,7 @@ func IntegrationGen(mutators ...Mutator[Integration]) func() Integration {
|
||||
SecureSettings: make(map[string]string),
|
||||
}
|
||||
|
||||
IntegrationMuts.WithValidConfig(randomIntegrationType)(&c)
|
||||
IntegrationMuts.WithValidConfig(schema.IntegrationType(randomIntegrationType))(&c)
|
||||
|
||||
for _, mutator := range mutators {
|
||||
mutator(&c)
|
||||
@@ -1312,11 +1314,12 @@ func (n IntegrationMutators) WithName(name string) Mutator[Integration] {
|
||||
}
|
||||
}
|
||||
|
||||
func (n IntegrationMutators) WithValidConfig(integrationType string) Mutator[Integration] {
|
||||
func (n IntegrationMutators) WithValidConfig(integrationType schema.IntegrationType) Mutator[Integration] {
|
||||
return func(c *Integration) {
|
||||
// TODO add support for v0 integrations
|
||||
config := alertingNotify.AllKnownConfigsForTesting[integrationType].GetRawNotifierConfig(c.Name)
|
||||
integrationConfig, _ := IntegrationConfigFromType(integrationType, nil)
|
||||
config := alertingNotify.AllKnownConfigsForTesting[string(integrationType)].GetRawNotifierConfig(c.Name)
|
||||
typeSchema, _ := alertingNotify.GetSchemaForIntegration(integrationType)
|
||||
integrationConfig, _ := IntegrationConfigFromSchema(typeSchema, schema.V1)
|
||||
c.Config = integrationConfig
|
||||
|
||||
var settings map[string]any
|
||||
@@ -1332,13 +1335,13 @@ func (n IntegrationMutators) WithValidConfig(integrationType string) Mutator[Int
|
||||
}
|
||||
}
|
||||
|
||||
func (n IntegrationMutators) WithInvalidConfig(integrationType string) Mutator[Integration] {
|
||||
func (n IntegrationMutators) WithInvalidConfig(integrationType schema.IntegrationType) Mutator[Integration] {
|
||||
return func(c *Integration) {
|
||||
integrationConfig, _ := IntegrationConfigFromType(integrationType, nil)
|
||||
c.Config = integrationConfig
|
||||
typeSchema, _ := alertingNotify.GetSchemaForIntegration(integrationType)
|
||||
c.Config, _ = IntegrationConfigFromSchema(typeSchema, schema.V1)
|
||||
c.Settings = map[string]interface{}{}
|
||||
c.SecureSettings = map[string]string{}
|
||||
if integrationType == "webex" {
|
||||
if integrationType == webex.Type {
|
||||
// Webex passes validation without any settings but should fail with an unparsable URL.
|
||||
c.Settings["api_url"] = "(*^$*^%!@#$*()"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user