diff --git a/pkg/services/ngalert/notifier/alertmanager_config.go b/pkg/services/ngalert/notifier/alertmanager_config.go index 90d548d5e6c..cf500c86419 100644 --- a/pkg/services/ngalert/notifier/alertmanager_config.go +++ b/pkg/services/ngalert/notifier/alertmanager_config.go @@ -156,6 +156,13 @@ func (moa *MultiOrgAlertmanager) gettableUserConfigFromAMConfigString(ctx contex } func (moa *MultiOrgAlertmanager) ApplyAlertmanagerConfiguration(ctx context.Context, org int64, config definitions.PostableUserConfig) error { + // We cannot add this validation to PostableUserConfig as that struct is used for both + // Grafana Alertmanager (where inhibition rules are not supported) and External Alertmanagers + // (including Mimir) where inhibition rules are supported. + if len(config.AlertmanagerConfig.InhibitRules) > 0 { + return errors.New("inhibition rules are not supported") + } + // Get the last known working configuration _, err := moa.configStore.GetLatestAlertmanagerConfiguration(ctx, org) if err != nil { diff --git a/pkg/tests/api/alerting/api_admin_configuration_test.go b/pkg/tests/api/alerting/api_admin_configuration_test.go index 7ceae36e0b0..ed601c60ff2 100644 --- a/pkg/tests/api/alerting/api_admin_configuration_test.go +++ b/pkg/tests/api/alerting/api_admin_configuration_test.go @@ -10,11 +10,13 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/expr" + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/services/datasources" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -352,3 +354,49 @@ func TestIntegrationAdminConfiguration_SendingToExternalAlertmanagers(t *testing }, 16*time.Second, 8*time.Second) // the sync interval is 2s so after 8s all alertmanagers (if any) most probably are started } } + +func TestIntegrationAdminConfiguration_CannotCreateInhibitionRules(t *testing.T) { + testinfra.SQLiteIntegrationTest(t) + dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + DisableLegacyAlerting: true, + EnableUnifiedAlerting: true, + AppModeProduction: true, + }) + grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path) + createUser(t, store, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleAdmin), + Password: "admin", + Login: "admin", + }) + client := newAlertingApiClient(grafanaListedAddr, "admin", "admin") + + cfg := apimodels.PostableUserConfig{ + AlertmanagerConfig: apimodels.PostableApiAlertingConfig{ + Config: apimodels.Config{ + Route: &apimodels.Route{ + Receiver: "test", + }, + InhibitRules: []config.InhibitRule{{ + SourceMatchers: config.Matchers{{ + Type: labels.MatchEqual, + Name: "foo", + Value: "bar", + }}, + TargetMatchers: config.Matchers{{ + Type: labels.MatchEqual, + Name: "bar", + Value: "baz", + }}, + }}, + }, + Receivers: []*apimodels.PostableApiReceiver{{ + Receiver: config.Receiver{ + Name: "test", + }, + }}, + }, + } + ok, err := client.PostConfiguration(t, cfg) + require.False(t, ok) + require.EqualError(t, err, "inhibition rules are not supported") +} diff --git a/pkg/tests/api/alerting/testing.go b/pkg/tests/api/alerting/testing.go index 271546ee67a..7d3bb10aed8 100644 --- a/pkg/tests/api/alerting/testing.go +++ b/pkg/tests/api/alerting/testing.go @@ -3,6 +3,7 @@ package alerting import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -341,6 +342,39 @@ func (a apiClient) UpdateAlertRuleOrgQuota(t *testing.T, orgID int64, limit int6 assert.Equal(t, http.StatusOK, resp.StatusCode) } +func (a apiClient) PostConfiguration(t *testing.T, c apimodels.PostableUserConfig) (bool, error) { + t.Helper() + + b, err := json.Marshal(c) + require.NoError(t, err) + + u := fmt.Sprintf("%s/api/alertmanager/grafana/config/api/v1/alerts", a.url) + req, err := http.NewRequest(http.MethodPost, u, bytes.NewReader(b)) + require.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(req) + require.NoError(t, err) + require.NotNil(t, resp) + + defer func() { + _ = resp.Body.Close() + }() + b, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + data := struct { + Message string `json:"message"` + }{} + require.NoError(t, json.Unmarshal(b, &data)) + + if resp.StatusCode == http.StatusAccepted { + return true, nil + } + + return false, errors.New(data.Message) +} + func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) (apimodels.UpdateRuleGroupResponse, int, string) { t.Helper() buf := bytes.Buffer{}