package legacy_storage import ( "errors" "fmt" "slices" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/util" ) func (rev *ConfigRevision) DeleteReceiver(uid string) { // Remove the receiver from the configuration. rev.Config.AlertmanagerConfig.Receivers = slices.DeleteFunc(rev.Config.AlertmanagerConfig.Receivers, func(r *definitions.PostableApiReceiver) bool { return NameToUid(r.GetName()) == uid }) } func (rev *ConfigRevision) CreateReceiver(receiver *models.Receiver) (*definitions.PostableApiReceiver, error) { // Check if the receiver already exists. _, err := rev.GetReceiver(receiver.GetUID()) if err == nil { return nil, ErrReceiverExists.Errorf("") } if !errors.Is(err, ErrReceiverNotFound) { return nil, err } if err := validateAndSetIntegrationUIDs(receiver); err != nil { return nil, err } postable, err := ReceiverToPostableApiReceiver(receiver) if err != nil { return nil, err } rev.Config.AlertmanagerConfig.Receivers = append(rev.Config.AlertmanagerConfig.Receivers, postable) if err := rev.ValidateReceiver(postable); err != nil { return nil, err } return postable, nil } func (rev *ConfigRevision) UpdateReceiver(receiver *models.Receiver) (*definitions.PostableApiReceiver, error) { existing, err := rev.GetReceiver(receiver.GetUID()) if err != nil { return nil, err } if err := validateAndSetIntegrationUIDs(receiver); err != nil { return nil, err } postable, err := ReceiverToPostableApiReceiver(receiver) if err != nil { return nil, err } // Update receiver in the configuration. *existing = *postable if err := rev.ValidateReceiver(existing); err != nil { return nil, err } return postable, nil } // ReceiverNameUsedByRoutes checks if a receiver name is used in any routes. func (rev *ConfigRevision) ReceiverNameUsedByRoutes(name string) bool { return isReceiverInUse(name, []*definitions.Route{rev.Config.AlertmanagerConfig.Route}) } // ReceiverUseByName returns a map of receiver names to the number of times they are used in routes. func (rev *ConfigRevision) ReceiverUseByName() map[string]int { m := make(map[string]int) receiverUseCounts([]*definitions.Route{rev.Config.AlertmanagerConfig.Route}, m) return m } func (rev *ConfigRevision) GetReceiver(uid string) (*definitions.PostableApiReceiver, error) { for _, r := range rev.Config.AlertmanagerConfig.Receivers { if NameToUid(r.GetName()) == uid { return r, nil } } return nil, ErrReceiverNotFound.Errorf("") } func (rev *ConfigRevision) GetReceivers(uids []string) []*definitions.PostableApiReceiver { receivers := make([]*definitions.PostableApiReceiver, 0, len(uids)) for _, r := range rev.Config.AlertmanagerConfig.Receivers { if len(uids) == 0 || slices.Contains(uids, NameToUid(r.GetName())) { receivers = append(receivers, r) } } return receivers } // RenameReceiverInRoutes renames all references to a receiver in routes. Returns number of routes that were updated func (rev *ConfigRevision) RenameReceiverInRoutes(oldName, newName string) int { return RenameReceiverInRoute(oldName, newName, rev.Config.AlertmanagerConfig.Route) } // ValidateReceiver checks if the given receiver conflicts in name or integration UID with existing receivers. // We only check the receiver being modified to prevent existing issues from other receivers being reported. func (rev *ConfigRevision) ValidateReceiver(p *definitions.PostableApiReceiver) error { uids := make(map[string]struct{}, len(rev.Config.AlertmanagerConfig.Receivers)) for _, integrations := range p.GrafanaManagedReceivers { if _, exists := uids[integrations.UID]; exists { return MakeErrReceiverInvalid(fmt.Errorf("integration with UID %q already exists", integrations.UID)) } uids[integrations.UID] = struct{}{} } for _, r := range rev.Config.AlertmanagerConfig.Receivers { if p == r { // Skip the receiver itself. continue } if r.GetName() == p.GetName() { return MakeErrReceiverInvalid(fmt.Errorf("name %q already exists", r.GetName())) } for _, gr := range r.GrafanaManagedReceivers { if _, exists := uids[gr.UID]; exists { return MakeErrReceiverInvalid(fmt.Errorf("integration with UID %q already exists", gr.UID)) } } } return nil } func RenameReceiverInRoute(oldName, newName string, routes ...*definitions.Route) int { if len(routes) == 0 { return 0 } updated := 0 for _, route := range routes { if route.Receiver == oldName { route.Receiver = newName updated++ } updated += RenameReceiverInRoute(oldName, newName, route.Routes...) } return updated } // isReceiverInUse checks if a receiver is used in a route or any of its sub-routes. func isReceiverInUse(name string, routes []*definitions.Route) bool { if len(routes) == 0 { return false } for _, route := range routes { if route.Receiver == name { return true } if isReceiverInUse(name, route.Routes) { return true } } return false } // receiverUseCounts counts how many times receivers are used in a route or any of its sub-routes. func receiverUseCounts(routes []*definitions.Route, m map[string]int) { if len(routes) == 0 { return } for _, route := range routes { m[route.Receiver]++ receiverUseCounts(route.Routes, m) } } // validateAndSetIntegrationUIDs validates existing integration UIDs and generates them if they are empty. func validateAndSetIntegrationUIDs(receiver *models.Receiver) error { for _, integration := range receiver.Integrations { if integration.UID == "" { integration.UID = util.GenerateShortUID() } else if err := util.ValidateUID(integration.UID); err != nil { return MakeErrReceiverInvalid(fmt.Errorf("integration UID %q is invalid: %w", integration.UID, err)) } } return nil }