mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
[Alerting]: Assign UUID to grafana receivers (#34241)
* [Alerting]: Assign UUID to grafana receivers * Apply suggestions from code review * Add test for updating invalid receiver Co-authored-by: Domas <domasx2@gmail.com>
This commit is contained in:
committed by
GitHub
parent
2e7ccf0e42
commit
11243dec14
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@@ -86,6 +84,13 @@ func (srv AlertmanagerSrv) RouteGetAlertingConfig(c *models.ReqContext) response
|
||||
for _, pr := range recv.PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
secureFields := make(map[string]bool, len(pr.SecureSettings))
|
||||
for k := range pr.SecureSettings {
|
||||
decryptedValue, err := pr.GetDecryptedSecret(k)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to decrypt stored secure setting: %s", k), err)
|
||||
}
|
||||
if decryptedValue == "" {
|
||||
continue
|
||||
}
|
||||
secureFields[k] = true
|
||||
}
|
||||
gr := apimodels.GettableGrafanaReceiver{
|
||||
@@ -191,49 +196,43 @@ func (srv AlertmanagerSrv) RoutePostAlertingConfig(c *models.ReqContext, body ap
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "failed to load lastest configuration", err)
|
||||
}
|
||||
currentReceiverMap := currentConfig.GetGrafanaReceiverMap()
|
||||
|
||||
// Copy the previously known secure settings
|
||||
for i, r := range body.AlertmanagerConfig.Receivers {
|
||||
for j, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
if len(currentConfig.AlertmanagerConfig.Receivers) <= i { // this is a receiver we don't have any stored for - skip it.
|
||||
if gr.UID == "" { // new receiver
|
||||
continue
|
||||
}
|
||||
cr := currentConfig.AlertmanagerConfig.Receivers[i]
|
||||
|
||||
if len(cr.PostableGrafanaReceivers.GrafanaManagedReceivers) <= j { // this is a receiver we don't have anything stored for - skip it.
|
||||
continue
|
||||
cgmr, ok := currentReceiverMap[gr.UID]
|
||||
if !ok {
|
||||
// it tries to update a receiver that didn't previously exist
|
||||
return response.Error(http.StatusBadRequest, fmt.Sprintf("unknown receiver: %s", gr.UID), nil)
|
||||
}
|
||||
cgmr := cr.PostableGrafanaReceivers.GrafanaManagedReceivers[j]
|
||||
|
||||
//TODO: We use the name and type to match current stored receivers againt sent ones, but we should ideally use something unique e.g. UUID
|
||||
if cgmr.Name == gr.Name && cgmr.Type == gr.Type {
|
||||
// frontend sends only the secure settings that have to be updated
|
||||
// therefore we have to copy from the last configuration only those secure settings not included in the request
|
||||
for key, storedValue := range cgmr.SecureSettings {
|
||||
_, ok := body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings[key]
|
||||
if !ok {
|
||||
decodeValue, err := base64.StdEncoding.DecodeString(storedValue)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to decode stored secure setting: %s", key), err)
|
||||
}
|
||||
decryptedValue, err := util.Decrypt(decodeValue, setting.SecretKey)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to decrypt stored secure setting: %s", key), err)
|
||||
}
|
||||
|
||||
if body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings == nil {
|
||||
body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings = make(map[string]string, len(cgmr.SecureSettings))
|
||||
}
|
||||
|
||||
body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings[key] = string(decryptedValue)
|
||||
// frontend sends only the secure settings that have to be updated
|
||||
// therefore we have to copy from the last configuration only those secure settings not included in the request
|
||||
for key := range cgmr.SecureSettings {
|
||||
_, ok := body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings[key]
|
||||
if !ok {
|
||||
decryptedValue, err := cgmr.GetDecryptedSecret(key)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to decrypt stored secure setting: %s", key), err)
|
||||
}
|
||||
|
||||
if body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings == nil {
|
||||
body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings = make(map[string]string, len(cgmr.SecureSettings))
|
||||
}
|
||||
|
||||
body.AlertmanagerConfig.Receivers[i].PostableGrafanaReceivers.GrafanaManagedReceivers[j].SecureSettings[key] = decryptedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := body.EncryptSecureSettings(); err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "failed to encrypt receiver secrets", err)
|
||||
if err := body.ProcessConfig(); err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "failed to post process Alertmanager configuration", err)
|
||||
}
|
||||
|
||||
if err := srv.am.SaveAndApplyConfig(&body); err != nil {
|
||||
|
||||
@@ -245,7 +245,24 @@ func (c *PostableUserConfig) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PostableUserConfig) EncryptSecureSettings() error {
|
||||
// GetGrafanaReceiverMap returns a map that associates UUIDs to grafana receivers
|
||||
func (c *PostableUserConfig) GetGrafanaReceiverMap() map[string]*PostableGrafanaReceiver {
|
||||
UIDs := make(map[string]*PostableGrafanaReceiver)
|
||||
for _, r := range c.AlertmanagerConfig.Receivers {
|
||||
switch r.Type() {
|
||||
case GrafanaReceiverType:
|
||||
for _, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
UIDs[gr.UID] = gr
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return UIDs
|
||||
}
|
||||
|
||||
// ProcessConfig parses grafana receivers, encrypts secrets and assigns UUIDs (if they are missing)
|
||||
func (c *PostableUserConfig) ProcessConfig() error {
|
||||
seenUIDs := make(map[string]struct{})
|
||||
// encrypt secure settings for storing them in DB
|
||||
for _, r := range c.AlertmanagerConfig.Receivers {
|
||||
switch r.Type() {
|
||||
@@ -258,6 +275,21 @@ func (c *PostableUserConfig) EncryptSecureSettings() error {
|
||||
}
|
||||
gr.SecureSettings[k] = base64.StdEncoding.EncodeToString(encryptedData)
|
||||
}
|
||||
if gr.UID == "" {
|
||||
retries := 5
|
||||
for i := 0; i < retries; i++ {
|
||||
gen := util.GenerateShortUID()
|
||||
_, ok := seenUIDs[gen]
|
||||
if !ok {
|
||||
gr.UID = gen
|
||||
break
|
||||
}
|
||||
}
|
||||
if gr.UID == "" {
|
||||
return fmt.Errorf("all %d attempts to generate UID for receiver have failed; please retry", retries)
|
||||
}
|
||||
}
|
||||
seenUIDs[gr.UID] = struct{}{}
|
||||
}
|
||||
default:
|
||||
}
|
||||
@@ -353,6 +385,21 @@ func (c *GettableUserConfig) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tmp)
|
||||
}
|
||||
|
||||
// GetGrafanaReceiverMap returns a map that associates UUIDs to grafana receivers
|
||||
func (c *GettableUserConfig) GetGrafanaReceiverMap() map[string]*GettableGrafanaReceiver {
|
||||
UIDs := make(map[string]*GettableGrafanaReceiver)
|
||||
for _, r := range c.AlertmanagerConfig.Receivers {
|
||||
switch r.Type() {
|
||||
case GrafanaReceiverType:
|
||||
for _, gr := range r.GettableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
UIDs[gr.UID] = gr
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return UIDs
|
||||
}
|
||||
|
||||
type GettableApiAlertingConfig struct {
|
||||
Config `yaml:",inline"`
|
||||
|
||||
@@ -521,6 +568,22 @@ type PostableGrafanaReceiver struct {
|
||||
SecureSettings map[string]string `json:"secureSettings"`
|
||||
}
|
||||
|
||||
func (r *PostableGrafanaReceiver) GetDecryptedSecret(key string) (string, error) {
|
||||
storedValue, ok := r.SecureSettings[key]
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
decodeValue, err := base64.StdEncoding.DecodeString(storedValue)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
decryptedValue, err := util.Decrypt(decodeValue, setting.SecretKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(decryptedValue), nil
|
||||
}
|
||||
|
||||
type ReceiverType int
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user