diff --git a/pkg/services/ngalert/provisioning/compat.go b/pkg/services/ngalert/provisioning/compat.go index 29c38ec4007..ca9c86d2417 100644 --- a/pkg/services/ngalert/provisioning/compat.go +++ b/pkg/services/ngalert/provisioning/compat.go @@ -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 diff --git a/pkg/services/ngalert/provisioning/contactpoints.go b/pkg/services/ngalert/provisioning/contactpoints.go index f7d49da1c79..d25aa4e959f 100644 --- a/pkg/services/ngalert/provisioning/contactpoints.go +++ b/pkg/services/ngalert/provisioning/contactpoints.go @@ -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 diff --git a/pkg/services/ngalert/provisioning/contactpoints_test.go b/pkg/services/ngalert/provisioning/contactpoints_test.go index 5d5a3b44cba..34535c1a504 100644 --- a/pkg/services/ngalert/provisioning/contactpoints_test.go +++ b/pkg/services/ngalert/provisioning/contactpoints_test.go @@ -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) diff --git a/pkg/services/provisioning/alerting/contact_point_types.go b/pkg/services/provisioning/alerting/contact_point_types.go index 391c9ff1fc2..155ecde3cc0 100644 --- a/pkg/services/provisioning/alerting/contact_point_types.go +++ b/pkg/services/provisioning/alerting/contact_point_types.go @@ -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 {