From c3d1930d05d136cbb7e10f819a448f37718bcc29 Mon Sep 17 00:00:00 2001 From: Steve Simpson Date: Mon, 24 Apr 2023 20:43:23 +0200 Subject: [PATCH] [v9.5.x] Alerting: Allow hooking into request handler functions. (#67154) Alerting: Allow hooking into request handler functions. (#67000) * Alerting: Allow hooking into request handler functions. Adds a facility to AlertNG for hooking into API handlers, allowing the replacement of request handlers for specific paths. One of goals of this approach was to allow hooking as late as possible in the request, e.g. after all middleware has been applied, to simplfiy usage. * Update pkg/services/ngalert/api/hooks.go Co-authored-by: gotjosh * Update pkg/services/ngalert/api/hooks.go Co-authored-by: gotjosh * Update pkg/services/ngalert/ngalert.go Co-authored-by: gotjosh * Fixes to review comments * Fix passing logger in --------- Co-authored-by: gotjosh (cherry picked from commit 9effb9a708697f5d718df2c7bce7e5466251d964) --- pkg/services/ngalert/api/api.go | 3 ++ .../api/generated_base_api_alertmanager.go | 48 ++++++++--------- .../api/generated_base_api_configuration.go | 10 ++-- .../ngalert/api/generated_base_api_history.go | 2 +- .../api/generated_base_api_prometheus.go | 8 +-- .../api/generated_base_api_provisioning.go | 52 +++++++++---------- .../ngalert/api/generated_base_api_ruler.go | 24 ++++----- .../ngalert/api/generated_base_api_testing.go | 8 +-- pkg/services/ngalert/api/hooks.go | 43 +++++++++++++++ .../templates/controller-api.mustache | 2 +- pkg/services/ngalert/ngalert.go | 14 +++-- 11 files changed, 134 insertions(+), 80 deletions(-) create mode 100644 pkg/services/ngalert/api/hooks.go diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index f99a6e12d45..1c6e54fa257 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -85,6 +85,9 @@ type API struct { Historian Historian AppUrl *url.URL + + // Hooks can be used to replace API handlers for specific paths. + Hooks *Hooks } // RegisterAPIEndpoints registers API handlers diff --git a/pkg/services/ngalert/api/generated_base_api_alertmanager.go b/pkg/services/ngalert/api/generated_base_api_alertmanager.go index d474837b9ec..9b7911f8f2a 100644 --- a/pkg/services/ngalert/api/generated_base_api_alertmanager.go +++ b/pkg/services/ngalert/api/generated_base_api_alertmanager.go @@ -184,7 +184,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodPost, "/api/alertmanager/grafana/api/v2/silences", - srv.RouteCreateGrafanaSilence, + api.Hooks.Wrap(srv.RouteCreateGrafanaSilence), m, ), ) @@ -194,7 +194,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodPost, "/api/alertmanager/{DatasourceUID}/api/v2/silences", - srv.RouteCreateSilence, + api.Hooks.Wrap(srv.RouteCreateSilence), m, ), ) @@ -204,7 +204,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/alertmanager/{DatasourceUID}/config/api/v1/alerts", - srv.RouteDeleteAlertingConfig, + api.Hooks.Wrap(srv.RouteDeleteAlertingConfig), m, ), ) @@ -214,7 +214,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/alertmanager/grafana/config/api/v1/alerts", - srv.RouteDeleteGrafanaAlertingConfig, + api.Hooks.Wrap(srv.RouteDeleteGrafanaAlertingConfig), m, ), ) @@ -224,7 +224,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/alertmanager/grafana/api/v2/silence/{SilenceId}", - srv.RouteDeleteGrafanaSilence, + api.Hooks.Wrap(srv.RouteDeleteGrafanaSilence), m, ), ) @@ -234,7 +234,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/alertmanager/{DatasourceUID}/api/v2/silence/{SilenceId}", - srv.RouteDeleteSilence, + api.Hooks.Wrap(srv.RouteDeleteSilence), m, ), ) @@ -244,7 +244,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/{DatasourceUID}/api/v2/alerts/groups", - srv.RouteGetAMAlertGroups, + api.Hooks.Wrap(srv.RouteGetAMAlertGroups), m, ), ) @@ -254,7 +254,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/{DatasourceUID}/api/v2/alerts", - srv.RouteGetAMAlerts, + api.Hooks.Wrap(srv.RouteGetAMAlerts), m, ), ) @@ -264,7 +264,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/{DatasourceUID}/api/v2/status", - srv.RouteGetAMStatus, + api.Hooks.Wrap(srv.RouteGetAMStatus), m, ), ) @@ -274,7 +274,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/{DatasourceUID}/config/api/v1/alerts", - srv.RouteGetAlertingConfig, + api.Hooks.Wrap(srv.RouteGetAlertingConfig), m, ), ) @@ -284,7 +284,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/api/v2/alerts/groups", - srv.RouteGetGrafanaAMAlertGroups, + api.Hooks.Wrap(srv.RouteGetGrafanaAMAlertGroups), m, ), ) @@ -294,7 +294,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/api/v2/alerts", - srv.RouteGetGrafanaAMAlerts, + api.Hooks.Wrap(srv.RouteGetGrafanaAMAlerts), m, ), ) @@ -304,7 +304,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/api/v2/status", - srv.RouteGetGrafanaAMStatus, + api.Hooks.Wrap(srv.RouteGetGrafanaAMStatus), m, ), ) @@ -314,7 +314,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/config/api/v1/alerts", - srv.RouteGetGrafanaAlertingConfig, + api.Hooks.Wrap(srv.RouteGetGrafanaAlertingConfig), m, ), ) @@ -324,7 +324,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/config/history", - srv.RouteGetGrafanaAlertingConfigHistory, + api.Hooks.Wrap(srv.RouteGetGrafanaAlertingConfigHistory), m, ), ) @@ -334,7 +334,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/config/api/v1/receivers", - srv.RouteGetGrafanaReceivers, + api.Hooks.Wrap(srv.RouteGetGrafanaReceivers), m, ), ) @@ -344,7 +344,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/api/v2/silence/{SilenceId}", - srv.RouteGetGrafanaSilence, + api.Hooks.Wrap(srv.RouteGetGrafanaSilence), m, ), ) @@ -354,7 +354,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/grafana/api/v2/silences", - srv.RouteGetGrafanaSilences, + api.Hooks.Wrap(srv.RouteGetGrafanaSilences), m, ), ) @@ -364,7 +364,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/{DatasourceUID}/api/v2/silence/{SilenceId}", - srv.RouteGetSilence, + api.Hooks.Wrap(srv.RouteGetSilence), m, ), ) @@ -374,7 +374,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodGet, "/api/alertmanager/{DatasourceUID}/api/v2/silences", - srv.RouteGetSilences, + api.Hooks.Wrap(srv.RouteGetSilences), m, ), ) @@ -384,7 +384,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodPost, "/api/alertmanager/{DatasourceUID}/api/v2/alerts", - srv.RoutePostAMAlerts, + api.Hooks.Wrap(srv.RoutePostAMAlerts), m, ), ) @@ -394,7 +394,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodPost, "/api/alertmanager/{DatasourceUID}/config/api/v1/alerts", - srv.RoutePostAlertingConfig, + api.Hooks.Wrap(srv.RoutePostAlertingConfig), m, ), ) @@ -404,7 +404,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodPost, "/api/alertmanager/grafana/config/api/v1/alerts", - srv.RoutePostGrafanaAlertingConfig, + api.Hooks.Wrap(srv.RoutePostGrafanaAlertingConfig), m, ), ) @@ -414,7 +414,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics metrics.Instrument( http.MethodPost, "/api/alertmanager/grafana/config/api/v1/receivers/test", - srv.RoutePostTestGrafanaReceivers, + api.Hooks.Wrap(srv.RoutePostTestGrafanaReceivers), m, ), ) diff --git a/pkg/services/ngalert/api/generated_base_api_configuration.go b/pkg/services/ngalert/api/generated_base_api_configuration.go index c0685d12a1c..abb1bbc87ae 100644 --- a/pkg/services/ngalert/api/generated_base_api_configuration.go +++ b/pkg/services/ngalert/api/generated_base_api_configuration.go @@ -55,7 +55,7 @@ func (api *API) RegisterConfigurationApiEndpoints(srv ConfigurationApi, m *metri metrics.Instrument( http.MethodDelete, "/api/v1/ngalert/admin_config", - srv.RouteDeleteNGalertConfig, + api.Hooks.Wrap(srv.RouteDeleteNGalertConfig), m, ), ) @@ -65,7 +65,7 @@ func (api *API) RegisterConfigurationApiEndpoints(srv ConfigurationApi, m *metri metrics.Instrument( http.MethodGet, "/api/v1/ngalert/alertmanagers", - srv.RouteGetAlertmanagers, + api.Hooks.Wrap(srv.RouteGetAlertmanagers), m, ), ) @@ -75,7 +75,7 @@ func (api *API) RegisterConfigurationApiEndpoints(srv ConfigurationApi, m *metri metrics.Instrument( http.MethodGet, "/api/v1/ngalert/admin_config", - srv.RouteGetNGalertConfig, + api.Hooks.Wrap(srv.RouteGetNGalertConfig), m, ), ) @@ -85,7 +85,7 @@ func (api *API) RegisterConfigurationApiEndpoints(srv ConfigurationApi, m *metri metrics.Instrument( http.MethodGet, "/api/v1/ngalert", - srv.RouteGetStatus, + api.Hooks.Wrap(srv.RouteGetStatus), m, ), ) @@ -95,7 +95,7 @@ func (api *API) RegisterConfigurationApiEndpoints(srv ConfigurationApi, m *metri metrics.Instrument( http.MethodPost, "/api/v1/ngalert/admin_config", - srv.RoutePostNGalertConfig, + api.Hooks.Wrap(srv.RoutePostNGalertConfig), m, ), ) diff --git a/pkg/services/ngalert/api/generated_base_api_history.go b/pkg/services/ngalert/api/generated_base_api_history.go index 8f352d366c8..f8a616d2894 100644 --- a/pkg/services/ngalert/api/generated_base_api_history.go +++ b/pkg/services/ngalert/api/generated_base_api_history.go @@ -32,7 +32,7 @@ func (api *API) RegisterHistoryApiEndpoints(srv HistoryApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/v1/rules/history", - srv.RouteGetStateHistory, + api.Hooks.Wrap(srv.RouteGetStateHistory), m, ), ) diff --git a/pkg/services/ngalert/api/generated_base_api_prometheus.go b/pkg/services/ngalert/api/generated_base_api_prometheus.go index a4f04e25199..34b584d7a83 100644 --- a/pkg/services/ngalert/api/generated_base_api_prometheus.go +++ b/pkg/services/ngalert/api/generated_base_api_prometheus.go @@ -49,7 +49,7 @@ func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApi, m *metrics.API metrics.Instrument( http.MethodGet, "/api/prometheus/{DatasourceUID}/api/v1/alerts", - srv.RouteGetAlertStatuses, + api.Hooks.Wrap(srv.RouteGetAlertStatuses), m, ), ) @@ -59,7 +59,7 @@ func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApi, m *metrics.API metrics.Instrument( http.MethodGet, "/api/prometheus/grafana/api/v1/alerts", - srv.RouteGetGrafanaAlertStatuses, + api.Hooks.Wrap(srv.RouteGetGrafanaAlertStatuses), m, ), ) @@ -69,7 +69,7 @@ func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApi, m *metrics.API metrics.Instrument( http.MethodGet, "/api/prometheus/grafana/api/v1/rules", - srv.RouteGetGrafanaRuleStatuses, + api.Hooks.Wrap(srv.RouteGetGrafanaRuleStatuses), m, ), ) @@ -79,7 +79,7 @@ func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApi, m *metrics.API metrics.Instrument( http.MethodGet, "/api/prometheus/{DatasourceUID}/api/v1/rules", - srv.RouteGetRuleStatuses, + api.Hooks.Wrap(srv.RouteGetRuleStatuses), m, ), ) diff --git a/pkg/services/ngalert/api/generated_base_api_provisioning.go b/pkg/services/ngalert/api/generated_base_api_provisioning.go index f3e6ed66c32..6921fbc2007 100644 --- a/pkg/services/ngalert/api/generated_base_api_provisioning.go +++ b/pkg/services/ngalert/api/generated_base_api_provisioning.go @@ -212,7 +212,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/v1/provisioning/alert-rules/{UID}", - srv.RouteDeleteAlertRule, + api.Hooks.Wrap(srv.RouteDeleteAlertRule), m, ), ) @@ -222,7 +222,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/v1/provisioning/contact-points/{UID}", - srv.RouteDeleteContactpoints, + api.Hooks.Wrap(srv.RouteDeleteContactpoints), m, ), ) @@ -232,7 +232,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/v1/provisioning/mute-timings/{name}", - srv.RouteDeleteMuteTiming, + api.Hooks.Wrap(srv.RouteDeleteMuteTiming), m, ), ) @@ -242,7 +242,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/v1/provisioning/templates/{name}", - srv.RouteDeleteTemplate, + api.Hooks.Wrap(srv.RouteDeleteTemplate), m, ), ) @@ -252,7 +252,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/alert-rules/{UID}", - srv.RouteGetAlertRule, + api.Hooks.Wrap(srv.RouteGetAlertRule), m, ), ) @@ -262,7 +262,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/alert-rules/{UID}/export", - srv.RouteGetAlertRuleExport, + api.Hooks.Wrap(srv.RouteGetAlertRuleExport), m, ), ) @@ -272,7 +272,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}", - srv.RouteGetAlertRuleGroup, + api.Hooks.Wrap(srv.RouteGetAlertRuleGroup), m, ), ) @@ -282,7 +282,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}/export", - srv.RouteGetAlertRuleGroupExport, + api.Hooks.Wrap(srv.RouteGetAlertRuleGroupExport), m, ), ) @@ -292,7 +292,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/alert-rules", - srv.RouteGetAlertRules, + api.Hooks.Wrap(srv.RouteGetAlertRules), m, ), ) @@ -302,7 +302,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/alert-rules/export", - srv.RouteGetAlertRulesExport, + api.Hooks.Wrap(srv.RouteGetAlertRulesExport), m, ), ) @@ -312,7 +312,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/contact-points", - srv.RouteGetContactpoints, + api.Hooks.Wrap(srv.RouteGetContactpoints), m, ), ) @@ -322,7 +322,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/mute-timings/{name}", - srv.RouteGetMuteTiming, + api.Hooks.Wrap(srv.RouteGetMuteTiming), m, ), ) @@ -332,7 +332,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/mute-timings", - srv.RouteGetMuteTimings, + api.Hooks.Wrap(srv.RouteGetMuteTimings), m, ), ) @@ -342,7 +342,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/policies", - srv.RouteGetPolicyTree, + api.Hooks.Wrap(srv.RouteGetPolicyTree), m, ), ) @@ -352,7 +352,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/templates/{name}", - srv.RouteGetTemplate, + api.Hooks.Wrap(srv.RouteGetTemplate), m, ), ) @@ -362,7 +362,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodGet, "/api/v1/provisioning/templates", - srv.RouteGetTemplates, + api.Hooks.Wrap(srv.RouteGetTemplates), m, ), ) @@ -372,7 +372,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPost, "/api/v1/provisioning/alert-rules", - srv.RoutePostAlertRule, + api.Hooks.Wrap(srv.RoutePostAlertRule), m, ), ) @@ -382,7 +382,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPost, "/api/v1/provisioning/contact-points", - srv.RoutePostContactpoints, + api.Hooks.Wrap(srv.RoutePostContactpoints), m, ), ) @@ -392,7 +392,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPost, "/api/v1/provisioning/mute-timings", - srv.RoutePostMuteTiming, + api.Hooks.Wrap(srv.RoutePostMuteTiming), m, ), ) @@ -402,7 +402,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPut, "/api/v1/provisioning/alert-rules/{UID}", - srv.RoutePutAlertRule, + api.Hooks.Wrap(srv.RoutePutAlertRule), m, ), ) @@ -412,7 +412,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPut, "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}", - srv.RoutePutAlertRuleGroup, + api.Hooks.Wrap(srv.RoutePutAlertRuleGroup), m, ), ) @@ -422,7 +422,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPut, "/api/v1/provisioning/contact-points/{UID}", - srv.RoutePutContactpoint, + api.Hooks.Wrap(srv.RoutePutContactpoint), m, ), ) @@ -432,7 +432,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPut, "/api/v1/provisioning/mute-timings/{name}", - srv.RoutePutMuteTiming, + api.Hooks.Wrap(srv.RoutePutMuteTiming), m, ), ) @@ -442,7 +442,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPut, "/api/v1/provisioning/policies", - srv.RoutePutPolicyTree, + api.Hooks.Wrap(srv.RoutePutPolicyTree), m, ), ) @@ -452,7 +452,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodPut, "/api/v1/provisioning/templates/{name}", - srv.RoutePutTemplate, + api.Hooks.Wrap(srv.RoutePutTemplate), m, ), ) @@ -462,7 +462,7 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics metrics.Instrument( http.MethodDelete, "/api/v1/provisioning/policies", - srv.RouteResetPolicyTree, + api.Hooks.Wrap(srv.RouteResetPolicyTree), m, ), ) diff --git a/pkg/services/ngalert/api/generated_base_api_ruler.go b/pkg/services/ngalert/api/generated_base_api_ruler.go index b5687db2a3f..1142093103b 100644 --- a/pkg/services/ngalert/api/generated_base_api_ruler.go +++ b/pkg/services/ngalert/api/generated_base_api_ruler.go @@ -119,7 +119,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodDelete, "/api/ruler/grafana/api/v1/rules/{Namespace}/{Groupname}", - srv.RouteDeleteGrafanaRuleGroupConfig, + api.Hooks.Wrap(srv.RouteDeleteGrafanaRuleGroupConfig), m, ), ) @@ -129,7 +129,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodDelete, "/api/ruler/grafana/api/v1/rules/{Namespace}", - srv.RouteDeleteNamespaceGrafanaRulesConfig, + api.Hooks.Wrap(srv.RouteDeleteNamespaceGrafanaRulesConfig), m, ), ) @@ -139,7 +139,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodDelete, "/api/ruler/{DatasourceUID}/api/v1/rules/{Namespace}", - srv.RouteDeleteNamespaceRulesConfig, + api.Hooks.Wrap(srv.RouteDeleteNamespaceRulesConfig), m, ), ) @@ -149,7 +149,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodDelete, "/api/ruler/{DatasourceUID}/api/v1/rules/{Namespace}/{Groupname}", - srv.RouteDeleteRuleGroupConfig, + api.Hooks.Wrap(srv.RouteDeleteRuleGroupConfig), m, ), ) @@ -159,7 +159,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/ruler/grafana/api/v1/rules/{Namespace}/{Groupname}", - srv.RouteGetGrafanaRuleGroupConfig, + api.Hooks.Wrap(srv.RouteGetGrafanaRuleGroupConfig), m, ), ) @@ -169,7 +169,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/ruler/grafana/api/v1/rules", - srv.RouteGetGrafanaRulesConfig, + api.Hooks.Wrap(srv.RouteGetGrafanaRulesConfig), m, ), ) @@ -179,7 +179,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/ruler/grafana/api/v1/rules/{Namespace}", - srv.RouteGetNamespaceGrafanaRulesConfig, + api.Hooks.Wrap(srv.RouteGetNamespaceGrafanaRulesConfig), m, ), ) @@ -189,7 +189,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/ruler/{DatasourceUID}/api/v1/rules/{Namespace}", - srv.RouteGetNamespaceRulesConfig, + api.Hooks.Wrap(srv.RouteGetNamespaceRulesConfig), m, ), ) @@ -199,7 +199,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/ruler/{DatasourceUID}/api/v1/rules/{Namespace}/{Groupname}", - srv.RouteGetRulegGroupConfig, + api.Hooks.Wrap(srv.RouteGetRulegGroupConfig), m, ), ) @@ -209,7 +209,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodGet, "/api/ruler/{DatasourceUID}/api/v1/rules", - srv.RouteGetRulesConfig, + api.Hooks.Wrap(srv.RouteGetRulesConfig), m, ), ) @@ -219,7 +219,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodPost, "/api/ruler/grafana/api/v1/rules/{Namespace}", - srv.RoutePostNameGrafanaRulesConfig, + api.Hooks.Wrap(srv.RoutePostNameGrafanaRulesConfig), m, ), ) @@ -229,7 +229,7 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { metrics.Instrument( http.MethodPost, "/api/ruler/{DatasourceUID}/api/v1/rules/{Namespace}", - srv.RoutePostNameRulesConfig, + api.Hooks.Wrap(srv.RoutePostNameRulesConfig), m, ), ) diff --git a/pkg/services/ngalert/api/generated_base_api_testing.go b/pkg/services/ngalert/api/generated_base_api_testing.go index 6b3e104efab..342d0460b5f 100644 --- a/pkg/services/ngalert/api/generated_base_api_testing.go +++ b/pkg/services/ngalert/api/generated_base_api_testing.go @@ -68,7 +68,7 @@ func (api *API) RegisterTestingApiEndpoints(srv TestingApi, m *metrics.API) { metrics.Instrument( http.MethodPost, "/api/v1/rule/backtest", - srv.BacktestConfig, + api.Hooks.Wrap(srv.BacktestConfig), m, ), ) @@ -78,7 +78,7 @@ func (api *API) RegisterTestingApiEndpoints(srv TestingApi, m *metrics.API) { metrics.Instrument( http.MethodPost, "/api/v1/eval", - srv.RouteEvalQueries, + api.Hooks.Wrap(srv.RouteEvalQueries), m, ), ) @@ -88,7 +88,7 @@ func (api *API) RegisterTestingApiEndpoints(srv TestingApi, m *metrics.API) { metrics.Instrument( http.MethodPost, "/api/v1/rule/test/{DatasourceUID}", - srv.RouteTestRuleConfig, + api.Hooks.Wrap(srv.RouteTestRuleConfig), m, ), ) @@ -98,7 +98,7 @@ func (api *API) RegisterTestingApiEndpoints(srv TestingApi, m *metrics.API) { metrics.Instrument( http.MethodPost, "/api/v1/rule/test/grafana", - srv.RouteTestRuleGrafanaConfig, + api.Hooks.Wrap(srv.RouteTestRuleGrafanaConfig), m, ), ) diff --git a/pkg/services/ngalert/api/hooks.go b/pkg/services/ngalert/api/hooks.go new file mode 100644 index 00000000000..8b6c4dad804 --- /dev/null +++ b/pkg/services/ngalert/api/hooks.go @@ -0,0 +1,43 @@ +package api + +import ( + "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/infra/log" + contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" +) + +type RequestHandlerFunc func(*contextmodel.ReqContext) response.Response + +type Hooks struct { + logger log.Logger + hooks map[string]RequestHandlerFunc +} + +// NewHooks creates an empty set of request handler hooks. Hooks can be used +// to replace handlers for specific paths. +func NewHooks(logger log.Logger) *Hooks { + return &Hooks{ + logger: logger, + hooks: make(map[string]RequestHandlerFunc), + } +} + +// Add creates a new request hook for a path, causing requests to the path to +// be handled by the hook function, and not the original handler. +func (h *Hooks) Set(path string, hook RequestHandlerFunc) { + h.logger.Info("setting hook override for the specified route", "path", path) + h.hooks[path] = hook +} + +// Wrap returns a new handler which will intercept paths with hooks configured, +// and invoke the hooked in handler instead. If no hook is configured for a path, +// then the given handler is invoked. +func (h *Hooks) Wrap(next RequestHandlerFunc) RequestHandlerFunc { + return func(req *contextmodel.ReqContext) response.Response { + if hook, ok := h.hooks[req.Context.Req.URL.Path]; ok { + h.logger.Debug("hook defined - invoking new handler", "path", req.Context.Req.URL.Path) + return hook(req) + } + return next(req) + } +} diff --git a/pkg/services/ngalert/api/tooling/swagger-codegen/templates/controller-api.mustache b/pkg/services/ngalert/api/tooling/swagger-codegen/templates/controller-api.mustache index 7a7f7498108..7e15282fa38 100644 --- a/pkg/services/ngalert/api/tooling/swagger-codegen/templates/controller-api.mustache +++ b/pkg/services/ngalert/api/tooling/swagger-codegen/templates/controller-api.mustache @@ -40,7 +40,7 @@ func (api *API) Register{{classname}}Endpoints(srv {{classname}}, m *metrics.API metrics.Instrument( http.Method{{httpMethod}}, "{{{path}}}", - srv.{{nickname}}, + api.Hooks.Wrap(srv.{{nickname}}), m, ), ){{/operation}}{{/operations}} diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index 08f3c256fe2..7e4477bdb3d 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -127,6 +127,7 @@ type AlertNG struct { stateManager *state.Manager folderService folder.Service dashboardService dashboards.DashboardService + api *api.API // Alerting notification services MultiOrgAlertmanager *notifier.MultiOrgAlertmanager @@ -246,7 +247,7 @@ func (ng *AlertNG) init() error { int64(ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval.Seconds()), int64(ng.Cfg.UnifiedAlerting.BaseInterval.Seconds()), ng.Log) - api := api.API{ + ng.api = &api.API{ Cfg: ng.Cfg, DatasourceCache: ng.DataSourceCache, DatasourceService: ng.DataSourceService, @@ -271,8 +272,9 @@ func (ng *AlertNG) init() error { FeatureManager: ng.FeatureToggles, AppUrl: appUrl, Historian: history, + Hooks: api.NewHooks(ng.Log), } - api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics()) + ng.api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics()) defaultLimits, err := readQuotaConfig(ng.Cfg) if err != nil { @@ -282,7 +284,7 @@ func (ng *AlertNG) init() error { if err := ng.QuotaService.RegisterQuotaReporter("a.NewUsageReporter{ TargetSrv: models.QuotaTargetSrv, DefaultLimits: defaultLimits, - Reporter: api.Usage, + Reporter: ng.api.Usage, }); err != nil { return err } @@ -349,6 +351,12 @@ func (ng *AlertNG) IsDisabled() bool { return !ng.Cfg.UnifiedAlerting.IsEnabled() } +// GetHooks returns a facility for replacing handlers for paths. The handler hook for a path +// is invoked after all other middleware is invoked (authentication, instrumentation). +func (ng *AlertNG) GetHooks() *api.Hooks { + return ng.api.Hooks +} + func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) { limits := "a.Map{}