diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index fa614b82c1d..ada52882d9c 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -25,6 +25,7 @@ var timeNow = time.Now type Alertmanager interface { // Configuration SaveAndApplyConfig(config *apimodels.PostableUserConfig) error + SaveAndApplyDefaultConfig() error GetStatus() apimodels.GettableStatus // Silences diff --git a/pkg/services/ngalert/api/api_alertmanager.go b/pkg/services/ngalert/api/api_alertmanager.go index c0b3dabb10f..4f8a0a24628 100644 --- a/pkg/services/ngalert/api/api_alertmanager.go +++ b/pkg/services/ngalert/api/api_alertmanager.go @@ -45,8 +45,15 @@ func (srv AlertmanagerSrv) RouteCreateSilence(c *models.ReqContext, postableSile } func (srv AlertmanagerSrv) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response { - // not implemented - return NotImplementedResp + if !c.HasUserRole(models.ROLE_EDITOR) { + return ErrResp(http.StatusForbidden, errors.New("permission denied"), "") + } + if err := srv.am.SaveAndApplyDefaultConfig(); err != nil { + srv.log.Error("unable to save and apply default alertmanager configuration", "err", err) + return ErrResp(http.StatusInternalServerError, err, "failed to save and apply default Alertmanager configuration") + } + + return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration deleted; the default is applied"}) } func (srv AlertmanagerSrv) RouteDeleteSilence(c *models.ReqContext) response.Response { diff --git a/pkg/services/ngalert/notifier/alertmanager.go b/pkg/services/ngalert/notifier/alertmanager.go index a763e9efb8a..3aced9ec938 100644 --- a/pkg/services/ngalert/notifier/alertmanager.go +++ b/pkg/services/ngalert/notifier/alertmanager.go @@ -201,6 +201,37 @@ func (am *Alertmanager) StopAndWait() error { return nil } +// SaveAndApplyDefaultConfig saves the default configuration the database and applies the configuration to the Alertmanager. +// It rollbacks the save if we fail to apply the configuration. +func (am *Alertmanager) SaveAndApplyDefaultConfig() error { + am.reloadConfigMtx.Lock() + defer am.reloadConfigMtx.Unlock() + + cmd := &ngmodels.SaveAlertmanagerConfigurationCmd{ + AlertmanagerConfiguration: alertmanagerDefaultConfiguration, + Default: true, + ConfigurationVersion: fmt.Sprintf("v%d", ngmodels.AlertConfigurationVersion), + } + + cfg, err := Load([]byte(alertmanagerDefaultConfiguration)) + if err != nil { + return err + } + + err = am.Store.SaveAlertmanagerConfigurationWithCallback(cmd, func() error { + if err := am.applyConfig(cfg, []byte(alertmanagerDefaultConfiguration)); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + am.Metrics.ActiveConfigurations.Set(1) + + return nil +} + // SaveAndApplyConfig saves the configuration the database and applies the configuration to the Alertmanager. // It rollbacks the save if we fail to apply the configuration. func (am *Alertmanager) SaveAndApplyConfig(cfg *apimodels.PostableUserConfig) error { diff --git a/pkg/tests/api/alerting/api_notification_channel_test.go b/pkg/tests/api/alerting/api_notification_channel_test.go index ce3aa7e429c..438f1d3eb01 100644 --- a/pkg/tests/api/alerting/api_notification_channel_test.go +++ b/pkg/tests/api/alerting/api_notification_channel_test.go @@ -122,6 +122,30 @@ func TestNotificationChannels(t *testing.T) { mockChannel.matchesExpNotifications(t, expNonEmailNotifications) require.Equal(t, expEmailNotifications, mockEmail.emails) require.NoError(t, mockChannel.Close()) + + { + // Delete the configuration; so it returns the default configuration. + u := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr) + req, err := http.NewRequest(http.MethodDelete, u, nil) + require.NoError(t, err) + client := &http.Client{} + resp, err := client.Do(req) + require.NoError(t, err) + t.Cleanup(func() { + err := resp.Body.Close() + require.NoError(t, err) + }) + b, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + require.Equal(t, 202, resp.StatusCode) + require.JSONEq(t, `{"message":"configuration deleted; the default is applied"}`, string(b)) + + alertsURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr) + resp = getRequest(t, alertsURL, http.StatusOK) // nolint + b, err = ioutil.ReadAll(resp.Body) + require.NoError(t, err) + require.JSONEq(t, defaultAlertmanagerConfigJSON, string(b)) + } } func getAlertmanagerConfig(channelAddr string) string {