2022-04-13 22:15:55 +02:00
package provisioning
import (
"context"
"encoding/base64"
"encoding/json"
2023-09-08 15:09:35 -04:00
"errors"
2022-04-13 22:15:55 +02:00
"fmt"
"sort"
2023-07-20 14:35:56 -04:00
"strings"
2022-04-13 22:15:55 +02:00
2023-03-29 13:34:59 -04:00
alertingNotify "github.com/grafana/alerting/notify"
2022-12-16 13:01:06 -05:00
"github.com/prometheus/alertmanager/config"
2022-04-13 22:15:55 +02:00
"github.com/grafana/grafana/pkg/infra/log"
2023-08-08 12:29:34 -04:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
2022-04-13 22:15:55 +02:00
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
2023-03-29 13:34:59 -04:00
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
2022-04-13 22:15:55 +02:00
"github.com/grafana/grafana/pkg/services/secrets"
2023-07-20 14:35:56 -04:00
"github.com/grafana/grafana/pkg/services/user"
2022-04-13 22:15:55 +02:00
"github.com/grafana/grafana/pkg/util"
)
type ContactPointService struct {
amStore AMConfigStore
encryptionService secrets . Service
provenanceStore ProvisioningStore
xact TransactionManager
log log . Logger
2023-08-08 12:29:34 -04:00
ac accesscontrol . AccessControl
2022-04-13 22:15:55 +02:00
}
2022-06-09 03:38:46 -05:00
func NewContactPointService ( store AMConfigStore , encryptionService secrets . Service ,
2023-08-08 12:29:34 -04:00
provenanceStore ProvisioningStore , xact TransactionManager , log log . Logger , ac accesscontrol . AccessControl ) * ContactPointService {
2022-04-13 22:15:55 +02:00
return & ContactPointService {
amStore : store ,
encryptionService : encryptionService ,
provenanceStore : provenanceStore ,
xact : xact ,
log : log ,
2023-08-08 12:29:34 -04:00
ac : ac ,
2022-04-13 22:15:55 +02:00
}
}
2022-07-11 17:11:46 -05:00
type ContactPointQuery struct {
// Optionally filter by name.
Name string
OrgID int64
2023-07-20 14:35:56 -04:00
// Optionally decrypt secure settings, requires OrgAdmin.
Decrypt bool
2022-07-11 17:11:46 -05:00
}
2023-08-08 12:29:34 -04:00
func ( ecp * ContactPointService ) canDecryptSecrets ( ctx context . Context , u * user . SignedInUser ) bool {
if u == nil {
return false
}
permitted , err := ecp . ac . Evaluate ( ctx , u , accesscontrol . EvalPermission ( accesscontrol . ActionAlertingProvisioningReadSecrets ) )
if err != nil {
ecp . log . Error ( "Failed to evaluate user permissions" , "error" , err )
permitted = false
}
return permitted
}
2023-07-20 14:35:56 -04:00
// GetContactPoints returns contact points. If q.Decrypt is true and the user is an OrgAdmin, decrypted secure settings are included instead of redacted ones.
func ( ecp * ContactPointService ) GetContactPoints ( ctx context . Context , q ContactPointQuery , u * user . SignedInUser ) ( [ ] apimodels . EmbeddedContactPoint , error ) {
2023-08-08 12:29:34 -04:00
if q . Decrypt && ! ecp . canDecryptSecrets ( ctx , u ) {
return nil , fmt . Errorf ( "%w: user requires Admin role or alert.provisioning.secrets:read permission to view decrypted secure settings" , ErrPermissionDenied )
2023-07-20 14:35:56 -04:00
}
2022-07-11 17:11:46 -05:00
revision , err := getLastConfiguration ( ctx , q . OrgID , ecp . amStore )
2022-04-13 22:15:55 +02:00
if err != nil {
return nil , err
}
2022-07-11 17:11:46 -05:00
provenances , err := ecp . provenanceStore . GetProvenances ( ctx , q . OrgID , "contactPoint" )
2022-04-13 22:15:55 +02:00
if err != nil {
return nil , err
}
2023-10-05 16:13:34 -04:00
var contactPoints [ ] apimodels . EmbeddedContactPoint
2022-05-23 18:16:03 -05:00
for _ , contactPoint := range revision . cfg . GetGrafanaReceiverMap ( ) {
2022-07-11 17:11:46 -05:00
if q . Name != "" && contactPoint . Name != q . Name {
continue
}
2023-10-05 16:13:34 -04:00
embeddedContactPoint , err := PostableGrafanaReceiverToEmbeddedContactPoint (
contactPoint ,
provenances [ contactPoint . UID ] ,
ecp . decryptValueOrRedacted ( q . Decrypt , contactPoint . UID ) ,
)
2022-12-16 13:01:06 -05:00
if err != nil {
return nil , err
}
2022-04-13 22:15:55 +02:00
contactPoints = append ( contactPoints , embeddedContactPoint )
}
sort . SliceStable ( contactPoints , func ( i , j int ) bool {
2023-07-20 14:35:56 -04:00
switch strings . Compare ( contactPoints [ i ] . Name , contactPoints [ j ] . Name ) {
case - 1 :
return true
case 1 :
return false
}
return contactPoints [ i ] . UID < contactPoints [ j ] . UID
2022-04-13 22:15:55 +02:00
} )
return contactPoints , nil
}
2022-06-23 15:13:39 -05:00
// getContactPointDecrypted is an internal-only function that gets full contact point info, included encrypted fields.
// nil is returned if no matching contact point exists.
2022-04-13 22:15:55 +02:00
func ( ecp * ContactPointService ) getContactPointDecrypted ( ctx context . Context , orgID int64 , uid string ) ( apimodels . EmbeddedContactPoint , error ) {
2022-05-23 18:16:03 -05:00
revision , err := getLastConfiguration ( ctx , orgID , ecp . amStore )
2022-04-13 22:15:55 +02:00
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
2022-05-23 18:16:03 -05:00
for _ , receiver := range revision . cfg . GetGrafanaReceiverMap ( ) {
2022-04-13 22:15:55 +02:00
if receiver . UID != uid {
continue
}
2023-10-05 16:13:34 -04:00
embeddedContactPoint , err := PostableGrafanaReceiverToEmbeddedContactPoint (
receiver ,
models . ProvenanceNone ,
ecp . decryptValueOrRedacted ( true , receiver . UID ) ,
)
2022-12-16 13:01:06 -05:00
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
2022-04-13 22:15:55 +02:00
return embeddedContactPoint , nil
}
2022-06-23 15:13:39 -05:00
return apimodels . EmbeddedContactPoint { } , fmt . Errorf ( "%w: contact point with uid '%s' not found" , ErrNotFound , uid )
2022-04-13 22:15:55 +02:00
}
func ( ecp * ContactPointService ) CreateContactPoint ( ctx context . Context , orgID int64 ,
contactPoint apimodels . EmbeddedContactPoint , provenance models . Provenance ) ( apimodels . EmbeddedContactPoint , error ) {
2023-04-25 13:39:46 -04:00
if err := ValidateContactPoint ( ctx , contactPoint , ecp . encryptionService . GetDecryptedValue ) ; err != nil {
2022-06-09 03:38:46 -05:00
return apimodels . EmbeddedContactPoint { } , fmt . Errorf ( "%w: %s" , ErrValidation , err . Error ( ) )
2022-04-13 22:15:55 +02:00
}
2022-05-23 18:16:03 -05:00
revision , err := getLastConfiguration ( ctx , orgID , ecp . amStore )
2022-04-13 22:15:55 +02:00
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
2023-03-29 13:34:59 -04:00
extractedSecrets , err := RemoveSecretsForContactPoint ( & contactPoint )
2022-04-13 22:15:55 +02:00
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
for k , v := range extractedSecrets {
encryptedValue , err := ecp . encryptValue ( v )
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
extractedSecrets [ k ] = encryptedValue
}
2022-06-03 10:33:47 +02:00
if contactPoint . UID == "" {
contactPoint . UID = util . GenerateShortUID ( )
2023-09-08 15:09:35 -04:00
} else if err := util . ValidateUID ( contactPoint . UID ) ; err != nil {
return apimodels . EmbeddedContactPoint { } , errors . Join ( ErrValidation , fmt . Errorf ( "cannot create contact point with UID '%s': %w" , contactPoint . UID , err ) )
2022-06-03 10:33:47 +02:00
}
2022-12-16 13:01:06 -05:00
jsonData , err := contactPoint . Settings . MarshalJSON ( )
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
2022-04-13 22:15:55 +02:00
grafanaReceiver := & apimodels . PostableGrafanaReceiver {
UID : contactPoint . UID ,
Name : contactPoint . Name ,
Type : contactPoint . Type ,
DisableResolveMessage : contactPoint . DisableResolveMessage ,
2022-12-16 13:01:06 -05:00
Settings : jsonData ,
2022-04-13 22:15:55 +02:00
SecureSettings : extractedSecrets ,
}
receiverFound := false
2022-05-23 18:16:03 -05:00
for _ , receiver := range revision . cfg . AlertmanagerConfig . Receivers {
2022-06-27 18:57:47 +02:00
// check if uid is already used in receiver
for _ , rec := range receiver . PostableGrafanaReceivers . GrafanaManagedReceivers {
if grafanaReceiver . UID == rec . UID {
return apimodels . EmbeddedContactPoint { } , fmt . Errorf (
"receiver configuration with UID '%s' already exist in contact point '%s'. Please use unique identifiers for receivers across all contact points" ,
rec . UID ,
rec . Name )
}
}
2022-04-13 22:15:55 +02:00
if receiver . Name == contactPoint . Name {
receiver . PostableGrafanaReceivers . GrafanaManagedReceivers = append ( receiver . PostableGrafanaReceivers . GrafanaManagedReceivers , grafanaReceiver )
receiverFound = true
}
}
if ! receiverFound {
2022-05-23 18:16:03 -05:00
revision . cfg . AlertmanagerConfig . Receivers = append ( revision . cfg . AlertmanagerConfig . Receivers , & apimodels . PostableApiReceiver {
2022-04-13 22:15:55 +02:00
Receiver : config . Receiver {
Name : grafanaReceiver . Name ,
} ,
PostableGrafanaReceivers : apimodels . PostableGrafanaReceivers {
GrafanaManagedReceivers : [ ] * apimodels . PostableGrafanaReceiver { grafanaReceiver } ,
} ,
} )
}
2022-05-23 18:16:03 -05:00
data , err := json . Marshal ( revision . cfg )
2022-04-13 22:15:55 +02:00
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
err = ecp . xact . InTransaction ( ctx , func ( ctx context . Context ) error {
2022-09-09 10:05:52 +02:00
err = PersistConfig ( ctx , ecp . amStore , & models . SaveAlertmanagerConfigurationCmd {
2022-04-13 22:15:55 +02:00
AlertmanagerConfiguration : string ( data ) ,
2022-05-23 18:16:03 -05:00
FetchedConfigurationHash : revision . concurrencyToken ,
ConfigurationVersion : revision . version ,
2022-04-13 22:15:55 +02:00
Default : false ,
OrgID : orgID ,
} )
if err != nil {
return err
}
2022-04-26 10:30:57 -05:00
err = ecp . provenanceStore . SetProvenance ( ctx , & contactPoint , orgID , provenance )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
contactPoint . Provenance = string ( provenance )
return nil
} )
if err != nil {
return apimodels . EmbeddedContactPoint { } , err
}
for k := range extractedSecrets {
contactPoint . Settings . Set ( k , apimodels . RedactedValue )
}
return contactPoint , nil
}
func ( ecp * ContactPointService ) UpdateContactPoint ( ctx context . Context , orgID int64 , contactPoint apimodels . EmbeddedContactPoint , provenance models . Provenance ) error {
// set all redacted values with the latest known value from the store
2022-06-09 03:38:46 -05:00
if contactPoint . Settings == nil {
return fmt . Errorf ( "%w: %s" , ErrValidation , "settings should not be empty" )
}
2022-04-13 22:15:55 +02:00
rawContactPoint , err := ecp . getContactPointDecrypted ( ctx , orgID , contactPoint . UID )
if err != nil {
return err
}
2023-10-11 14:21:21 -05:00
secretKeys , err := GetSecretKeysForContactPointType ( contactPoint . Type )
2022-04-13 22:15:55 +02:00
if err != nil {
2022-06-09 03:38:46 -05:00
return fmt . Errorf ( "%w: %s" , ErrValidation , err . Error ( ) )
2022-04-13 22:15:55 +02:00
}
for _ , secretKey := range secretKeys {
secretValue := contactPoint . Settings . Get ( secretKey ) . MustString ( )
if secretValue == apimodels . RedactedValue {
contactPoint . Settings . Set ( secretKey , rawContactPoint . Settings . Get ( secretKey ) . MustString ( ) )
}
}
2022-06-09 03:38:46 -05:00
2022-04-13 22:15:55 +02:00
// validate merged values
2023-04-25 13:39:46 -04:00
if err := ValidateContactPoint ( ctx , contactPoint , ecp . encryptionService . GetDecryptedValue ) ; err != nil {
2022-06-09 03:38:46 -05:00
return fmt . Errorf ( "%w: %s" , ErrValidation , err . Error ( ) )
2022-04-13 22:15:55 +02:00
}
2022-06-09 03:38:46 -05:00
2023-04-18 15:10:36 +02:00
// check that provenance is not changed in an invalid way
2022-04-26 10:30:57 -05:00
storedProvenance , err := ecp . provenanceStore . GetProvenance ( ctx , & contactPoint , orgID )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
if storedProvenance != provenance && storedProvenance != models . ProvenanceNone {
2023-04-18 15:10:36 +02:00
return fmt . Errorf ( "cannot change provenance from '%s' to '%s'" , storedProvenance , provenance )
2022-04-13 22:15:55 +02:00
}
// transform to internal model
2023-03-29 13:34:59 -04:00
extractedSecrets , err := RemoveSecretsForContactPoint ( & contactPoint )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
for k , v := range extractedSecrets {
encryptedValue , err := ecp . encryptValue ( v )
if err != nil {
return err
}
extractedSecrets [ k ] = encryptedValue
}
2022-12-16 13:01:06 -05:00
jsonData , err := contactPoint . Settings . MarshalJSON ( )
if err != nil {
return err
}
2022-04-13 22:15:55 +02:00
mergedReceiver := & apimodels . PostableGrafanaReceiver {
UID : contactPoint . UID ,
Name : contactPoint . Name ,
Type : contactPoint . Type ,
DisableResolveMessage : contactPoint . DisableResolveMessage ,
2022-12-16 13:01:06 -05:00
Settings : jsonData ,
2022-04-13 22:15:55 +02:00
SecureSettings : extractedSecrets ,
}
// save to store
2022-05-23 18:16:03 -05:00
revision , err := getLastConfiguration ( ctx , orgID , ecp . amStore )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
2022-06-17 10:19:22 -05:00
configModified := stitchReceiver ( revision . cfg , mergedReceiver )
if ! configModified {
return fmt . Errorf ( "contact point with uid '%s' not found" , mergedReceiver . UID )
2022-04-13 22:15:55 +02:00
}
2022-06-17 10:19:22 -05:00
2022-05-23 18:16:03 -05:00
data , err := json . Marshal ( revision . cfg )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
return ecp . xact . InTransaction ( ctx , func ( ctx context . Context ) error {
2022-09-09 10:05:52 +02:00
err = PersistConfig ( ctx , ecp . amStore , & models . SaveAlertmanagerConfigurationCmd {
2022-04-13 22:15:55 +02:00
AlertmanagerConfiguration : string ( data ) ,
2022-05-23 18:16:03 -05:00
FetchedConfigurationHash : revision . concurrencyToken ,
ConfigurationVersion : revision . version ,
2022-04-13 22:15:55 +02:00
Default : false ,
OrgID : orgID ,
} )
if err != nil {
return err
}
2022-04-26 10:30:57 -05:00
err = ecp . provenanceStore . SetProvenance ( ctx , & contactPoint , orgID , provenance )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
contactPoint . Provenance = string ( provenance )
return nil
} )
}
func ( ecp * ContactPointService ) DeleteContactPoint ( ctx context . Context , orgID int64 , uid string ) error {
2022-05-23 18:16:03 -05:00
revision , err := getLastConfiguration ( ctx , orgID , ecp . amStore )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
// Indicates if the full contact point is removed or just one of the
// configurations, as a contactpoint can consist of any number of
// configurations.
fullRemoval := false
// Name of the contact point that will be removed, might be used if a
// full removal is done to check if it's referenced in any route.
name := ""
2022-05-23 18:16:03 -05:00
for i , receiver := range revision . cfg . AlertmanagerConfig . Receivers {
2022-04-13 22:15:55 +02:00
for j , grafanaReceiver := range receiver . GrafanaManagedReceivers {
if grafanaReceiver . UID == uid {
name = grafanaReceiver . Name
receiver . GrafanaManagedReceivers = append ( receiver . GrafanaManagedReceivers [ : j ] , receiver . GrafanaManagedReceivers [ j + 1 : ] ... )
// if this was the last receiver we removed, we remove the whole receiver
if len ( receiver . GrafanaManagedReceivers ) == 0 {
fullRemoval = true
2022-05-23 18:16:03 -05:00
revision . cfg . AlertmanagerConfig . Receivers = append ( revision . cfg . AlertmanagerConfig . Receivers [ : i ] , revision . cfg . AlertmanagerConfig . Receivers [ i + 1 : ] ... )
2022-04-13 22:15:55 +02:00
}
break
}
}
}
2022-05-23 18:16:03 -05:00
if fullRemoval && isContactPointInUse ( name , [ ] * apimodels . Route { revision . cfg . AlertmanagerConfig . Route } ) {
2022-04-13 22:15:55 +02:00
return fmt . Errorf ( "contact point '%s' is currently used by a notification policy" , name )
}
2022-05-23 18:16:03 -05:00
data , err := json . Marshal ( revision . cfg )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
return ecp . xact . InTransaction ( ctx , func ( ctx context . Context ) error {
2022-04-26 10:30:57 -05:00
target := & apimodels . EmbeddedContactPoint {
UID : uid ,
}
err := ecp . provenanceStore . DeleteProvenance ( ctx , target , orgID )
2022-04-13 22:15:55 +02:00
if err != nil {
return err
}
2022-09-09 10:05:52 +02:00
return PersistConfig ( ctx , ecp . amStore , & models . SaveAlertmanagerConfigurationCmd {
2022-04-13 22:15:55 +02:00
AlertmanagerConfiguration : string ( data ) ,
2022-05-23 18:16:03 -05:00
FetchedConfigurationHash : revision . concurrencyToken ,
ConfigurationVersion : revision . version ,
2022-04-13 22:15:55 +02:00
Default : false ,
OrgID : orgID ,
} )
} )
}
func isContactPointInUse ( name string , routes [ ] * apimodels . Route ) bool {
if len ( routes ) == 0 {
return false
}
for _ , route := range routes {
if route . Receiver == name {
return true
}
if isContactPointInUse ( name , route . Routes ) {
return true
}
}
return false
}
2023-10-05 16:13:34 -04:00
// decryptValueOrRedacted returns a function that decodes a string from Base64 and then decrypts using secrets.Service.
// If argument 'decrypt' is false, then returns definitions.RedactedValue regardless of the decrypted value.
// Otherwise, it returns the decoded and decrypted value. The function returns empty string in the case of errors, which are logged
func ( ecp * ContactPointService ) decryptValueOrRedacted ( decrypt bool , integrationUID string ) func ( v string ) string {
return func ( value string ) string {
decodeValue , err := base64 . StdEncoding . DecodeString ( value )
if err != nil {
ecp . log . Warn ( "Failed to decode secret value from Base64" , "error" , err . Error ( ) , "integrationUid" , integrationUID )
return ""
}
decryptedValue , err := ecp . encryptionService . Decrypt ( context . Background ( ) , decodeValue )
if err != nil {
ecp . log . Warn ( "Failed to decrypt secret value" , "error" , err . Error ( ) , "integrationUid" , integrationUID )
return ""
}
if decrypt {
return string ( decryptedValue )
} else {
return apimodels . RedactedValue
}
2022-04-13 22:15:55 +02:00
}
}
func ( ecp * ContactPointService ) encryptValue ( value string ) ( string , error ) {
encryptedData , err := ecp . encryptionService . Encrypt ( context . Background ( ) , [ ] byte ( value ) , secrets . WithoutScope ( ) )
if err != nil {
return "" , fmt . Errorf ( "failed to encrypt secure settings: %w" , err )
}
return base64 . StdEncoding . EncodeToString ( encryptedData ) , nil
}
2022-06-17 10:19:22 -05:00
// stitchReceiver modifies a receiver, target, in an alertmanager config. It modifies the given config in-place.
// Returns true if the config was altered in any way, and false otherwise.
func stitchReceiver ( cfg * apimodels . PostableUserConfig , target * apimodels . PostableGrafanaReceiver ) bool {
// Algorithm to fix up receivers. Receivers are very complex and depend heavily on internal consistency.
// All receivers in a given receiver group have the same name. We must maintain this across renames.
configModified := false
groupLoop :
2023-09-04 13:30:15 -04:00
for groupIdx , receiverGroup := range cfg . AlertmanagerConfig . Receivers {
2022-06-17 10:19:22 -05:00
// Does the current group contain the grafana receiver we're interested in?
for i , grafanaReceiver := range receiverGroup . GrafanaManagedReceivers {
if grafanaReceiver . UID == target . UID {
// If it's a basic field change, simply replace it. Done!
//
// NOTE:
// In a "normal" database, receiverGroup.Name should always == grafanaReceiver.Name.
// Check it regardless.
// If these values are out of sync due to some bug elsewhere in the code, let's fix it up.
// Our receiver group fixing logic below will handle it.
if grafanaReceiver . Name == target . Name && receiverGroup . Name == grafanaReceiver . Name {
receiverGroup . GrafanaManagedReceivers [ i ] = target
configModified = true
break groupLoop
}
// If we're renaming, we'll need to fix up the macro receiver group for consistency.
// Firstly, if we're the only receiver in the group, simply rename the group to match. Done!
if len ( receiverGroup . GrafanaManagedReceivers ) == 1 {
2022-09-08 15:20:52 +02:00
replaceReferences ( receiverGroup . Name , target . Name , cfg . AlertmanagerConfig . Route )
2022-06-17 10:19:22 -05:00
receiverGroup . Name = target . Name
receiverGroup . GrafanaManagedReceivers [ i ] = target
}
// Otherwise, we only want to rename the receiver we are touching... NOT all of them.
// Check to see whether a different group with the name we want already exists.
2023-04-24 20:23:23 -05:00
for _ , candidateExistingGroup := range cfg . AlertmanagerConfig . Receivers {
2022-06-17 10:19:22 -05:00
// If so, put our modified receiver into that group. Done!
if candidateExistingGroup . Name == target . Name {
// Drop it from the old group...
receiverGroup . GrafanaManagedReceivers = append ( receiverGroup . GrafanaManagedReceivers [ : i ] , receiverGroup . GrafanaManagedReceivers [ i + 1 : ] ... )
// Add the modified receiver to the new group...
candidateExistingGroup . GrafanaManagedReceivers = append ( candidateExistingGroup . GrafanaManagedReceivers , target )
configModified = true
2023-09-04 13:30:15 -04:00
// if the old receiver group turns out to be empty. Remove it.
if len ( receiverGroup . GrafanaManagedReceivers ) == 0 {
cfg . AlertmanagerConfig . Receivers = append ( cfg . AlertmanagerConfig . Receivers [ : groupIdx ] , cfg . AlertmanagerConfig . Receivers [ groupIdx + 1 : ] ... )
}
2022-06-17 10:19:22 -05:00
break groupLoop
}
}
// Doesn't exist? Create a new group just for the receiver.
newGroup := & apimodels . PostableApiReceiver {
Receiver : config . Receiver {
Name : target . Name ,
} ,
PostableGrafanaReceivers : apimodels . PostableGrafanaReceivers {
GrafanaManagedReceivers : [ ] * apimodels . PostableGrafanaReceiver {
target ,
} ,
} ,
}
cfg . AlertmanagerConfig . Receivers = append ( cfg . AlertmanagerConfig . Receivers , newGroup )
// Drop it from the old spot.
receiverGroup . GrafanaManagedReceivers = append ( receiverGroup . GrafanaManagedReceivers [ : i ] , receiverGroup . GrafanaManagedReceivers [ i + 1 : ] ... )
configModified = true
break groupLoop
}
}
}
return configModified
}
2022-09-08 15:20:52 +02:00
func replaceReferences ( oldName , newName string , routes ... * apimodels . Route ) {
if len ( routes ) == 0 {
return
}
for _ , route := range routes {
if route . Receiver == oldName {
route . Receiver = newName
}
replaceReferences ( oldName , newName , route . Routes ... )
}
}
2023-03-29 13:34:59 -04:00
2023-04-25 13:39:46 -04:00
func ValidateContactPoint ( ctx context . Context , e apimodels . EmbeddedContactPoint , decryptFunc alertingNotify . GetDecryptedValueFn ) error {
2023-03-29 13:34:59 -04:00
if e . Type == "" {
return fmt . Errorf ( "type should not be an empty string" )
}
if e . Settings == nil {
return fmt . Errorf ( "settings should not be empty" )
}
2023-04-25 13:39:46 -04:00
integration , err := EmbeddedContactPointToGrafanaIntegrationConfig ( e )
2023-03-29 13:34:59 -04:00
if err != nil {
return err
}
2023-04-25 13:39:46 -04:00
_ , err = alertingNotify . BuildReceiverConfiguration ( ctx , & alertingNotify . APIReceiver {
GrafanaIntegrations : alertingNotify . GrafanaIntegrations {
Integrations : [ ] * alertingNotify . GrafanaIntegrationConfig { & integration } ,
} ,
} , decryptFunc )
if err != nil {
2023-03-29 13:34:59 -04:00
return err
}
return nil
}
2023-10-11 14:21:21 -05:00
// 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 := channels_config . 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 )
}
2023-03-29 13:34:59 -04:00
// RemoveSecretsForContactPoint removes all secrets from the contact point's settings and returns them as a map. Returns error if contact point type is not known.
func RemoveSecretsForContactPoint ( e * apimodels . EmbeddedContactPoint ) ( map [ string ] string , error ) {
s := map [ string ] string { }
2023-10-11 14:21:21 -05:00
secretKeys , err := GetSecretKeysForContactPointType ( e . Type )
2023-03-29 13:34:59 -04:00
if err != nil {
return nil , err
}
for _ , secretKey := range secretKeys {
secretValue := e . Settings . Get ( secretKey ) . MustString ( )
e . Settings . Del ( secretKey )
s [ secretKey ] = secretValue
}
return s , nil
}