mirror of https://github.com/grafana/grafana.git
Alerting: API to show converted alertmanager configurations in the UI (#109353)
What is this feature? This PR add the backend functionality to support viewing extra Alertmanager configurations (imported with the Prometheus conversion API) in the UI under the feature flag alertingImportAlertmanagerUI. The same flag will be used to enable this in the UI. This is just the backend part, the full PoC PR is here: #109027 It uses a special datasource UID prefix __grafana-converted-extra-config-{identifier} to identify imported configurations. When the Alertmanager proxy handler detects this prefix: GET requests are proxied to either the Grafana Alertmanager service (for alerts, silences, etc.) or the Prometheus conversion API to get the config Write operations are not supported
This commit is contained in:
parent
466aa70179
commit
587f52cf5b
|
|
@ -1012,6 +1012,11 @@ export interface FeatureToggles {
|
|||
*/
|
||||
alertingImportAlertmanagerAPI?: boolean;
|
||||
/**
|
||||
* Enables the UI to see imported Alertmanager configuration
|
||||
* @default false
|
||||
*/
|
||||
alertingImportAlertmanagerUI?: boolean;
|
||||
/**
|
||||
* Enables image sharing functionality for dashboards
|
||||
*/
|
||||
sharingDashboardImage?: boolean;
|
||||
|
|
|
|||
|
|
@ -1748,6 +1748,15 @@ var (
|
|||
HideFromDocs: true,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "alertingImportAlertmanagerUI",
|
||||
Description: "Enables the UI to see imported Alertmanager configuration",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaAlertingSquad,
|
||||
HideFromAdminPage: true,
|
||||
HideFromDocs: true,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "sharingDashboardImage",
|
||||
Description: "Enables image sharing functionality for dashboards",
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ restoreDashboards,experimental,@grafana/grafana-frontend-platform,false,false,fa
|
|||
skipTokenRotationIfRecent,GA,@grafana/identity-access-team,false,false,false
|
||||
alertEnrichment,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertingImportAlertmanagerAPI,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertingImportAlertmanagerUI,experimental,@grafana/alerting-squad,false,false,false
|
||||
sharingDashboardImage,experimental,@grafana/sharing-squad,false,false,true
|
||||
preferLibraryPanelTitle,privatePreview,@grafana/dashboards-squad,false,false,false
|
||||
tabularNumbers,GA,@grafana/grafana-frontend-platform,false,false,false
|
||||
|
|
|
|||
|
|
|
@ -919,6 +919,10 @@ const (
|
|||
// Enables the API to import Alertmanager configuration
|
||||
FlagAlertingImportAlertmanagerAPI = "alertingImportAlertmanagerAPI"
|
||||
|
||||
// FlagAlertingImportAlertmanagerUI
|
||||
// Enables the UI to see imported Alertmanager configuration
|
||||
FlagAlertingImportAlertmanagerUI = "alertingImportAlertmanagerUI"
|
||||
|
||||
// FlagSharingDashboardImage
|
||||
// Enables image sharing functionality for dashboards
|
||||
FlagSharingDashboardImage = "sharingDashboardImage"
|
||||
|
|
|
|||
|
|
@ -242,6 +242,21 @@
|
|||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertingImportAlertmanagerUI",
|
||||
"resourceVersion": "1754585847887",
|
||||
"creationTimestamp": "2025-08-07T16:57:27Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables the UI to see imported Alertmanager configuration",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/alerting-squad",
|
||||
"hideFromAdminPage": true,
|
||||
"hideFromDocs": true,
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertingImportYAMLUI",
|
||||
|
|
|
|||
|
|
@ -95,6 +95,16 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
|||
}
|
||||
ruleAuthzService := accesscontrol.NewRuleService(api.AccessControl)
|
||||
|
||||
convertSrv := NewConvertPrometheusSrv(
|
||||
&api.Cfg.UnifiedAlerting,
|
||||
logger,
|
||||
api.RuleStore,
|
||||
api.DatasourceCache,
|
||||
api.AlertRules,
|
||||
api.FeatureManager,
|
||||
api.MultiOrgAlertmanager,
|
||||
)
|
||||
|
||||
// Register endpoints for proxying to Alertmanager-compatible backends.
|
||||
api.RegisterAlertmanagerApiEndpoints(NewForkingAM(
|
||||
api.DatasourceCache,
|
||||
|
|
@ -115,6 +125,8 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
|||
),
|
||||
receiverAuthz: accesscontrol.NewReceiverAccess[ReceiverStatus](api.AccessControl, false),
|
||||
},
|
||||
convertSrv,
|
||||
api.FeatureManager,
|
||||
), m)
|
||||
// Register endpoints for proxying to Prometheus-compatible backends.
|
||||
api.RegisterPrometheusApiEndpoints(NewForkingProm(
|
||||
|
|
@ -181,15 +193,5 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
|||
hist: api.Historian,
|
||||
}), m)
|
||||
|
||||
api.RegisterConvertPrometheusApiEndpoints(NewConvertPrometheusApi(
|
||||
NewConvertPrometheusSrv(
|
||||
&api.Cfg.UnifiedAlerting,
|
||||
logger,
|
||||
api.RuleStore,
|
||||
api.DatasourceCache,
|
||||
api.AlertRules,
|
||||
api.FeatureManager,
|
||||
api.MultiOrgAlertmanager,
|
||||
),
|
||||
), m)
|
||||
api.RegisterConvertPrometheusApiEndpoints(NewConvertPrometheusApi(convertSrv), m)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,83 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
const extraConfigPrefix = "~grafana-converted-extra-config-"
|
||||
|
||||
type ConvertService interface {
|
||||
RouteConvertPrometheusGetAlertmanagerConfig(ctx *contextmodel.ReqContext) response.Response
|
||||
}
|
||||
|
||||
type AlertmanagerApiHandler struct {
|
||||
AMSvc *LotexAM
|
||||
GrafanaSvc *AlertmanagerSrv
|
||||
ConvertSvc ConvertService
|
||||
DatasourceCache datasources.CacheService
|
||||
FeatureManager featuremgmt.FeatureToggles
|
||||
}
|
||||
|
||||
// NewForkingAM implements a set of routes that proxy to various Alertmanager-compatible backends.
|
||||
func NewForkingAM(datasourceCache datasources.CacheService, proxy *LotexAM, grafana *AlertmanagerSrv) *AlertmanagerApiHandler {
|
||||
func NewForkingAM(datasourceCache datasources.CacheService, proxy *LotexAM, grafana *AlertmanagerSrv, convertSvc ConvertService, featureManager featuremgmt.FeatureToggles) *AlertmanagerApiHandler {
|
||||
return &AlertmanagerApiHandler{
|
||||
AMSvc: proxy,
|
||||
GrafanaSvc: grafana,
|
||||
ConvertSvc: convertSvc,
|
||||
DatasourceCache: datasourceCache,
|
||||
FeatureManager: featureManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) getService(ctx *contextmodel.ReqContext) (*LotexAM, error) {
|
||||
_, err := getDatasourceByUID(ctx, f.DatasourceCache, apimodels.AlertmanagerBackend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// If this is not an extra config request, we should check that the datasource exists and is of the correct type.
|
||||
if isExtra, _ := f.isExtraConfig(ctx); !isExtra {
|
||||
_, err := getDatasourceByUID(ctx, f.DatasourceCache, apimodels.AlertmanagerBackend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return f.AMSvc, nil
|
||||
}
|
||||
|
||||
// isExtraConfig checks if the datasourceUID represents an extra config.
|
||||
// Extra configs are the alertmanager configurations that were saved using the Prometheus conversion API.
|
||||
func (f *AlertmanagerApiHandler) isExtraConfig(ctx *contextmodel.ReqContext) (bool, string) {
|
||||
// Only enabled if feature flag is on
|
||||
if !f.FeatureManager.IsEnabledGlobally(featuremgmt.FlagAlertingImportAlertmanagerUI) {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
datasourceUID := web.Params(ctx.Req)[":DatasourceUID"]
|
||||
if strings.HasPrefix(datasourceUID, extraConfigPrefix) {
|
||||
identifier := strings.TrimPrefix(datasourceUID, extraConfigPrefix)
|
||||
return true, identifier
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetAMStatus(ctx *contextmodel.ReqContext, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
status := apimodels.GettableStatus{
|
||||
Cluster: &amv2.ClusterStatus{
|
||||
Status: util.Pointer("ready"),
|
||||
},
|
||||
}
|
||||
return response.JSON(http.StatusOK, status)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -40,6 +87,10 @@ func (f *AlertmanagerApiHandler) handleRouteGetAMStatus(ctx *contextmodel.ReqCon
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteCreateSilence(ctx *contextmodel.ReqContext, body apimodels.PostableSilence, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return response.Error(http.StatusForbidden, "Read-only configuration", nil)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -49,6 +100,10 @@ func (f *AlertmanagerApiHandler) handleRouteCreateSilence(ctx *contextmodel.ReqC
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteDeleteAlertingConfig(ctx *contextmodel.ReqContext, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return response.Error(http.StatusForbidden, "Read-only configuration", nil)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -58,6 +113,10 @@ func (f *AlertmanagerApiHandler) handleRouteDeleteAlertingConfig(ctx *contextmod
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteDeleteSilence(ctx *contextmodel.ReqContext, silenceID string, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return response.Error(http.StatusForbidden, "Read-only configuration", nil)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -67,6 +126,23 @@ func (f *AlertmanagerApiHandler) handleRouteDeleteSilence(ctx *contextmodel.ReqC
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetAlertingConfig(ctx *contextmodel.ReqContext, dsUID string) response.Response {
|
||||
if isExtra, identifier := f.isExtraConfig(ctx); isExtra {
|
||||
ctx.Req.Header.Set(configIdentifierHeader, identifier)
|
||||
ctx.Req.Header.Set("Accept", "application/yaml")
|
||||
|
||||
conversionResp := f.ConvertSvc.RouteConvertPrometheusGetAlertmanagerConfig(ctx)
|
||||
if conversionResp.Status() != http.StatusOK {
|
||||
return conversionResp
|
||||
}
|
||||
|
||||
config, err := yamlExtractor(&apimodels.GettableUserConfig{})(conversionResp.(*response.NormalResponse))
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to parse alertmanager config", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, config)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -76,6 +152,10 @@ func (f *AlertmanagerApiHandler) handleRouteGetAlertingConfig(ctx *contextmodel.
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetAMAlertGroups(ctx *contextmodel.ReqContext, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return f.GrafanaSvc.RouteGetAMAlertGroups(ctx)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -85,6 +165,10 @@ func (f *AlertmanagerApiHandler) handleRouteGetAMAlertGroups(ctx *contextmodel.R
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetAMAlerts(ctx *contextmodel.ReqContext, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return f.GrafanaSvc.RouteGetAMAlerts(ctx)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -94,6 +178,10 @@ func (f *AlertmanagerApiHandler) handleRouteGetAMAlerts(ctx *contextmodel.ReqCon
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetSilence(ctx *contextmodel.ReqContext, silenceID string, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return f.GrafanaSvc.RouteGetSilence(ctx, silenceID)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -103,6 +191,10 @@ func (f *AlertmanagerApiHandler) handleRouteGetSilence(ctx *contextmodel.ReqCont
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetSilences(ctx *contextmodel.ReqContext, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return f.GrafanaSvc.RouteGetSilences(ctx)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -112,6 +204,10 @@ func (f *AlertmanagerApiHandler) handleRouteGetSilences(ctx *contextmodel.ReqCon
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRoutePostAlertingConfig(ctx *contextmodel.ReqContext, body apimodels.PostableUserConfig, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return response.Error(http.StatusForbidden, "Read-only configuration", nil)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
@ -123,6 +219,10 @@ func (f *AlertmanagerApiHandler) handleRoutePostAlertingConfig(ctx *contextmodel
|
|||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRoutePostAMAlerts(ctx *contextmodel.ReqContext, body apimodels.PostableAlerts, dsUID string) response.Response {
|
||||
if isExtra, _ := f.isExtraConfig(ctx); isExtra {
|
||||
return response.Error(http.StatusForbidden, "Read-only configuration", nil)
|
||||
}
|
||||
|
||||
s, err := f.getService(ctx)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
func TestAlertmanagerApiHandler_isExtraConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
datasourceUID string
|
||||
flagEnabled bool
|
||||
expectedIsExtra bool
|
||||
expectedIdentifier string
|
||||
}{
|
||||
{
|
||||
name: "normal datasource when feature enabled",
|
||||
datasourceUID: "normal-datasource",
|
||||
flagEnabled: true,
|
||||
expectedIsExtra: false,
|
||||
expectedIdentifier: "",
|
||||
},
|
||||
{
|
||||
name: "extra config when feature enabled",
|
||||
datasourceUID: "~grafana-converted-extra-config-test-config",
|
||||
flagEnabled: true,
|
||||
expectedIsExtra: true,
|
||||
expectedIdentifier: "test-config",
|
||||
},
|
||||
{
|
||||
name: "extra config when feature disabled",
|
||||
datasourceUID: "~grafana-converted-extra-config-test-config",
|
||||
flagEnabled: false,
|
||||
expectedIsExtra: false,
|
||||
expectedIdentifier: "",
|
||||
},
|
||||
{
|
||||
name: "empty datasource UID",
|
||||
datasourceUID: "",
|
||||
flagEnabled: true,
|
||||
expectedIsExtra: false,
|
||||
expectedIdentifier: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := &http.Request{}
|
||||
ctx := &contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: req,
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.Req = web.SetURLParams(req, map[string]string{
|
||||
":DatasourceUID": tt.datasourceUID,
|
||||
})
|
||||
|
||||
var features featuremgmt.FeatureToggles
|
||||
if tt.flagEnabled {
|
||||
features = featuremgmt.WithFeatures(featuremgmt.FlagAlertingImportAlertmanagerUI)
|
||||
} else {
|
||||
features = featuremgmt.WithFeatures()
|
||||
}
|
||||
|
||||
handler := &AlertmanagerApiHandler{
|
||||
FeatureManager: features,
|
||||
}
|
||||
|
||||
isExtra, identifier := handler.isExtraConfig(ctx)
|
||||
assert.Equal(t, tt.expectedIsExtra, isExtra)
|
||||
assert.Equal(t, tt.expectedIdentifier, identifier)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertmanagerApiHandler_ExtraConfigRouting(t *testing.T) {
|
||||
t.Run("GET status returns ready for extra config", func(t *testing.T) {
|
||||
req := &http.Request{}
|
||||
ctx := &contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: req,
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.Req = web.SetURLParams(req, map[string]string{
|
||||
":DatasourceUID": "~grafana-converted-extra-config-test",
|
||||
})
|
||||
|
||||
handler := &AlertmanagerApiHandler{
|
||||
FeatureManager: featuremgmt.WithFeatures(featuremgmt.FlagAlertingImportAlertmanagerUI),
|
||||
}
|
||||
|
||||
resp := handler.handleRouteGetAMStatus(ctx, "~grafana-converted-extra-config-test")
|
||||
require.Equal(t, http.StatusOK, resp.Status())
|
||||
})
|
||||
|
||||
t.Run("POST operations return 403 for extra config", func(t *testing.T) {
|
||||
req := &http.Request{}
|
||||
ctx := &contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: req,
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.Req = web.SetURLParams(req, map[string]string{
|
||||
":DatasourceUID": "~grafana-converted-extra-config-test",
|
||||
})
|
||||
|
||||
handler := &AlertmanagerApiHandler{
|
||||
FeatureManager: featuremgmt.WithFeatures(featuremgmt.FlagAlertingImportAlertmanagerUI),
|
||||
}
|
||||
|
||||
resp := handler.handleRouteCreateSilence(ctx, apimodels.PostableSilence{}, "~grafana-converted-extra-config-test")
|
||||
assert.Equal(t, http.StatusForbidden, resp.Status())
|
||||
|
||||
resp = handler.handleRouteDeleteAlertingConfig(ctx, "~grafana-converted-extra-config-test")
|
||||
assert.Equal(t, http.StatusForbidden, resp.Status())
|
||||
|
||||
resp = handler.handleRoutePostAlertingConfig(ctx, apimodels.PostableUserConfig{}, "~grafana-converted-extra-config-test")
|
||||
assert.Equal(t, http.StatusForbidden, resp.Status())
|
||||
|
||||
resp = handler.handleRoutePostAMAlerts(ctx, apimodels.PostableAlerts{}, "~grafana-converted-extra-config-test")
|
||||
assert.Equal(t, http.StatusForbidden, resp.Status())
|
||||
})
|
||||
|
||||
t.Run("GET extra config", func(t *testing.T) {
|
||||
req := &http.Request{
|
||||
Header: make(http.Header),
|
||||
}
|
||||
ctx := &contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: req,
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.Req = web.SetURLParams(req, map[string]string{
|
||||
":DatasourceUID": "~grafana-converted-extra-config-test-identifier",
|
||||
})
|
||||
|
||||
mockConvertSvc := &mockConvertService{}
|
||||
|
||||
yamlConfig := `alertmanager_config: |
|
||||
global: {}
|
||||
route:
|
||||
receiver: test-receiver
|
||||
receivers:
|
||||
- name: test-receiver`
|
||||
|
||||
mockResponse := response.Respond(http.StatusOK, yamlConfig).
|
||||
SetHeader("Content-Type", "application/yaml")
|
||||
|
||||
mockConvertSvc.On("RouteConvertPrometheusGetAlertmanagerConfig", mock.Anything).
|
||||
Run(func(args mock.Arguments) {
|
||||
passedCtx := args.Get(0).(*contextmodel.ReqContext)
|
||||
assert.Equal(t, "test-identifier", passedCtx.Req.Header.Get(configIdentifierHeader))
|
||||
assert.Equal(t, "application/yaml", passedCtx.Req.Header.Get("Accept"))
|
||||
}).
|
||||
Return(mockResponse)
|
||||
|
||||
handler := &AlertmanagerApiHandler{
|
||||
FeatureManager: featuremgmt.WithFeatures(featuremgmt.FlagAlertingImportAlertmanagerUI),
|
||||
ConvertSvc: mockConvertSvc,
|
||||
}
|
||||
|
||||
resp := handler.handleRouteGetAlertingConfig(ctx, "~grafana-converted-extra-config-test-identifier")
|
||||
assert.Equal(t, http.StatusOK, resp.Status())
|
||||
|
||||
mockConvertSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
type mockConvertService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *mockConvertService) RouteConvertPrometheusGetAlertmanagerConfig(ctx *contextmodel.ReqContext) response.Response {
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).(response.Response)
|
||||
}
|
||||
Loading…
Reference in New Issue