Alerting: Provisioning to fix contact point type on save (#112246)

fix contact point type on create\update
This commit is contained in:
Yuri Tseretyan
2025-10-23 11:11:36 -04:00
committed by GitHub
parent 2b9c138d7e
commit 8b7f119cad
4 changed files with 52 additions and 9 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
func EmbeddedContactPointToGrafanaIntegrationConfig(e definitions.EmbeddedContactPoint) (alertingModels.IntegrationConfig, error) {
func EmbeddedContactPointToGrafanaIntegrationConfig(e *definitions.EmbeddedContactPoint) (alertingModels.IntegrationConfig, error) {
data, err := e.Settings.MarshalJSON()
if err != nil {
return alertingModels.IntegrationConfig{}, err
@@ -151,7 +151,7 @@ func (ecp *ContactPointService) CreateContactPoint(
contactPoint apimodels.EmbeddedContactPoint,
provenance models.Provenance,
) (apimodels.EmbeddedContactPoint, error) {
if err := ValidateContactPoint(ctx, contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
if err := ValidateContactPoint(ctx, &contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
return apimodels.EmbeddedContactPoint{}, fmt.Errorf("%w: %s", ErrValidation, err.Error())
}
@@ -243,14 +243,20 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
if contactPoint.Settings == nil {
return fmt.Errorf("%w: %s", ErrValidation, "settings should not be empty")
}
iType, err := alertingNotify.IntegrationTypeFromString(contactPoint.Type)
if err != nil {
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
}
typeSchema, ok := alertingNotify.GetSchemaVersionForIntegration(iType, schema.V1)
if !ok {
return fmt.Errorf("%w: failed to get secret keys for contact point type %s", ErrValidation, contactPoint.Type)
}
// patch integration with the secrets from the existing version
rawContactPoint, err := ecp.getContactPointDecrypted(ctx, orgID, contactPoint.UID)
if err != nil {
return err
}
typeSchema, ok := alertingNotify.GetSchemaVersionForIntegration(schema.IntegrationType(contactPoint.Type), schema.V1)
if !ok {
return fmt.Errorf("%w: failed to get secret keys for contact point type %s", ErrValidation, contactPoint.Type)
}
for _, secretPath := range typeSchema.GetSecretFieldsPaths() {
secretKey := secretPath.String()
secretValue := contactPoint.Settings.Get(secretKey).MustString()
@@ -260,7 +266,7 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
}
// validate merged values
if err := ValidateContactPoint(ctx, contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
if err := ValidateContactPoint(ctx, &contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
}
@@ -512,7 +518,12 @@ groupLoop:
return oldReceiverName, fullRemoval, newReceiverCreated
}
func ValidateContactPoint(ctx context.Context, e apimodels.EmbeddedContactPoint, decryptFunc alertingNotify.GetDecryptedValueFn) error {
func ValidateContactPoint(ctx context.Context, e *apimodels.EmbeddedContactPoint, decryptFunc alertingNotify.GetDecryptedValueFn) error {
iType, err := alertingNotify.IntegrationTypeFromString(e.Type)
if err != nil {
return err
}
e.Type = string(iType)
integration, err := EmbeddedContactPointToGrafanaIntegrationConfig(e)
if err != nil {
return err
@@ -11,6 +11,7 @@ import (
"github.com/grafana/alerting/notify"
"github.com/grafana/alerting/notify/notifytest"
"github.com/grafana/alerting/receivers/schema"
"github.com/grafana/alerting/receivers/slack"
"github.com/prometheus/alertmanager/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -146,6 +147,21 @@ func TestIntegrationContactPointService(t *testing.T) {
require.ErrorIs(t, err, ErrValidation)
})
t.Run("create accepts contact point with type in different cases", func(t *testing.T) {
sut := createContactPointServiceSut(t, secretsService)
newCp := createTestContactPoint()
newCp.Type = "Slack"
created, err := sut.CreateContactPoint(context.Background(), 1, redactedUser, newCp, models.ProvenanceAPI)
require.NoError(t, err)
assert.EqualValues(t, slack.Type, created.Type)
got, err := sut.GetContactPoints(context.Background(), cpsQueryWithName(1, newCp.Name), redactedUser)
require.NoError(t, err)
require.Len(t, got, 1)
assert.EqualValues(t, slack.Type, got[0].Type)
})
t.Run("update rejects contact points with no settings", func(t *testing.T) {
sut := createContactPointServiceSut(t, secretsService)
newCp := createTestContactPoint()
@@ -182,6 +198,22 @@ func TestIntegrationContactPointService(t *testing.T) {
require.ErrorIs(t, err, ErrValidation)
})
t.Run("update accepts contact points with type in another case", func(t *testing.T) {
sut := createContactPointServiceSut(t, secretsService)
newCp := createTestContactPoint()
newCp, err := sut.CreateContactPoint(context.Background(), 1, redactedUser, newCp, models.ProvenanceAPI)
require.NoError(t, err)
newCp.Type = "Slack"
err = sut.UpdateContactPoint(context.Background(), 1, newCp, models.ProvenanceAPI)
require.NoError(t, err)
got, err := sut.GetContactPoints(context.Background(), cpsQueryWithName(1, newCp.Name), redactedUser)
require.NoError(t, err)
require.Len(t, got, 1)
assert.EqualValues(t, slack.Type, got[0].Type)
})
t.Run("update renames references when group is renamed", func(t *testing.T) {
cfg := createEncryptedConfig(t, secretsService)
store := fakes.NewFakeAlertmanagerConfigStore(cfg)
@@ -95,7 +95,7 @@ func (config *ReceiverV1) mapToModel(name string) (definitions.EmbeddedContactPo
}
// As the values are not encrypted when coming from disk files,
// we can simply return the fallback for validation.
err := provisioning.ValidateContactPoint(context.Background(), cp, func(_ context.Context, _ map[string][]byte, _, fallback string) string {
err := provisioning.ValidateContactPoint(context.Background(), &cp, func(_ context.Context, _ map[string][]byte, _, fallback string) string {
return fallback
})
if err != nil {