The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/services/ngalert/models/receivers_test.go

411 lines
14 KiB

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
10 months ago
package models
import (
"reflect"
"testing"
alertingNotify "github.com/grafana/alerting/notify"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
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
10 months ago
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
)
func TestReceiver_Clone(t *testing.T) {
testCases := []struct {
name string
receiver Receiver
}{
{name: "empty receiver", receiver: Receiver{}},
{name: "empty integration", receiver: Receiver{Integrations: []*Integration{{Config: IntegrationConfig{}}}}},
{name: "random receiver", receiver: ReceiverGen()()},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
receiverClone := tc.receiver.Clone()
assert.Equal(t, tc.receiver, receiverClone)
for _, integration := range tc.receiver.Integrations {
integrationClone := integration.Clone()
assert.Equal(t, *integration, integrationClone)
}
})
}
}
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) {
decrypedIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
encrypted := decrypedIntegration.Clone()
secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType)
assert.NoError(t, err)
for _, key := range secrets {
val, ok, err := extractField(encrypted.Settings, NewIntegrationFieldPath(key))
assert.NoError(t, err)
if ok {
encryptedVal, err := encryptFn(val)
assert.NoError(t, err)
encrypted.SecureSettings[key] = encryptedVal
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
10 months ago
}
}
testIntegration := decrypedIntegration.Clone()
err = testIntegration.Encrypt(encryptFn)
assert.NoError(t, err)
require.Equal(t, encrypted, testIntegration)
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
10 months ago
err = testIntegration.Decrypt(decryptnFn)
assert.NoError(t, err)
assert.Equal(t, decrypedIntegration, testIntegration)
})
}
}
func TestIntegration_Redact(t *testing.T) {
redactFn := func(key string) string {
return "TESTREDACTED"
}
// Test that all known integration types redact their secrets.
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
t.Run(integrationType, func(t *testing.T) {
validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
expected := validIntegration.Clone()
secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType)
assert.NoError(t, err)
for _, key := range secrets {
err := setField(expected.Settings, NewIntegrationFieldPath(key), func(current any) any {
if s, isString := current.(string); isString && s != "" {
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
10 months ago
delete(expected.SecureSettings, key)
return redactFn(s)
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
10 months ago
}
return current
}, true)
require.NoError(t, err)
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
10 months ago
}
validIntegration.Redact(redactFn)
assert.Equal(t, expected, validIntegration)
})
}
}
func TestIntegration_Validate(t *testing.T) {
// Test that all known integration types are valid.
for integrationType := range alertingNotify.AllKnownConfigsForTesting {
t.Run(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")
invalidIntegration := IntegrationGen(IntegrationMuts.WithInvalidConfig(integrationType))()
assert.NoError(t, invalidIntegration.Encrypt(Base64Enrypt))
assert.Errorf(t, invalidIntegration.Validate(Base64Decrypt), "integration should be invalid")
})
}
}
func TestIntegration_WithExistingSecureFields(t *testing.T) {
// Test that WithExistingSecureFields will copy over the secure fields from the existing integration.
testCases := []struct {
name string
integration Integration
secureFields []string
existing Integration
expected Integration
}{
{
name: "test receiver",
integration: Integration{
SecureSettings: map[string]string{
"f1": "newVal1",
"f2": "newVal2",
"f3": "newVal3",
"f5": "newVal5",
},
},
secureFields: []string{"f2", "f4", "f5"},
existing: Integration{
SecureSettings: map[string]string{
"f1": "oldVal1",
"f2": "oldVal2",
"f3": "oldVal3",
"f4": "oldVal4",
},
},
expected: Integration{
SecureSettings: map[string]string{
"f1": "newVal1",
"f2": "oldVal2",
"f3": "newVal3",
"f4": "oldVal4",
},
},
},
{
name: "Integration[exists], SecureFields[true], Existing[exists]: old value",
integration: Integration{
SecureSettings: map[string]string{"f1": "newVal1"},
},
secureFields: []string{"f1"},
existing: Integration{SecureSettings: map[string]string{"f1": "oldVal1"}},
expected: Integration{SecureSettings: map[string]string{"f1": "oldVal1"}},
},
{
name: "Integration[exists], SecureFields[true], Existing[missing]: no value",
integration: Integration{
SecureSettings: map[string]string{"f1": "newVal1"},
},
secureFields: []string{"f1"},
existing: Integration{SecureSettings: map[string]string{}},
expected: Integration{SecureSettings: map[string]string{}},
},
{
name: "Integration[exists], SecureFields[false], Existing[exists]: new value",
integration: Integration{
SecureSettings: map[string]string{"f1": "newVal1"},
},
existing: Integration{SecureSettings: map[string]string{"f1": "oldVal1"}},
expected: Integration{SecureSettings: map[string]string{"f1": "newVal1"}},
},
{
name: "Integration[exists], SecureFields[false], Existing[missing]: new value",
integration: Integration{
SecureSettings: map[string]string{"f1": "newVal1"},
},
existing: Integration{SecureSettings: map[string]string{}},
expected: Integration{SecureSettings: map[string]string{"f1": "newVal1"}},
},
{
name: "Integration[missing], SecureFields[true], Existing[exists]: old value",
integration: Integration{
SecureSettings: map[string]string{},
},
secureFields: []string{"f1"},
existing: Integration{SecureSettings: map[string]string{"f1": "oldVal1"}},
expected: Integration{SecureSettings: map[string]string{"f1": "oldVal1"}},
},
{
name: "Integration[missing], SecureFields[true], Existing[missing]: no value",
integration: Integration{
SecureSettings: map[string]string{},
},
secureFields: []string{"f1"},
existing: Integration{SecureSettings: map[string]string{}},
expected: Integration{SecureSettings: map[string]string{}},
},
{
name: "Integration[missing], SecureFields[false], Existing[exists]: no value",
integration: Integration{
SecureSettings: map[string]string{},
},
existing: Integration{SecureSettings: map[string]string{"f1": "oldVal1"}},
expected: Integration{SecureSettings: map[string]string{}},
},
{
name: "Integration[missing], SecureFields[false], Existing[missing]: no value",
integration: Integration{
SecureSettings: map[string]string{},
},
existing: Integration{SecureSettings: map[string]string{}},
expected: Integration{SecureSettings: map[string]string{}},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.integration.WithExistingSecureFields(&tc.existing, tc.secureFields)
assert.Equal(t, tc.expected, tc.integration)
})
}
}
func TestIntegrationConfig(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)
assert.NoError(t, err)
secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType)
assert.NoError(t, err)
allSecrets := make(map[string]struct{}, len(secrets))
for _, key := range secrets {
allSecrets[key] = struct{}{}
}
for field := range config.Fields {
_, isSecret := allSecrets[field]
assert.Equalf(t, isSecret, config.IsSecureField(NewIntegrationFieldPath(field)), "field '%s' is expected to be secret", field)
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
10 months ago
}
assert.False(t, config.IsSecureField(IntegrationFieldPath{"__--**unknown_field**--__"}))
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
10 months ago
})
}
t.Run("Unknown type returns error", func(t *testing.T) {
_, err := IntegrationConfigFromType("__--**unknown_type**--__")
assert.Error(t, err)
})
}
func TestIntegration_SecureFields(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) {
t.Run("contains SecureSettings", func(t *testing.T) {
validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
expected := make(map[string]bool, len(validIntegration.SecureSettings))
for _, path := range validIntegration.Config.GetSecretFields() {
if validIntegration.Config.IsSecureField(path) {
expected[path.String()] = true
validIntegration.SecureSettings[path.String()] = "test"
_, _, err := extractField(validIntegration.Settings, path)
require.NoError(t, err)
continue
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
10 months ago
}
}
assert.Equal(t, expected, validIntegration.SecureFields())
})
t.Run("contains secret Settings not in SecureSettings", func(t *testing.T) {
validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))()
expected := make(map[string]bool, len(validIntegration.SecureSettings))
for _, path := range validIntegration.Config.GetSecretFields() {
if validIntegration.Config.IsSecureField(path) {
expected[path.String()] = true
assert.NoError(t, setField(validIntegration.Settings, path, func(current any) any {
return "test"
}, false))
delete(validIntegration.SecureSettings, path.String())
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
10 months ago
}
}
assert.Equal(t, expected, validIntegration.SecureFields())
})
})
}
}
// This is a broken type that will error if marshalled.
type broken struct {
f1 string
}
func (b broken) MarshalJSON() ([]byte, error) {
return nil, assert.AnError
}
func TestReceiver_Fingerprint(t *testing.T) {
// Test that the fingerprint is stable.
im := IntegrationMuts
baseReceiver := ReceiverGen(ReceiverMuts.WithName("test receiver"), ReceiverMuts.WithIntegrations(
IntegrationGen(im.WithName("test receiver"), im.WithValidConfig("slack"))(),
))()
baseReceiver.Integrations[0].UID = "stable UID"
baseReceiver.Integrations[0].DisableResolveMessage = true
baseReceiver.Integrations[0].SecureSettings = map[string]string{"test2": "test2", "test3": "test223", "test1": "rest22"}
baseReceiver.Integrations[0].Settings["broken"] = broken{f1: "this"} // Add a broken type to ensure it is stable in the fingerprint.
baseReceiver.Integrations[0].Settings["sub-map"] = map[string]any{
"setting": "value",
"something": 123,
"data": []string{"test"},
} // Add a broken type to ensure it is stable in the fingerprint.
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
10 months ago
baseReceiver.Integrations[0].Config = IntegrationConfig{Type: baseReceiver.Integrations[0].Config.Type} // Remove all fields except Type.
completelyDifferentReceiver := ReceiverGen(ReceiverMuts.WithName("test receiver2"), ReceiverMuts.WithIntegrations(
IntegrationGen(im.WithName("test receiver2"), im.WithValidConfig("discord"))(),
))()
completelyDifferentReceiver.Integrations[0].UID = "stable UID2"
completelyDifferentReceiver.Integrations[0].DisableResolveMessage = false
completelyDifferentReceiver.Integrations[0].SecureSettings = map[string]string{"test": "test"}
completelyDifferentReceiver.Provenance = ProvenanceAPI
completelyDifferentReceiver.Integrations[0].Config = IntegrationConfig{Type: completelyDifferentReceiver.Integrations[0].Config.Type} // Remove all fields except Type.
t.Run("stable across code changes", func(t *testing.T) {
expectedFingerprint := "c0c82936be34b183" // If this is a valid fingerprint generation change, update the expected value.
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
10 months ago
assert.Equal(t, expectedFingerprint, baseReceiver.Fingerprint())
})
t.Run("stable across clones", func(t *testing.T) {
fingerprint := baseReceiver.Fingerprint()
receiverClone := baseReceiver.Clone()
assert.Equal(t, fingerprint, receiverClone.Fingerprint())
})
t.Run("stable across Version field modification", func(t *testing.T) {
fingerprint := baseReceiver.Fingerprint()
receiverClone := baseReceiver.Clone()
receiverClone.Version = "new version"
assert.Equal(t, fingerprint, receiverClone.Fingerprint())
})
t.Run("unstable across field modification", func(t *testing.T) {
fingerprint := baseReceiver.Fingerprint()
excludedFields := map[string]struct{}{
"Version": {},
}
reflectVal := reflect.ValueOf(&completelyDifferentReceiver).Elem()
receiverType := reflect.TypeOf((*Receiver)(nil)).Elem()
for i := 0; i < receiverType.NumField(); i++ {
field := receiverType.Field(i).Name
if _, ok := excludedFields[field]; ok {
continue
}
cp := baseReceiver.Clone()
// Get the current field being modified.
v := reflect.ValueOf(&cp).Elem()
vf := v.Field(i)
otherField := reflectVal.Field(i)
if reflect.DeepEqual(otherField.Interface(), vf.Interface()) {
assert.Failf(t, "filds are identical", "Receiver field %s is the same as the original, test does not ensure instability across the field", field)
continue
}
// Set the field to the value of the completelyDifferentReceiver.
vf.Set(otherField)
f2 := cp.Fingerprint()
assert.NotEqualf(t, fingerprint, f2, "Receiver field %s does not seem to be used in fingerprint", field)
}
excludedFields = map[string]struct{}{}
reflectVal = reflect.ValueOf(completelyDifferentReceiver.Integrations[0]).Elem()
integrationType := reflect.TypeOf((*Integration)(nil)).Elem()
for i := 0; i < integrationType.NumField(); i++ {
field := integrationType.Field(i).Name
if _, ok := excludedFields[field]; ok {
continue
}
cp := baseReceiver.Clone()
integrationCp := cp.Integrations[0]
// Get the current field being modified.
v := reflect.ValueOf(integrationCp).Elem()
vf := v.Field(i)
otherField := reflectVal.Field(i)
if reflect.DeepEqual(otherField.Interface(), vf.Interface()) {
assert.Failf(t, "filds are identical", "Integration field %s is the same as the original, test does not ensure instability across the field", field)
continue
}
// Set the field to the value of the completelyDifferentReceiver.
vf.Set(otherField)
f2 := cp.Fingerprint()
assert.NotEqualf(t, fingerprint, f2, "Integration field %s does not seem to be used in fingerprint", field)
}
})
}