Alerting: Use a struct when sending a Grafana AM configuration to the remote Alertmanager (#86451)
* Alerting: Use a struct when sending a Grafana AM configuration to the remote Alertmanager * remove '-distroless' from mimir image name
This commit is contained in:
+7
-7
@@ -879,7 +879,7 @@ services:
|
|||||||
- commands:
|
- commands:
|
||||||
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
||||||
environment: {}
|
environment: {}
|
||||||
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
name: mimir_backend
|
name: mimir_backend
|
||||||
- environment: {}
|
- environment: {}
|
||||||
image: redis:6.2.11-alpine
|
image: redis:6.2.11-alpine
|
||||||
@@ -1327,7 +1327,7 @@ services:
|
|||||||
- commands:
|
- commands:
|
||||||
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
||||||
environment: {}
|
environment: {}
|
||||||
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
name: mimir_backend
|
name: mimir_backend
|
||||||
- environment: {}
|
- environment: {}
|
||||||
image: redis:6.2.11-alpine
|
image: redis:6.2.11-alpine
|
||||||
@@ -2329,7 +2329,7 @@ services:
|
|||||||
- commands:
|
- commands:
|
||||||
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
||||||
environment: {}
|
environment: {}
|
||||||
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
name: mimir_backend
|
name: mimir_backend
|
||||||
- environment: {}
|
- environment: {}
|
||||||
image: redis:6.2.11-alpine
|
image: redis:6.2.11-alpine
|
||||||
@@ -4127,7 +4127,7 @@ services:
|
|||||||
- commands:
|
- commands:
|
||||||
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
- /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled
|
||||||
environment: {}
|
environment: {}
|
||||||
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
name: mimir_backend
|
name: mimir_backend
|
||||||
- environment: {}
|
- environment: {}
|
||||||
image: redis:6.2.11-alpine
|
image: redis:6.2.11-alpine
|
||||||
@@ -4646,7 +4646,7 @@ steps:
|
|||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack
|
||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM python:3.8
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM python:3.8
|
||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM postgres:12.3-alpine
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM postgres:12.3-alpine
|
||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:5.7.39
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:5.7.39
|
||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:8.0.32
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:8.0.32
|
||||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM redis:6.2.11-alpine
|
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM redis:6.2.11-alpine
|
||||||
@@ -4681,7 +4681,7 @@ steps:
|
|||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack
|
- trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack
|
||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL python:3.8
|
- trivy --exit-code 1 --severity HIGH,CRITICAL python:3.8
|
||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL postgres:12.3-alpine
|
- trivy --exit-code 1 --severity HIGH,CRITICAL postgres:12.3-alpine
|
||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
- trivy --exit-code 1 --severity HIGH,CRITICAL us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL mysql:5.7.39
|
- trivy --exit-code 1 --severity HIGH,CRITICAL mysql:5.7.39
|
||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL mysql:8.0.32
|
- trivy --exit-code 1 --severity HIGH,CRITICAL mysql:8.0.32
|
||||||
- trivy --exit-code 1 --severity HIGH,CRITICAL redis:6.2.11-alpine
|
- trivy --exit-code 1 --severity HIGH,CRITICAL redis:6.2.11-alpine
|
||||||
@@ -4925,6 +4925,6 @@ kind: secret
|
|||||||
name: gcr_credentials
|
name: gcr_credentials
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: 958ed40ca0620498c01fa867b05a1d6c3bcd908067fbb34be691923e95cfc77b
|
hmac: e67367689de11270bb3139f25a7cabf54e2b980460fd55ad410c590722d122b8
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mimir_backend:
|
mimir_backend:
|
||||||
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP
|
image: us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP
|
||||||
container_name: mimir_backend
|
container_name: mimir_backend
|
||||||
command:
|
command:
|
||||||
- -target=backend
|
- -target=backend
|
||||||
|
|||||||
@@ -166,7 +166,6 @@ func (am *Alertmanager) ApplyConfig(ctx context.Context, config *models.AlertCon
|
|||||||
am.log.Error("Unable to upload the state to the remote Alertmanager", "err", err)
|
am.log.Error("Unable to upload the state to the remote Alertmanager", "err", err)
|
||||||
}
|
}
|
||||||
am.log.Debug("Completed state upload to remote Alertmanager", "url", am.url)
|
am.log.Debug("Completed state upload to remote Alertmanager", "url", am.url)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,20 +201,16 @@ func (am *Alertmanager) CompareAndSendConfiguration(ctx context.Context, config
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rawDecrypted, err := json.Marshal(decrypted)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the configuration only if we need to.
|
// Send the configuration only if we need to.
|
||||||
if !am.shouldSendConfig(ctx, rawDecrypted) {
|
if !am.shouldSendConfig(ctx, &decrypted) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
am.metrics.ConfigSyncsTotal.Inc()
|
am.metrics.ConfigSyncsTotal.Inc()
|
||||||
if err := am.mimirClient.CreateGrafanaAlertmanagerConfig(
|
if err := am.mimirClient.CreateGrafanaAlertmanagerConfig(
|
||||||
ctx,
|
ctx,
|
||||||
string(rawDecrypted),
|
&decrypted,
|
||||||
config.ConfigurationHash,
|
config.ConfigurationHash,
|
||||||
config.CreatedAt,
|
config.CreatedAt,
|
||||||
config.Default,
|
config.Default,
|
||||||
@@ -447,15 +442,25 @@ func (am *Alertmanager) getFullState(ctx context.Context) (string, error) {
|
|||||||
|
|
||||||
// shouldSendConfig compares the remote Alertmanager configuration with our local one.
|
// shouldSendConfig compares the remote Alertmanager configuration with our local one.
|
||||||
// It returns true if the configurations are different.
|
// It returns true if the configurations are different.
|
||||||
func (am *Alertmanager) shouldSendConfig(ctx context.Context, rawConfig []byte) bool {
|
func (am *Alertmanager) shouldSendConfig(ctx context.Context, config *apimodels.PostableUserConfig) bool {
|
||||||
rc, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
|
rc, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log the error and return true so we try to upload our config anyway.
|
// Log the error and return true so we try to upload our config anyway.
|
||||||
am.log.Error("Unable to get the remote Alertmanager Configuration for comparison", "err", err)
|
am.log.Error("Unable to get the remote Alertmanager configuration for comparison", "err", err)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return md5.Sum([]byte(rc.GrafanaAlertmanagerConfig)) != md5.Sum(rawConfig)
|
rawRemote, err := json.Marshal(rc.GrafanaAlertmanagerConfig)
|
||||||
|
if err != nil {
|
||||||
|
am.log.Error("Unable to marshal the remote Alertmanager configuration for comparison", "err", err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
rawInternal, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
am.log.Error("Unable to marshal the internal Alertmanager configuration for comparison", "err", err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return md5.Sum(rawRemote) != md5.Sum(rawInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldSendState compares the remote Alertmanager state with our local one.
|
// shouldSendState compares the remote Alertmanager state with our local one.
|
||||||
|
|||||||
@@ -118,7 +118,9 @@ func TestApplyConfig(t *testing.T) {
|
|||||||
if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/config") {
|
if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/config") {
|
||||||
var c client.UserGrafanaConfig
|
var c client.UserGrafanaConfig
|
||||||
require.NoError(t, json.NewDecoder(r.Body).Decode(&c))
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&c))
|
||||||
configSent = c.GrafanaAlertmanagerConfig
|
amCfg, err := json.Marshal(c.GrafanaAlertmanagerConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
configSent = string(amCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
@@ -179,6 +181,8 @@ func TestApplyConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareAndSendConfiguration(t *testing.T) {
|
func TestCompareAndSendConfiguration(t *testing.T) {
|
||||||
|
cfgWithSecret, err := notifier.Load([]byte(testGrafanaConfigWithSecret))
|
||||||
|
require.NoError(t, err)
|
||||||
testValue := []byte("test")
|
testValue := []byte("test")
|
||||||
testErr := errors.New("test error")
|
testErr := errors.New("test error")
|
||||||
decryptFn := func(_ context.Context, payload []byte) ([]byte, error) {
|
decryptFn := func(_ context.Context, payload []byte) ([]byte, error) {
|
||||||
@@ -243,7 +247,7 @@ func TestCompareAndSendConfiguration(t *testing.T) {
|
|||||||
"no error",
|
"no error",
|
||||||
strings.Replace(testGrafanaConfigWithSecret, `"password":"test"`, fmt.Sprintf("%q:%q", "password", base64.StdEncoding.EncodeToString(testValue)), 1),
|
strings.Replace(testGrafanaConfigWithSecret, `"password":"test"`, fmt.Sprintf("%q:%q", "password", base64.StdEncoding.EncodeToString(testValue)), 1),
|
||||||
&client.UserGrafanaConfig{
|
&client.UserGrafanaConfig{
|
||||||
GrafanaAlertmanagerConfig: testGrafanaConfigWithSecret,
|
GrafanaAlertmanagerConfig: cfgWithSecret,
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
@@ -335,7 +339,10 @@ func TestIntegrationRemoteAlertmanagerApplyConfigOnlyUploadsOnce(t *testing.T) {
|
|||||||
// Next, we need to verify that Mimir received both the configuration and state.
|
// Next, we need to verify that Mimir received both the configuration and state.
|
||||||
config, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
|
config, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, testGrafanaConfig, config.GrafanaAlertmanagerConfig)
|
|
||||||
|
rawCfg, err := json.Marshal(config.GrafanaAlertmanagerConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, testGrafanaConfig, string(rawCfg))
|
||||||
require.Equal(t, fakeConfigHash, config.Hash)
|
require.Equal(t, fakeConfigHash, config.Hash)
|
||||||
require.Equal(t, fakeConfigCreatedAt, config.CreatedAt)
|
require.Equal(t, fakeConfigCreatedAt, config.CreatedAt)
|
||||||
require.Equal(t, true, config.Default)
|
require.Equal(t, true, config.Default)
|
||||||
@@ -358,7 +365,10 @@ func TestIntegrationRemoteAlertmanagerApplyConfigOnlyUploadsOnce(t *testing.T) {
|
|||||||
// Next, we need to verify that the config that was uploaded remains the same.
|
// Next, we need to verify that the config that was uploaded remains the same.
|
||||||
config, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
|
config, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, testGrafanaConfig, config.GrafanaAlertmanagerConfig)
|
|
||||||
|
rawCfg, err := json.Marshal(config.GrafanaAlertmanagerConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, testGrafanaConfig, string(rawCfg))
|
||||||
require.Equal(t, fakeConfigHash, config.Hash)
|
require.Equal(t, fakeConfigHash, config.Hash)
|
||||||
require.Equal(t, fakeConfigCreatedAt, config.CreatedAt)
|
require.Equal(t, fakeConfigCreatedAt, config.CreatedAt)
|
||||||
require.Equal(t, true, config.Default)
|
require.Equal(t, true, config.Default)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -13,10 +15,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UserGrafanaConfig struct {
|
type UserGrafanaConfig struct {
|
||||||
GrafanaAlertmanagerConfig string `json:"configuration"`
|
GrafanaAlertmanagerConfig *apimodels.PostableUserConfig `json:"configuration"`
|
||||||
Hash string `json:"configuration_hash"`
|
Hash string `json:"configuration_hash"`
|
||||||
CreatedAt int64 `json:"created"`
|
CreatedAt int64 `json:"created"`
|
||||||
Default bool `json:"default"`
|
Default bool `json:"default"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) {
|
func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) {
|
||||||
@@ -38,7 +40,7 @@ func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafana
|
|||||||
return gc, nil
|
return gc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *Mimir) CreateGrafanaAlertmanagerConfig(ctx context.Context, cfg, hash string, createdAt int64, isDefault bool) error {
|
func (mc *Mimir) CreateGrafanaAlertmanagerConfig(ctx context.Context, cfg *apimodels.PostableUserConfig, hash string, createdAt int64, isDefault bool) error {
|
||||||
payload, err := json.Marshal(&UserGrafanaConfig{
|
payload, err := json.Marshal(&UserGrafanaConfig{
|
||||||
GrafanaAlertmanagerConfig: cfg,
|
GrafanaAlertmanagerConfig: cfg,
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/client"
|
"github.com/grafana/grafana/pkg/services/ngalert/client"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
)
|
)
|
||||||
@@ -23,7 +24,7 @@ type MimirClient interface {
|
|||||||
DeleteGrafanaAlertmanagerState(ctx context.Context) error
|
DeleteGrafanaAlertmanagerState(ctx context.Context) error
|
||||||
|
|
||||||
GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error)
|
GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error)
|
||||||
CreateGrafanaAlertmanagerConfig(ctx context.Context, configuration, hash string, createdAt int64, isDefault bool) error
|
CreateGrafanaAlertmanagerConfig(ctx context.Context, configuration *apimodels.PostableUserConfig, hash string, createdAt int64, isDefault bool) error
|
||||||
DeleteGrafanaAlertmanagerConfig(ctx context.Context) error
|
DeleteGrafanaAlertmanagerConfig(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ images = {
|
|||||||
"plugins_slack": "plugins/slack",
|
"plugins_slack": "plugins/slack",
|
||||||
"python": "python:3.8",
|
"python": "python:3.8",
|
||||||
"postgres_alpine": "postgres:12.3-alpine",
|
"postgres_alpine": "postgres:12.3-alpine",
|
||||||
"mimir": "us.gcr.io/kubernetes-dev/mimir:santihernandezc-remove_id_from_grafana_config-d3826b4f8-WIP",
|
"mimir": "us.gcr.io/kubernetes-dev/mimir:santihernandezc-validate_grafana_am_config-1e903e462-WIP",
|
||||||
"mysql5": "mysql:5.7.39",
|
"mysql5": "mysql:5.7.39",
|
||||||
"mysql8": "mysql:8.0.32",
|
"mysql8": "mysql:8.0.32",
|
||||||
"redis_alpine": "redis:6.2.11-alpine",
|
"redis_alpine": "redis:6.2.11-alpine",
|
||||||
|
|||||||
Reference in New Issue
Block a user