Alerting: Remove the POST endpoint for the internal Grafana Alertmanager config (#103819)
* Remove POST config for Grafana Alertmanager * Delete auth + test for removed path * Alerting: Remove check for `alertingApiServer` toggle in UI (#103805) * Remove check for alertingApiServer in UI * Update tests to no longer care about alertingApiServer * Add contact points handlers now that we use alertingApiServer all the time * Fix test broken from removing camelCase for UIDs --------- Co-authored-by: Tom Ratcliffe <tom.ratcliffe@grafana.com>
This commit is contained in:
co-authored by
Tom Ratcliffe
parent
c47ab101d1
commit
a5288db624
@@ -44,7 +44,7 @@ func RegisterAPIService(
|
||||
cfg *setting.Cfg,
|
||||
ng *ngalert.AlertNG,
|
||||
) *NotificationsAPIBuilder {
|
||||
if ng.IsDisabled() || !features.IsEnabledGlobally(featuremgmt.FlagAlertingApiServer) {
|
||||
if ng.IsDisabled() {
|
||||
return nil
|
||||
}
|
||||
builder := &NotificationsAPIBuilder{
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func convertToK8sResource(orgID int64, r definitions.Route, version string, namespacer request.NamespaceMapper) (*model.RoutingTree, error) {
|
||||
func ConvertToK8sResource(orgID int64, r definitions.Route, version string, namespacer request.NamespaceMapper) (*model.RoutingTree, error) {
|
||||
spec := model.Spec{
|
||||
Defaults: model.RouteDefaults{
|
||||
GroupBy: r.GroupByStr,
|
||||
|
||||
@@ -65,7 +65,7 @@ func (s *legacyStorage) getUserDefinedRoutingTree(ctx context.Context) (*model.R
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertToK8sResource(orgId, res, version, s.namespacer)
|
||||
return ConvertToK8sResource(orgId, res, version, s.namespacer)
|
||||
}
|
||||
|
||||
func (s *legacyStorage) List(ctx context.Context, _ *internalversion.ListOptions) (runtime.Object, error) {
|
||||
@@ -131,7 +131,7 @@ func (s *legacyStorage) Update(ctx context.Context, name string, objInfo rest.Up
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
obj, err = convertToK8sResource(info.OrgID, updated, updatedVersion, s.namespacer)
|
||||
obj, err = ConvertToK8sResource(info.OrgID, updated, updatedVersion, s.namespacer)
|
||||
return obj, false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
)
|
||||
|
||||
func convertToK8sResources(orgID int64, intervals []definitions.MuteTimeInterval, namespacer request.NamespaceMapper, selector fields.Selector) (*model.TimeIntervalList, error) {
|
||||
func ConvertToK8sResources(orgID int64, intervals []definitions.MuteTimeInterval, namespacer request.NamespaceMapper, selector fields.Selector) (*model.TimeIntervalList, error) {
|
||||
data, err := json.Marshal(intervals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -39,7 +39,7 @@ func convertToK8sResources(orgID int64, intervals []definitions.MuteTimeInterval
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func convertToK8sResource(orgID int64, interval definitions.MuteTimeInterval, namespacer request.NamespaceMapper) (*model.TimeInterval, error) {
|
||||
func ConvertToK8sResource(orgID int64, interval definitions.MuteTimeInterval, namespacer request.NamespaceMapper) (*model.TimeInterval, error) {
|
||||
data, err := json.Marshal(interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -67,7 +67,7 @@ func (s *legacyStorage) List(ctx context.Context, opts *internalversion.ListOpti
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return convertToK8sResources(orgId, res, s.namespacer, opts.FieldSelector)
|
||||
return ConvertToK8sResources(orgId, res, s.namespacer, opts.FieldSelector)
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Get(ctx context.Context, uid string, _ *metav1.GetOptions) (runtime.Object, error) {
|
||||
@@ -83,7 +83,7 @@ func (s *legacyStorage) Get(ctx context.Context, uid string, _ *metav1.GetOption
|
||||
|
||||
for _, mt := range timings {
|
||||
if mt.UID == uid {
|
||||
return convertToK8sResource(info.OrgID, mt, s.namespacer)
|
||||
return ConvertToK8sResource(info.OrgID, mt, s.namespacer)
|
||||
}
|
||||
}
|
||||
return nil, errors.NewNotFound(ResourceInfo.GroupResource(), uid)
|
||||
@@ -118,7 +118,7 @@ func (s *legacyStorage) Create(ctx context.Context,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertToK8sResource(info.OrgID, out, s.namespacer)
|
||||
return ConvertToK8sResource(info.OrgID, out, s.namespacer)
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Update(ctx context.Context,
|
||||
@@ -165,7 +165,7 @@ func (s *legacyStorage) Update(ctx context.Context,
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
r, err := convertToK8sResource(info.OrgID, updated, s.namespacer)
|
||||
r, err := ConvertToK8sResource(info.OrgID, updated, s.namespacer)
|
||||
return r, false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -429,20 +429,14 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
|
||||
contactPointsPerms := []ac.Evaluator{
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsRead),
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsExternalRead),
|
||||
}
|
||||
|
||||
// With the new alerting API, we have other permissions to consider. We don't want to consider these with the old
|
||||
// alerting API to maintain backwards compatibility.
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingApiServer) {
|
||||
contactPointsPerms = append(contactPointsPerms,
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversRead),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversCreate),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversRead),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversCreate),
|
||||
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesRead),
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesWrite),
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesDelete),
|
||||
)
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesRead),
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesWrite),
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsTemplatesDelete),
|
||||
}
|
||||
|
||||
if hasAccess(ac.EvalAny(contactPointsPerms...)) {
|
||||
|
||||
@@ -381,10 +381,9 @@ func DeclareFixedRoles(service accesscontrol.Service, features featuremgmt.Featu
|
||||
instancesReaderRole, instancesWriterRole,
|
||||
notificationsReaderRole, notificationsWriterRole,
|
||||
alertingReaderRole, alertingWriterRole, alertingAdminRole, alertingProvisionerRole, alertingProvisioningReaderWithSecretsRole, alertingProvisioningStatus,
|
||||
}
|
||||
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagAlertingApiServer) {
|
||||
fixedRoles = append(fixedRoles, receiversReaderRole, receiversCreatorRole, receiversWriterRole, templatesReaderRole, templatesWriterRole, timeIntervalsReaderRole, timeIntervalsWriterRole, routesReaderRole, routesWriterRole)
|
||||
// k8s roles
|
||||
receiversReaderRole, receiversCreatorRole, receiversWriterRole, templatesReaderRole, templatesWriterRole,
|
||||
timeIntervalsReaderRole, timeIntervalsWriterRole, routesReaderRole, routesWriterRole,
|
||||
}
|
||||
|
||||
return service.DeclareFixedRoles(fixedRoles...)
|
||||
|
||||
@@ -188,52 +188,6 @@ func (srv AlertmanagerSrv) RoutePostGrafanaAlertingConfigHistoryActivate(c *cont
|
||||
return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration activated"})
|
||||
}
|
||||
|
||||
func (srv AlertmanagerSrv) RoutePostAlertingConfig(c *contextmodel.ReqContext, body apimodels.PostableUserConfig) response.Response {
|
||||
// Remove autogenerated config from the user config before checking provenance guard and eventually saving it.
|
||||
// TODO: This and provenance guard should be moved to the notifier package.
|
||||
notifier.RemoveAutogenConfigIfExists(body.AlertmanagerConfig.Route)
|
||||
currentConfig, err := srv.mam.GetAlertmanagerConfiguration(c.Req.Context(), c.GetOrgID(), false)
|
||||
// If a config is present and valid we proceed with the guard, otherwise we
|
||||
// just bypass the guard which is okay as we are anyway in an invalid state.
|
||||
if err == nil {
|
||||
if err := srv.provenanceGuard(currentConfig, body); err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
}
|
||||
if srv.featureManager.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingApiServer) {
|
||||
if err != nil {
|
||||
// Unclear if returning an error here is the right thing to do, preventing the user from posting a new config
|
||||
// when the current one is legitimately invalid is not optimal, but we need to ensure receiver
|
||||
// permissions are maintained and prevent potential access control bypasses. The workaround is to use the
|
||||
// various new k8s API endpoints to fix the configuration.
|
||||
return ErrResp(http.StatusInternalServerError, err, "")
|
||||
}
|
||||
if err := srv.k8sApiServiceGuard(currentConfig, body); err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
}
|
||||
err = srv.mam.SaveAndApplyAlertmanagerConfiguration(c.Req.Context(), c.GetOrgID(), body)
|
||||
if err == nil {
|
||||
return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration created"})
|
||||
}
|
||||
var unknownReceiverError notifier.UnknownReceiverError
|
||||
if errors.As(err, &unknownReceiverError) {
|
||||
return ErrResp(http.StatusBadRequest, unknownReceiverError, "")
|
||||
}
|
||||
var configRejectedError notifier.AlertmanagerConfigRejectedError
|
||||
if errors.As(err, &configRejectedError) {
|
||||
return ErrResp(http.StatusBadRequest, configRejectedError, "")
|
||||
}
|
||||
if errors.Is(err, notifier.ErrNoAlertmanagerForOrg) {
|
||||
return response.Error(http.StatusNotFound, err.Error(), err)
|
||||
}
|
||||
if errors.Is(err, notifier.ErrAlertmanagerNotReady) {
|
||||
return response.Error(http.StatusConflict, err.Error(), err)
|
||||
}
|
||||
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, err.Error(), err)
|
||||
}
|
||||
|
||||
func (srv AlertmanagerSrv) RouteGetReceivers(c *contextmodel.ReqContext) response.Response {
|
||||
am, errResp := srv.AlertmanagerFor(c.GetOrgID())
|
||||
if errResp != nil {
|
||||
|
||||
@@ -16,46 +16,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util/cmputil"
|
||||
)
|
||||
|
||||
func (srv AlertmanagerSrv) provenanceGuard(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
if err := checkRoutes(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkTemplates(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkContactPoints(currentConfig.AlertmanagerConfig.Receivers, newConfig.AlertmanagerConfig.Receivers); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkMuteTimes(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv AlertmanagerSrv) k8sApiServiceGuard(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
// Modifications to receivers via this API is tricky with new per-receiver RBAC. Assuming we restrict the API to only
|
||||
// those users with global edit permissions, we would still need to consider the following:
|
||||
// - Since the UIDs stored in the database for the purposes of per-receiver RBAC are generated based on the receiver
|
||||
// name, we would need to ensure continuity of permissions when a receiver is renamed. This would, preferably,
|
||||
// require detecting renames and updating the permissions UID in the database.
|
||||
// - Editors don't have permission to manage all receiver permissions by default, only admin users do. This means that
|
||||
// certain combined operations (e.g. swapping the name of receivers) would require careful handling to ensure
|
||||
// that the correct permissions are maintained, and considering receivers don't have a unique identifier outside
|
||||
// their name, this is non-trivial.
|
||||
|
||||
// Neither of these are insurmountable, but considering this endpoint will be removed once FlagAlertingApiServer
|
||||
// becomes GA, the complexity may not be worthwhile. To that end, for now we reject any request that attempts to
|
||||
// update receivers, while allowing operations that add or remove receivers.
|
||||
delta, err := calculateReceiversDelta(currentConfig.AlertmanagerConfig.Receivers, newConfig.AlertmanagerConfig.Receivers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(delta.Updated) > 0 {
|
||||
return fmt.Errorf("cannot update receivers using this API while per-receiver RBAC is enabled; either disable the `alertingApiServer` feature flag or use an API that supports per-receiver RBAC (e.g. provisioning or receivers API)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRoutes(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
reporter := cmputil.DiffReporter{}
|
||||
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(labels.Matcher{}), cmp.Transformer("", func(regexp amConfig.Regexp) any {
|
||||
|
||||
@@ -2,7 +2,6 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
@@ -101,90 +100,6 @@ func TestContextWithTimeoutFromRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAlertmanagerConfig(t *testing.T) {
|
||||
sut := createSut(t)
|
||||
|
||||
t.Run("assert 404 Not Found when applying config to nonexistent org", func(t *testing.T) {
|
||||
rc := contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: &http.Request{},
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 12,
|
||||
},
|
||||
}
|
||||
request := createAmConfigRequest(t, validConfig)
|
||||
|
||||
response := sut.RoutePostAlertingConfig(&rc, request)
|
||||
|
||||
require.Equal(t, 404, response.Status())
|
||||
require.Contains(t, string(response.Body()), "Alertmanager does not exist for this organization")
|
||||
})
|
||||
|
||||
t.Run("assert 202 when config successfully applied", func(t *testing.T) {
|
||||
rc := contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: &http.Request{},
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
},
|
||||
}
|
||||
request := createAmConfigRequest(t, validConfig)
|
||||
|
||||
response := sut.RoutePostAlertingConfig(&rc, request)
|
||||
|
||||
require.Equal(t, 202, response.Status())
|
||||
})
|
||||
|
||||
t.Run("assert 202 when alertmanager to configure is not ready", func(t *testing.T) {
|
||||
sut := createSut(t)
|
||||
rc := contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: &http.Request{},
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 3, // Org 3 was initialized with broken config.
|
||||
},
|
||||
}
|
||||
request := createAmConfigRequest(t, validConfig)
|
||||
|
||||
response := sut.RoutePostAlertingConfig(&rc, request)
|
||||
|
||||
require.Equal(t, 202, response.Status())
|
||||
})
|
||||
|
||||
t.Run("assert config hash doesn't change when sending RouteGetAlertingConfig back to RoutePostAlertingConfig", func(t *testing.T) {
|
||||
rc := contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: &http.Request{},
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
},
|
||||
}
|
||||
request := createAmConfigRequest(t, validConfigWithSecureSetting)
|
||||
|
||||
r := sut.RoutePostAlertingConfig(&rc, request)
|
||||
require.Equal(t, 202, r.Status())
|
||||
|
||||
getResponse := sut.RouteGetAlertingConfig(&rc)
|
||||
require.Equal(t, 200, getResponse.Status())
|
||||
|
||||
body := getResponse.Body()
|
||||
hash := md5.Sum(body)
|
||||
postable, err := notifier.Load(body)
|
||||
require.NoError(t, err)
|
||||
|
||||
r = sut.RoutePostAlertingConfig(&rc, *postable)
|
||||
require.Equal(t, 202, r.Status())
|
||||
|
||||
getResponse = sut.RouteGetAlertingConfig(&rc)
|
||||
require.Equal(t, 200, getResponse.Status())
|
||||
|
||||
newHash := md5.Sum(getResponse.Body())
|
||||
require.Equal(t, hash, newHash)
|
||||
})
|
||||
|
||||
t.Run("when objects are not provisioned", func(t *testing.T) {
|
||||
t.Run("route from GET config has no provenance", func(t *testing.T) {
|
||||
sut := createSut(t)
|
||||
@@ -229,9 +144,8 @@ func TestAlertmanagerConfig(t *testing.T) {
|
||||
t.Run("contact point from GET config has expected provenance", func(t *testing.T) {
|
||||
sut := createSut(t)
|
||||
rc := createRequestCtxInOrg(1)
|
||||
request := createAmConfigRequest(t, validConfig)
|
||||
|
||||
_ = sut.RoutePostAlertingConfig(rc, request)
|
||||
RoutePostAlertingConfig(t, sut.mam, rc, validConfig)
|
||||
|
||||
response := sut.RouteGetAlertingConfig(rc)
|
||||
body := asGettableUserConfig(t, response)
|
||||
@@ -347,11 +261,8 @@ func TestGetAlertmanagerConfiguration_NewSecretField(t *testing.T) {
|
||||
}
|
||||
}
|
||||
`
|
||||
postable := createAmConfigRequest(t, postWithoutChanges)
|
||||
|
||||
res = sut.RoutePostAlertingConfig(rc, postable)
|
||||
require.Equal(t, 202, res.Status())
|
||||
|
||||
RoutePostAlertingConfig(t, sut.mam, rc, postWithoutChanges)
|
||||
// Check that the secret field "integrationKey" is now encrypted in SecureSettings.
|
||||
savedConfig := &apimodels.PostableUserConfig{}
|
||||
err = json.Unmarshal([]byte(configs[orgId].AlertmanagerConfiguration), savedConfig)
|
||||
@@ -400,31 +311,6 @@ func TestAlertmanagerAutogenConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("route POST config", func(t *testing.T) {
|
||||
t.Run("does not save autogen routes", func(t *testing.T) {
|
||||
sut, configs := createSutForAutogen(t)
|
||||
rc := createRequestCtxInOrg(1)
|
||||
request := createAmConfigRequest(t, validConfigWithAutogen)
|
||||
response := sut.RoutePostAlertingConfig(rc, request)
|
||||
require.Equal(t, 202, response.Status())
|
||||
|
||||
compare(t, validConfigWithoutAutogen, configs[1].AlertmanagerConfiguration)
|
||||
})
|
||||
|
||||
t.Run("provenance guard ignores autogen routes", func(t *testing.T) {
|
||||
sut := createSut(t)
|
||||
rc := createRequestCtxInOrg(1)
|
||||
request := createAmConfigRequest(t, validConfigWithoutAutogen)
|
||||
_ = sut.RoutePostAlertingConfig(rc, request)
|
||||
|
||||
setRouteProvenance(t, 1, sut.mam.ProvStore)
|
||||
request = createAmConfigRequest(t, validConfigWithAutogen)
|
||||
request.AlertmanagerConfig.Route.Provenance = apimodels.Provenance(ngmodels.ProvenanceAPI)
|
||||
response := sut.RoutePostAlertingConfig(rc, request)
|
||||
require.Equal(t, 202, response.Status())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("route GET config", func(t *testing.T) {
|
||||
t.Run("when admin return autogen routes", func(t *testing.T) {
|
||||
sut, _ := createSutForAutogen(t)
|
||||
@@ -691,16 +577,6 @@ func createSut(t *testing.T) AlertmanagerSrv {
|
||||
}
|
||||
}
|
||||
|
||||
func createAmConfigRequest(t *testing.T, config string) apimodels.PostableUserConfig {
|
||||
t.Helper()
|
||||
|
||||
request := apimodels.PostableUserConfig{}
|
||||
err := request.UnmarshalJSON([]byte(config))
|
||||
require.NoError(t, err)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
func createMultiOrgAlertmanager(t *testing.T, configs map[int64]*ngmodels.AlertConfiguration) *notifier.MultiOrgAlertmanager {
|
||||
t.Helper()
|
||||
|
||||
@@ -849,40 +725,6 @@ var validConfigWithAutogen = `{
|
||||
}
|
||||
`
|
||||
|
||||
var validConfigWithSecureSetting = `{
|
||||
"template_files": {
|
||||
"a": "template"
|
||||
},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "grafana-default-email"
|
||||
},
|
||||
"receivers": [{
|
||||
"name": "grafana-default-email",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"uid": "",
|
||||
"name": "email receiver",
|
||||
"type": "email",
|
||||
"settings": {
|
||||
"addresses": "<example@email.com>"
|
||||
}
|
||||
}]},
|
||||
{
|
||||
"name": "slack",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"uid": "",
|
||||
"name": "slack1",
|
||||
"type": "slack",
|
||||
"settings": {"text": "slack text"},
|
||||
"secureSettings": {
|
||||
"url": "secure url"
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var brokenConfig = `
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
@@ -947,3 +789,13 @@ func asGettableHistoricUserConfigs(t *testing.T, r response.Response) []apimodel
|
||||
require.NoError(t, err)
|
||||
return body
|
||||
}
|
||||
|
||||
// RoutePostAlertingConfig drop-in replacement for removed POST endpoint to make test transition easier.
|
||||
func RoutePostAlertingConfig(t *testing.T, mam *notifier.MultiOrgAlertmanager, rc *contextmodel.ReqContext, amConfig string) {
|
||||
t.Helper()
|
||||
cfg := apimodels.PostableUserConfig{}
|
||||
err := json.Unmarshal([]byte(amConfig), &cfg)
|
||||
require.NoError(t, err)
|
||||
err = mam.SaveAndApplyAlertmanagerConfiguration(rc.Req.Context(), 1, cfg)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
@@ -229,9 +228,6 @@ func (api *API) authorize(method, path string) web.Handler {
|
||||
eval = ac.EvalPermission(ac.ActionAlertingNotificationsRead)
|
||||
case http.MethodGet + "/api/alertmanager/grafana/api/v2/status":
|
||||
eval = ac.EvalPermission(ac.ActionAlertingNotificationsRead)
|
||||
case http.MethodPost + "/api/alertmanager/grafana/config/api/v1/alerts":
|
||||
// additional authorization is done in the request handler
|
||||
eval = ac.EvalAny(ac.EvalPermission(ac.ActionAlertingNotificationsWrite))
|
||||
case http.MethodPost + "/api/alertmanager/grafana/config/history/{id}/_activate":
|
||||
eval = ac.EvalAny(ac.EvalPermission(ac.ActionAlertingNotificationsWrite))
|
||||
case http.MethodGet + "/api/alertmanager/grafana/config/api/v1/receivers":
|
||||
@@ -296,12 +292,9 @@ func (api *API) authorize(method, path string) web.Handler {
|
||||
ac.EvalPermission(ac.ActionAlertingProvisioningRead), // organization scope
|
||||
ac.EvalPermission(ac.ActionAlertingNotificationsProvisioningRead), // organization scope
|
||||
ac.EvalPermission(ac.ActionAlertingProvisioningReadSecrets), // organization scope
|
||||
}
|
||||
if api.FeatureManager.IsEnabledGlobally(featuremgmt.FlagAlertingApiServer) {
|
||||
perms = append(perms,
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversRead),
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets),
|
||||
)
|
||||
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversRead), // organization scope
|
||||
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets), // organization scope
|
||||
}
|
||||
eval = ac.EvalAny(perms...)
|
||||
|
||||
|
||||
@@ -175,13 +175,6 @@ func (f *AlertmanagerApiHandler) handleRouteGetGrafanaSilences(ctx *contextmodel
|
||||
return f.GrafanaSvc.RouteGetSilences(ctx)
|
||||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRoutePostGrafanaAlertingConfig(ctx *contextmodel.ReqContext, conf apimodels.PostableUserConfig) response.Response {
|
||||
if !conf.AlertmanagerConfig.ReceiverType().Can(apimodels.GrafanaReceiverType) {
|
||||
return errorToResponse(backendTypeDoesNotMatchPayloadTypeError(apimodels.GrafanaBackend, conf.AlertmanagerConfig.ReceiverType().String()))
|
||||
}
|
||||
return f.GrafanaSvc.RoutePostAlertingConfig(ctx, conf)
|
||||
}
|
||||
|
||||
func (f *AlertmanagerApiHandler) handleRouteGetGrafanaReceivers(ctx *contextmodel.ReqContext) response.Response {
|
||||
return f.GrafanaSvc.RouteGetReceivers(ctx)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ type AlertmanagerApi interface {
|
||||
RouteGetSilences(*contextmodel.ReqContext) response.Response
|
||||
RoutePostAMAlerts(*contextmodel.ReqContext) response.Response
|
||||
RoutePostAlertingConfig(*contextmodel.ReqContext) response.Response
|
||||
RoutePostGrafanaAlertingConfig(*contextmodel.ReqContext) response.Response
|
||||
RoutePostGrafanaAlertingConfigHistoryActivate(*contextmodel.ReqContext) response.Response
|
||||
RoutePostTestGrafanaReceivers(*contextmodel.ReqContext) response.Response
|
||||
RoutePostTestGrafanaTemplates(*contextmodel.ReqContext) response.Response
|
||||
@@ -162,14 +161,6 @@ func (f *AlertmanagerApiHandler) RoutePostAlertingConfig(ctx *contextmodel.ReqCo
|
||||
}
|
||||
return f.handleRoutePostAlertingConfig(ctx, conf, datasourceUIDParam)
|
||||
}
|
||||
func (f *AlertmanagerApiHandler) RoutePostGrafanaAlertingConfig(ctx *contextmodel.ReqContext) response.Response {
|
||||
// Parse Request Body
|
||||
conf := apimodels.PostableUserConfig{}
|
||||
if err := web.Bind(ctx.Req, &conf); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
return f.handleRoutePostGrafanaAlertingConfig(ctx, conf)
|
||||
}
|
||||
func (f *AlertmanagerApiHandler) RoutePostGrafanaAlertingConfigHistoryActivate(ctx *contextmodel.ReqContext) response.Response {
|
||||
// Parse Path Parameters
|
||||
idParam := web.Params(ctx.Req)[":id"]
|
||||
@@ -458,18 +449,6 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApi, m *metrics
|
||||
m,
|
||||
),
|
||||
)
|
||||
group.Post(
|
||||
toMacaronPath("/api/alertmanager/grafana/config/api/v1/alerts"),
|
||||
requestmeta.SetOwner(requestmeta.TeamAlerting),
|
||||
requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow),
|
||||
api.authorize(http.MethodPost, "/api/alertmanager/grafana/config/api/v1/alerts"),
|
||||
metrics.Instrument(
|
||||
http.MethodPost,
|
||||
"/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
api.Hooks.Wrap(srv.RoutePostGrafanaAlertingConfig),
|
||||
m,
|
||||
),
|
||||
)
|
||||
group.Post(
|
||||
toMacaronPath("/api/alertmanager/grafana/config/history/{id}/_activate"),
|
||||
requestmeta.SetOwner(requestmeta.TeamAlerting),
|
||||
|
||||
@@ -16,17 +16,6 @@ import (
|
||||
alertingmodels "github.com/grafana/alerting/models"
|
||||
)
|
||||
|
||||
// swagger:route POST /alertmanager/grafana/config/api/v1/alerts alertmanager RoutePostGrafanaAlertingConfig
|
||||
//
|
||||
// sets an Alerting config
|
||||
//
|
||||
// This API is designated to internal use only and can be removed or changed at any time without prior notice.
|
||||
//
|
||||
// Deprecated: true
|
||||
// Responses:
|
||||
// 201: Ack
|
||||
// 400: ValidationError
|
||||
|
||||
// swagger:route POST /alertmanager/{DatasourceUID}/config/api/v1/alerts alertmanager RoutePostAlertingConfig
|
||||
//
|
||||
// sets an Alerting config
|
||||
|
||||
@@ -5696,38 +5696,6 @@
|
||||
"tags": [
|
||||
"alertmanager"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"deprecated": true,
|
||||
"description": "This API is designated to internal use only and can be removed or changed at any time without prior notice.",
|
||||
"operationId": "RoutePostGrafanaAlertingConfig",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "Body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/PostableUserConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Ack",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Ack"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "ValidationError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"summary": "sets an Alerting config",
|
||||
"tags": [
|
||||
"alertmanager"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/alertmanager/grafana/config/api/v1/receivers": {
|
||||
|
||||
@@ -321,38 +321,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "This API is designated to internal use only and can be removed or changed at any time without prior notice.",
|
||||
"tags": [
|
||||
"alertmanager"
|
||||
],
|
||||
"summary": "sets an Alerting config",
|
||||
"operationId": "RoutePostGrafanaAlertingConfig",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/PostableUserConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Ack",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Ack"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "ValidationError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "This API is designated to internal use only and can be removed or changed at any time without prior notice.",
|
||||
"tags": [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
//
|
||||
// Frame[0] {
|
||||
// "type": "directory-listing",
|
||||
// "typeVersion": [
|
||||
@@ -10,7 +10,7 @@
|
||||
// "HasMore": false
|
||||
// }
|
||||
// }
|
||||
// Name:
|
||||
// Name:
|
||||
// Dimensions: 3 Fields by 3 Rows
|
||||
// +----------------------------+----------------------+---------------+
|
||||
// | Name: name | Name: mediaType | Name: size |
|
||||
@@ -87,4 +87,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -354,49 +352,3 @@ 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, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
createUser(t, env.SQLStore, env.Cfg, 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")
|
||||
}
|
||||
|
||||
@@ -1,554 +0,0 @@
|
||||
package alerting
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
"github.com/prometheus/alertmanager/timeinterval"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
)
|
||||
|
||||
func TestIntegrationAlertmanagerConfiguration(t *testing.T) {
|
||||
testinfra.SQLiteIntegrationTest(t)
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
AppModeProduction: true,
|
||||
})
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleAdmin),
|
||||
Password: "admin",
|
||||
Login: "admin",
|
||||
})
|
||||
client := newAlertingApiClient(grafanaListedAddr, "admin", "admin")
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
cfg apimodels.PostableUserConfig
|
||||
expErr string
|
||||
}{{
|
||||
name: "configuration with default route",
|
||||
cfg: apimodels.PostableUserConfig{
|
||||
AlertmanagerConfig: apimodels.PostableApiAlertingConfig{
|
||||
Config: apimodels.Config{
|
||||
Route: &apimodels.Route{
|
||||
Receiver: "test",
|
||||
},
|
||||
},
|
||||
Receivers: []*apimodels.PostableApiReceiver{{
|
||||
Receiver: config.Receiver{
|
||||
Name: "test",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "configuration with UTF-8 matchers",
|
||||
cfg: apimodels.PostableUserConfig{
|
||||
AlertmanagerConfig: apimodels.PostableApiAlertingConfig{
|
||||
Config: apimodels.Config{
|
||||
Route: &apimodels.Route{
|
||||
Receiver: "test",
|
||||
Routes: []*apimodels.Route{{
|
||||
GroupBy: []model.LabelName{"foo🙂"},
|
||||
Matchers: config.Matchers{{
|
||||
Type: labels.MatchEqual,
|
||||
Name: "foo🙂",
|
||||
Value: "bar",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "_bar1",
|
||||
Value: "baz🙂",
|
||||
}, {
|
||||
Type: labels.MatchRegexp,
|
||||
Name: "0baz",
|
||||
Value: "[a-zA-Z0-9]+,?",
|
||||
}, {
|
||||
Type: labels.MatchNotRegexp,
|
||||
Name: "corge",
|
||||
Value: "^[0-9]+((,[0-9]{3})*(,[0-9]{0,3})?)?$",
|
||||
}, {
|
||||
Type: labels.MatchEqual,
|
||||
Name: "Προμηθέας", // Prometheus in Greek
|
||||
Value: "Prom",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "犬", // Dog in Japanese
|
||||
Value: "Shiba Inu",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
Receivers: []*apimodels.PostableApiReceiver{{
|
||||
Receiver: config.Receiver{
|
||||
Name: "test",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "configuration with UTF-8 object matchers",
|
||||
cfg: apimodels.PostableUserConfig{
|
||||
AlertmanagerConfig: apimodels.PostableApiAlertingConfig{
|
||||
Config: apimodels.Config{
|
||||
Route: &apimodels.Route{
|
||||
Receiver: "test",
|
||||
Routes: []*apimodels.Route{{
|
||||
GroupBy: []model.LabelName{"foo🙂"},
|
||||
ObjectMatchers: apimodels.ObjectMatchers{{
|
||||
Type: labels.MatchEqual,
|
||||
Name: "foo🙂",
|
||||
Value: "bar",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "_bar1",
|
||||
Value: "baz🙂",
|
||||
}, {
|
||||
Type: labels.MatchRegexp,
|
||||
Name: "0baz",
|
||||
Value: "[a-zA-Z0-9]+,?",
|
||||
}, {
|
||||
Type: labels.MatchNotRegexp,
|
||||
Name: "corge",
|
||||
Value: "^[0-9]+((,[0-9]{3})*(,[0-9]{0,3})?)?$",
|
||||
}, {
|
||||
Type: labels.MatchEqual,
|
||||
Name: "Προμηθέας", // Prometheus in Greek
|
||||
Value: "Prom",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "犬", // Dog in Japanese
|
||||
Value: "Shiba Inu",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
Receivers: []*apimodels.PostableApiReceiver{{
|
||||
Receiver: config.Receiver{
|
||||
Name: "test",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "configuration with UTF-8 in both matchers and object matchers",
|
||||
cfg: apimodels.PostableUserConfig{
|
||||
AlertmanagerConfig: apimodels.PostableApiAlertingConfig{
|
||||
Config: apimodels.Config{
|
||||
Route: &apimodels.Route{
|
||||
Receiver: "test",
|
||||
Routes: []*apimodels.Route{{
|
||||
GroupBy: []model.LabelName{"foo🙂"},
|
||||
Matchers: config.Matchers{{
|
||||
Type: labels.MatchEqual,
|
||||
Name: "foo🙂",
|
||||
Value: "bar",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "_bar1",
|
||||
Value: "baz🙂",
|
||||
}, {
|
||||
Type: labels.MatchRegexp,
|
||||
Name: "0baz",
|
||||
Value: "[a-zA-Z0-9]+,?",
|
||||
}, {
|
||||
Type: labels.MatchNotRegexp,
|
||||
Name: "corge",
|
||||
Value: "^[0-9]+((,[0-9]{3})*(,[0-9]{0,3})?)?$",
|
||||
}},
|
||||
ObjectMatchers: apimodels.ObjectMatchers{{
|
||||
Type: labels.MatchEqual,
|
||||
Name: "Προμηθέας", // Prometheus in Greek
|
||||
Value: "Prom",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "犬", // Dog in Japanese
|
||||
Value: "Shiba Inu",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
Receivers: []*apimodels.PostableApiReceiver{{
|
||||
Receiver: config.Receiver{
|
||||
Name: "test",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// TODO: Mute time intervals is deprecated in Alertmanager and scheduled to be
|
||||
// removed before version 1.0. Remove this test when support for mute time
|
||||
// intervals is removed.
|
||||
name: "configuration with mute time intervals",
|
||||
cfg: apimodels.PostableUserConfig{
|
||||
AlertmanagerConfig: apimodels.PostableApiAlertingConfig{
|
||||
Config: apimodels.Config{
|
||||
Route: &apimodels.Route{
|
||||
Receiver: "test",
|
||||
Routes: []*apimodels.Route{{
|
||||
MuteTimeIntervals: []string{"weekends"},
|
||||
}},
|
||||
},
|
||||
MuteTimeIntervals: []config.MuteTimeInterval{{
|
||||
Name: "weekends",
|
||||
TimeIntervals: []timeinterval.TimeInterval{{
|
||||
Weekdays: []timeinterval.WeekdayRange{{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 5,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
Receivers: []*apimodels.PostableApiReceiver{{
|
||||
Receiver: config.Receiver{
|
||||
Name: "test",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "configuration with time intervals",
|
||||
cfg: apimodels.PostableUserConfig{
|
||||
AlertmanagerConfig: apimodels.PostableApiAlertingConfig{
|
||||
Config: apimodels.Config{
|
||||
Route: &apimodels.Route{
|
||||
Receiver: "test",
|
||||
Routes: []*apimodels.Route{{
|
||||
MuteTimeIntervals: []string{"weekends"},
|
||||
}},
|
||||
},
|
||||
TimeIntervals: []config.TimeInterval{{
|
||||
Name: "weekends",
|
||||
TimeIntervals: []timeinterval.TimeInterval{{
|
||||
Weekdays: []timeinterval.WeekdayRange{{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 5,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
Receivers: []*apimodels.PostableApiReceiver{{
|
||||
Receiver: config.Receiver{
|
||||
Name: "test",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ok, err := client.PostConfiguration(t, tc.cfg)
|
||||
if tc.expErr != "" {
|
||||
require.EqualError(t, err, tc.expErr)
|
||||
require.False(t, ok)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationAlertmanagerConfigurationIsTransactional(t *testing.T) {
|
||||
testinfra.SQLiteIntegrationTest(t)
|
||||
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
NGAlertAlertmanagerConfigPollInterval: 2 * time.Second,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
||||
orgService, err := orgimpl.ProvideService(env.SQLStore, env.Cfg, quotatest.New(false, nil))
|
||||
require.NoError(t, err)
|
||||
|
||||
// editor from main organisation requests configuration
|
||||
alertConfigURL := fmt.Sprintf("http://editor:editor@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
|
||||
// create user under main organisation
|
||||
userID := createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleEditor),
|
||||
Password: "editor",
|
||||
Login: "editor",
|
||||
})
|
||||
|
||||
// create another organisation
|
||||
newOrg, err := orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: "another org", UserID: userID})
|
||||
require.NoError(t, err)
|
||||
orgID := newOrg.ID
|
||||
|
||||
// create user under different organisation
|
||||
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleEditor),
|
||||
Password: "editor-42",
|
||||
Login: "editor-42",
|
||||
OrgID: orgID,
|
||||
})
|
||||
|
||||
// On a blank start with no configuration, it saves and delivers the default configuration.
|
||||
{
|
||||
resp := getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
require.JSONEq(t, defaultAlertmanagerConfigJSON, getBody(t, resp.Body))
|
||||
}
|
||||
|
||||
// When creating new configuration, if it fails to apply - it does not save it.
|
||||
{
|
||||
payload := `
|
||||
{
|
||||
"template_files": {},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "slack.receiver"
|
||||
},
|
||||
"receivers": [{
|
||||
"name": "slack.receiver",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"settings": {
|
||||
"iconEmoji": "",
|
||||
"iconUrl": "",
|
||||
"mentionGroups": "",
|
||||
"mentionUsers": "",
|
||||
"recipient": "#unified-alerting-test",
|
||||
"username": ""
|
||||
},
|
||||
"secureSettings": {},
|
||||
"type": "slack",
|
||||
"name": "slack.receiver",
|
||||
"disableResolveMessage": false,
|
||||
"uid": ""
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`
|
||||
resp := postRequest(t, alertConfigURL, payload, http.StatusBadRequest) // nolint
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
var res map[string]any
|
||||
require.NoError(t, json.Unmarshal(b, &res))
|
||||
require.Regexp(t, `^failed to save and apply Alertmanager configuration: failed to validate integration "slack.receiver" \(UID [^\)]+\) of type "slack": token must be specified when using the Slack chat API`, res["message"])
|
||||
resp = getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
|
||||
require.JSONEq(t, defaultAlertmanagerConfigJSON, getBody(t, resp.Body))
|
||||
}
|
||||
|
||||
// editor42 from organisation 42 posts configuration
|
||||
alertConfigURL = fmt.Sprintf("http://editor-42:editor-42@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
|
||||
// Before we start operating, make sure we've synced this org.
|
||||
require.Eventually(t, func() bool {
|
||||
resp, err := http.Get(alertConfigURL) // nolint
|
||||
require.NoError(t, err)
|
||||
return resp.StatusCode == http.StatusOK
|
||||
}, 10*time.Second, 2*time.Second)
|
||||
|
||||
// Post the alertmanager config.
|
||||
{
|
||||
mockChannel := newMockNotificationChannel(t, grafanaListedAddr)
|
||||
amConfig := getAlertmanagerConfig(mockChannel.server.Addr)
|
||||
postRequest(t, alertConfigURL, amConfig, http.StatusAccepted) // nolint
|
||||
|
||||
// Verifying that the new configuration is returned
|
||||
resp := getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
b := getBody(t, resp.Body)
|
||||
re := regexp.MustCompile(`"uid":"([\w|-]*)"`)
|
||||
e := getExpAlertmanagerConfigFromAPI(mockChannel.server.Addr)
|
||||
require.JSONEq(t, e, string(re.ReplaceAll([]byte(b), []byte(`"uid":""`))))
|
||||
}
|
||||
|
||||
// verify that main organisation still gets the default configuration
|
||||
alertConfigURL = fmt.Sprintf("http://editor:editor@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
{
|
||||
resp := getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
require.JSONEq(t, defaultAlertmanagerConfigJSON, getBody(t, resp.Body))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationAlertmanagerConfigurationPersistSecrets(t *testing.T) {
|
||||
testinfra.SQLiteIntegrationTest(t)
|
||||
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
DisableFeatureToggles: []string{featuremgmt.FlagAlertingApiServer},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
alertConfigURL := fmt.Sprintf("http://editor:editor@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
|
||||
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleEditor),
|
||||
Password: "editor",
|
||||
Login: "editor",
|
||||
})
|
||||
generatedUID := ""
|
||||
|
||||
// create a new configuration that has a secret
|
||||
{
|
||||
payload := `
|
||||
{
|
||||
"template_files": {},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "slack.receiver"
|
||||
},
|
||||
"receivers": [{
|
||||
"name": "slack.receiver",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"settings": {
|
||||
"recipient": "#unified-alerting-test"
|
||||
},
|
||||
"secureSettings": {
|
||||
"url": "http://averysecureurl.com/webhook"
|
||||
},
|
||||
"type": "slack",
|
||||
"name": "slack.receiver",
|
||||
"disableResolveMessage": false
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`
|
||||
resp := postRequest(t, alertConfigURL, payload, http.StatusAccepted) // nolint
|
||||
require.JSONEq(t, `{"message":"configuration created"}`, getBody(t, resp.Body))
|
||||
}
|
||||
|
||||
// Try to update a receiver with unknown UID
|
||||
{
|
||||
// Then, update the recipient
|
||||
payload := `
|
||||
{
|
||||
"template_files": {},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "slack.receiver"
|
||||
},
|
||||
"receivers": [{
|
||||
"name": "slack.receiver",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"settings": {
|
||||
"recipient": "#unified-alerting-test-but-updated"
|
||||
},
|
||||
"secureFields": {
|
||||
"url": true
|
||||
},
|
||||
"type": "slack",
|
||||
"name": "slack.receiver",
|
||||
"disableResolveMessage": false,
|
||||
"uid": "invalid"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
resp := postRequest(t, alertConfigURL, payload, http.StatusBadRequest) // nolint
|
||||
s := getBody(t, resp.Body)
|
||||
var res map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(s), &res))
|
||||
require.Equal(t, "unknown receiver: invalid", res["message"])
|
||||
}
|
||||
|
||||
// The secure settings must be present
|
||||
{
|
||||
resp := getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
var c apimodels.GettableUserConfig
|
||||
bb := getBody(t, resp.Body)
|
||||
err := json.Unmarshal([]byte(bb), &c)
|
||||
require.NoError(t, err)
|
||||
m := c.GetGrafanaReceiverMap()
|
||||
assert.Len(t, m, 1)
|
||||
for k := range m {
|
||||
generatedUID = m[k].UID
|
||||
}
|
||||
|
||||
// Then, update the recipient
|
||||
payload := fmt.Sprintf(`
|
||||
{
|
||||
"template_files": {},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "slack.receiver"
|
||||
},
|
||||
"receivers": [{
|
||||
"name": "slack.receiver",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"settings": {
|
||||
"recipient": "#unified-alerting-test-but-updated"
|
||||
},
|
||||
"secureFields": {
|
||||
"url": true
|
||||
},
|
||||
"type": "slack",
|
||||
"name": "slack.receiver",
|
||||
"disableResolveMessage": false,
|
||||
"uid": %q
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`, generatedUID)
|
||||
|
||||
resp = postRequest(t, alertConfigURL, payload, http.StatusAccepted) // nolint
|
||||
require.JSONEq(t, `{"message": "configuration created"}`, getBody(t, resp.Body))
|
||||
}
|
||||
|
||||
// The secure settings must be present
|
||||
{
|
||||
resp := getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
require.JSONEq(t, fmt.Sprintf(`
|
||||
{
|
||||
"template_files": {},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "slack.receiver"
|
||||
},
|
||||
"receivers": [{
|
||||
"name": "slack.receiver",
|
||||
"grafana_managed_receiver_configs": [{
|
||||
"uid": %q,
|
||||
"name": "slack.receiver",
|
||||
"type": "slack",
|
||||
"disableResolveMessage": false,
|
||||
"settings": {
|
||||
"recipient": "#unified-alerting-test-but-updated"
|
||||
},
|
||||
"secureFields": {
|
||||
"url": true
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`, generatedUID), getBody(t, resp.Body))
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package alerting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -16,7 +17,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/errutil"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@@ -32,9 +32,6 @@ func TestIntegrationAMConfigAccess(t *testing.T) {
|
||||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
DisableFeatureToggles: []string{
|
||||
featuremgmt.FlagAlertingApiServer,
|
||||
},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
@@ -63,8 +60,9 @@ func TestIntegrationAMConfigAccess(t *testing.T) {
|
||||
expBody string
|
||||
}
|
||||
|
||||
t.Run("when creating alertmanager configuration", func(t *testing.T) {
|
||||
body := `
|
||||
// Create alertmanager config
|
||||
cfg := apimodels.PostableUserConfig{}
|
||||
amConfig := `
|
||||
{
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
@@ -85,51 +83,10 @@ func TestIntegrationAMConfigAccess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
desc: "un-authenticated request should fail",
|
||||
url: "http://%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusUnauthorized,
|
||||
expBody: `"message":"Unauthorized"`,
|
||||
},
|
||||
{
|
||||
desc: "viewer request should fail",
|
||||
url: "http://viewer:viewer@%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusForbidden,
|
||||
expBody: `"title":"Access denied"`,
|
||||
},
|
||||
{
|
||||
desc: "editor request should succeed",
|
||||
url: "http://editor:editor@%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusAccepted,
|
||||
expBody: `{"message":"configuration created"}`,
|
||||
},
|
||||
{
|
||||
desc: "admin request should succeed",
|
||||
url: "http://admin:admin@%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusAccepted,
|
||||
expBody: `{"message":"configuration created"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
url := fmt.Sprintf(tc.url, grafanaListedAddr)
|
||||
buf := bytes.NewReader([]byte(body))
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(url, "application/json", buf)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, resp.Body.Close())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expStatus, resp.StatusCode)
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(b), tc.expBody)
|
||||
})
|
||||
}
|
||||
})
|
||||
err := json.Unmarshal([]byte(amConfig), &cfg)
|
||||
require.NoError(t, err)
|
||||
err = env.Server.HTTPServer.AlertNG.MultiOrgAlertmanager.SaveAndApplyAlertmanagerConfiguration(context.Background(), 1, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("when retrieve alertmanager configuration", func(t *testing.T) {
|
||||
cfgTemplate := `
|
||||
@@ -329,7 +286,7 @@ func TestIntegrationAMConfigAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
var silences apimodels.GettableSilences
|
||||
err := json.Unmarshal(blob, &silences)
|
||||
err = json.Unmarshal(blob, &silences)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, silences, 2)
|
||||
silenceIDs := make([]string, 0, len(silences))
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
@@ -249,112 +248,6 @@ func TestIntegrationTestReceivers(t *testing.T) {
|
||||
require.JSONEq(t, expectedJSON, string(b))
|
||||
})
|
||||
|
||||
t.Run("assert working receiver with existing secure settings returns OK", func(t *testing.T) {
|
||||
// Setup Grafana and its Database
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
AppModeProduction: true,
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
||||
createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleEditor),
|
||||
Login: "grafana",
|
||||
Password: "password",
|
||||
})
|
||||
|
||||
mockChannel := newMockNotificationChannel(t, grafanaListedAddr)
|
||||
amConfig := createAlertmanagerConfig(`{
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "receiver-1"
|
||||
},
|
||||
"receivers": [{
|
||||
"name":"receiver-1",
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"uid": "",
|
||||
"name": "receiver-1",
|
||||
"type": "slack",
|
||||
"disableResolveMessage": false,
|
||||
"settings": {},
|
||||
"secureSettings": {"url": "http://CHANNEL_ADDR/slack_recv1/slack_test_without_token"}
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}`, mockChannel.server.Addr)
|
||||
|
||||
// Set up responses
|
||||
mockChannel.responses["slack_recv1"] = `{"ok": true}`
|
||||
|
||||
// Post config
|
||||
u := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
_ = postRequest(t, u, amConfig, http.StatusAccepted) // nolint
|
||||
|
||||
// Get am config with UID and without secureSettings
|
||||
resp := getRequest(t, u, http.StatusOK)
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
config, err := notifier.Load(b)
|
||||
require.NoError(t, err)
|
||||
body, err := json.Marshal(apimodels.TestReceiversConfigBodyParams{
|
||||
Receivers: config.AlertmanagerConfig.Receivers,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test using the am config without secureSettings
|
||||
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
|
||||
// nolint
|
||||
resp = postRequest(t, testReceiversURL, string(body), http.StatusOK)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
b, err = io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
var result apimodels.TestReceiversResult
|
||||
require.NoError(t, json.Unmarshal(b, &result))
|
||||
require.Len(t, result.Receivers, 1)
|
||||
require.Len(t, result.Receivers[0].Configs, 1)
|
||||
|
||||
expectedJSON := fmt.Sprintf(`{
|
||||
"alert": {
|
||||
"annotations": {
|
||||
"summary": "Notification test",
|
||||
"__dashboardUid__": "dashboard_uid",
|
||||
"__orgId__": "1",
|
||||
"__panelId__": "1",
|
||||
"__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]",
|
||||
"__values__": "{\"B\":22,\"C\":1}"
|
||||
},
|
||||
"labels": {
|
||||
"alertname": "TestAlert",
|
||||
"grafana_folder": "Test Folder",
|
||||
"instance": "Grafana"
|
||||
}
|
||||
},
|
||||
"receivers": [{
|
||||
"name":"receiver-1",
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"name": "receiver-1",
|
||||
"uid": "%s",
|
||||
"status": "ok"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"notified_at": "%s"
|
||||
}`,
|
||||
result.Receivers[0].Configs[0].UID,
|
||||
result.NotifiedAt.Format(time.RFC3339Nano))
|
||||
require.JSONEq(t, expectedJSON, string(b))
|
||||
})
|
||||
|
||||
t.Run("assert invalid receiver returns 400 Bad Request", func(t *testing.T) {
|
||||
// Setup Grafana and its Database
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
@@ -1008,8 +901,11 @@ func TestIntegrationNotificationChannels(t *testing.T) {
|
||||
apiClient.CreateFolder(t, "default", "default")
|
||||
|
||||
// Post the alertmanager config.
|
||||
u := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
_ = postRequest(t, u, amConfig, http.StatusAccepted) // nolint
|
||||
cfg := apimodels.PostableUserConfig{}
|
||||
err := json.Unmarshal([]byte(amConfig), &cfg)
|
||||
require.NoError(t, err)
|
||||
err = env.Server.HTTPServer.AlertNG.MultiOrgAlertmanager.SaveAndApplyAlertmanagerConfiguration(context.Background(), 1, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verifying that all the receivers and routes have been registered.
|
||||
alertsURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
@@ -1025,7 +921,7 @@ func TestIntegrationNotificationChannels(t *testing.T) {
|
||||
b = getBody(t, resp.Body)
|
||||
|
||||
var receivers []apimodels.Receiver
|
||||
err := json.Unmarshal([]byte(b), &receivers)
|
||||
err = json.Unmarshal([]byte(b), &receivers)
|
||||
require.NoError(t, err)
|
||||
for _, rcv := range receivers {
|
||||
require.NotNil(t, rcv.Name)
|
||||
|
||||
@@ -3288,7 +3288,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
|
||||
"intervalSeconds":60,
|
||||
"is_paused": false,
|
||||
"version":1,
|
||||
"uid":"uid",
|
||||
"uid":"uid",
|
||||
"guid": "guid",
|
||||
"namespace_uid":"nsuid",
|
||||
"rule_group":"arulegroup",
|
||||
@@ -4451,32 +4451,6 @@ func TestIntegrationRuleNotificationSettings(t *testing.T) {
|
||||
t.Log(body)
|
||||
})
|
||||
|
||||
t.Run("create with '...' groupBy followed by config post should succeed", func(t *testing.T) {
|
||||
var copyD testData
|
||||
err = json.Unmarshal(testDataRaw, ©D)
|
||||
group := copyD.RuleGroup
|
||||
ns := group.Rules[0].GrafanaManagedAlert.NotificationSettings
|
||||
ns.GroupBy = []string{ngmodels.FolderTitleLabel, model.AlertNameLabel, ngmodels.GroupByAll}
|
||||
|
||||
_, status, body := apiClient.PostRulesGroupWithStatus(t, folder, &group, false)
|
||||
require.Equalf(t, http.StatusAccepted, status, body)
|
||||
|
||||
// Now update the config with no changes.
|
||||
_, status, body = apiClient.GetAlertmanagerConfigWithStatus(t)
|
||||
if !assert.Equalf(t, http.StatusOK, status, body) {
|
||||
return
|
||||
}
|
||||
|
||||
cfg := apimodels.PostableUserConfig{}
|
||||
|
||||
err = json.Unmarshal([]byte(body), &cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
ok, err := apiClient.PostConfiguration(t, cfg)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("should create rule and generate route", func(t *testing.T) {
|
||||
_, status, body := apiClient.PostRulesGroupWithStatus(t, folder, &d.RuleGroup, false)
|
||||
require.Equalf(t, http.StatusAccepted, status, body)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -420,40 +419,6 @@ 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, permanentlyDelete bool) (apimodels.UpdateRuleGroupResponse, int, string) {
|
||||
t.Helper()
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
v0alpha1_receiver "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/receiver/v0alpha1"
|
||||
v0alpha1_routingtree "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/routingtree/v0alpha1"
|
||||
v0alpha1_templategroup "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/templategroup/v0alpha1"
|
||||
v0alpha1_timeinterval "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1"
|
||||
)
|
||||
|
||||
func NewReceiverClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1_receiver.Receiver, v0alpha1_receiver.ReceiverList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1_receiver.Receiver, v0alpha1_receiver.ReceiverList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1_receiver.Kind().Group(),
|
||||
Version: v0alpha1_receiver.Kind().Version(),
|
||||
Resource: v0alpha1_receiver.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
|
||||
func NewRoutingTreeClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1_routingtree.RoutingTree, v0alpha1_routingtree.RoutingTreeList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1_routingtree.RoutingTree, v0alpha1_routingtree.RoutingTreeList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1_routingtree.Kind().Group(),
|
||||
Version: v0alpha1_routingtree.Kind().Version(),
|
||||
Resource: v0alpha1_routingtree.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTemplateGroupClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1_templategroup.TemplateGroup, v0alpha1_templategroup.TemplateGroupList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1_templategroup.TemplateGroup, v0alpha1_templategroup.TemplateGroupList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1_templategroup.Kind().Group(),
|
||||
Version: v0alpha1_templategroup.Kind().Version(),
|
||||
Resource: v0alpha1_templategroup.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTimeIntervalClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1_timeinterval.TimeInterval, v0alpha1_timeinterval.TimeIntervalList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1_timeinterval.TimeInterval, v0alpha1_timeinterval.TimeIntervalList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1_timeinterval.Kind().Group(),
|
||||
Version: v0alpha1_timeinterval.Kind().Version(),
|
||||
Resource: v0alpha1_timeinterval.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
@@ -14,18 +14,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/alerting/notify"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/receiver/v0alpha1"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
|
||||
test_common "github.com/grafana/grafana/pkg/tests/apis/alerting/notifications/common"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@@ -67,7 +68,7 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
client := newClient(t, helper.Org1.Admin)
|
||||
client := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
newResource := &v0alpha1.Receiver{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
@@ -145,7 +146,7 @@ func TestIntegrationResourcePermissions(t *testing.T) {
|
||||
admin := org1.Admin
|
||||
viewer := org1.Viewer
|
||||
editor := org1.Editor
|
||||
adminClient := newClient(t, admin)
|
||||
adminClient := test_common.NewReceiverClient(t, admin)
|
||||
|
||||
writeACMetadata := []string{"canWrite", "canDelete"}
|
||||
allACMetadata := []string{"canWrite", "canDelete", "canReadSecrets", "canAdmin"}
|
||||
@@ -297,8 +298,8 @@ func TestIntegrationResourcePermissions(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
createClient := newClient(t, tc.creatingUser)
|
||||
client := newClient(t, tc.testUser)
|
||||
createClient := test_common.NewReceiverClient(t, tc.creatingUser)
|
||||
client := test_common.NewReceiverClient(t, tc.testUser)
|
||||
|
||||
var created = &v0alpha1.Receiver{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -561,10 +562,10 @@ func TestIntegrationAccessControl(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("user '%s'", tc.user.Identity.GetLogin()), func(t *testing.T) {
|
||||
client := newClient(t, tc.user)
|
||||
client := test_common.NewReceiverClient(t, tc.user)
|
||||
|
||||
var expected = &v0alpha1.Receiver{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -739,7 +740,7 @@ func TestIntegrationInUseMetadata(t *testing.T) {
|
||||
cliCfg := helper.Org1.Admin.NewRestConfig()
|
||||
legacyCli := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
// Prepare environment and create notification policy and rule that use receiver
|
||||
alertmanagerRaw, err := testData.ReadFile(path.Join("test-data", "notification-settings.json"))
|
||||
require.NoError(t, err)
|
||||
@@ -754,7 +755,7 @@ func TestIntegrationInUseMetadata(t *testing.T) {
|
||||
parentRoute.Routes = []*definitions.Route{&route1, &route2}
|
||||
amConfig.AlertmanagerConfig.Route.Routes = append(amConfig.AlertmanagerConfig.Route.Routes, &parentRoute)
|
||||
|
||||
persistInitialConfig(t, amConfig, adminClient, legacyCli)
|
||||
persistInitialConfig(t, amConfig)
|
||||
|
||||
postGroupRaw, err := testData.ReadFile(path.Join("test-data", "rulegroup-1.json"))
|
||||
require.NoError(t, err)
|
||||
@@ -817,8 +818,11 @@ func TestIntegrationInUseMetadata(t *testing.T) {
|
||||
|
||||
// Removing the new extra route should leave only 1.
|
||||
amConfig.AlertmanagerConfig.Route.Routes = amConfig.AlertmanagerConfig.Route.Routes[:1]
|
||||
success, err := legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Failed to post Alertmanager configuration: %s", err)
|
||||
v1Route, err := routingtree.ConvertToK8sResource(helper.Org1.AdminServiceAccount.OrgId, *amConfig.AlertmanagerConfig.Route, "", func(int64) string { return "default" })
|
||||
require.NoError(t, err)
|
||||
routeAdminClient := test_common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
_, err = routeAdminClient.Update(ctx, v1Route, v1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
receiverListed, receiverGet = requestReceivers(t, "user-defined")
|
||||
checkInUse(t, receiverListed, receiverGet, 1, 2)
|
||||
@@ -834,11 +838,14 @@ func TestIntegrationInUseMetadata(t *testing.T) {
|
||||
receiverListed, receiverGet = requestReceivers(t, "grafana-default-email")
|
||||
checkInUse(t, receiverListed, receiverGet, 1, 0)
|
||||
|
||||
// Remove the rest.
|
||||
// Remove the remaining routes.
|
||||
amConfig.AlertmanagerConfig.Route.Routes = nil
|
||||
success, err = legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Failed to post Alertmanager configuration: %s", err)
|
||||
v1route, err := routingtree.ConvertToK8sResource(1, *amConfig.AlertmanagerConfig.Route, "", func(int64) string { return "default" })
|
||||
require.NoError(t, err)
|
||||
_, err = routeAdminClient.Update(ctx, v1route, v1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Remove the remaining rules.
|
||||
ruleGroup.Rules = nil
|
||||
_, status, data = legacyCli.PostRulesGroupWithStatus(t, folderUID, &ruleGroup, false)
|
||||
require.Equalf(t, http.StatusAccepted, status, "Failed to post Rule: %s", data)
|
||||
@@ -861,7 +868,7 @@ func TestIntegrationProvisioning(t *testing.T) {
|
||||
org := helper.Org1
|
||||
|
||||
admin := org.Admin
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
env := helper.GetEnv()
|
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles)
|
||||
db, err := store.ProvideDBStore(env.Cfg, env.FeatureToggles, env.SQLStore, &foldertest.FakeService{}, &dashboards.FakeDashboardService{}, ac, bus.ProvideBus(tracing.InitializeTracerForTest()))
|
||||
@@ -915,7 +922,7 @@ func TestIntegrationOptimisticConcurrency(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
receiver := v0alpha1.Receiver{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
@@ -998,7 +1005,7 @@ func TestIntegrationPatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
receiver := v0alpha1.Receiver{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
@@ -1086,75 +1093,6 @@ func TestIntegrationPatch(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationRejectConfigApiReceiverModification(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
helper := getTestHelper(t)
|
||||
|
||||
cliCfg := helper.Org1.Admin.NewRestConfig()
|
||||
legacyCli := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
|
||||
alertmanagerRaw, err := testData.ReadFile(path.Join("test-data", "notification-settings.json"))
|
||||
require.NoError(t, err)
|
||||
var amConfig definitions.PostableUserConfig
|
||||
require.NoError(t, json.Unmarshal(alertmanagerRaw, &amConfig))
|
||||
|
||||
persistInitialConfig(t, amConfig, adminClient, legacyCli)
|
||||
|
||||
// We modify the receiver, this should cause the POST to be rejected.
|
||||
userDefinedReceiver := amConfig.AlertmanagerConfig.Receivers[slices.IndexFunc(amConfig.AlertmanagerConfig.Receivers, func(receiver *definitions.PostableApiReceiver) bool {
|
||||
return receiver.Name == "user-defined"
|
||||
})]
|
||||
userDefinedReceiver.GrafanaManagedReceivers[0].DisableResolveMessage = !userDefinedReceiver.GrafanaManagedReceivers[0].DisableResolveMessage
|
||||
success, err := legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Falsef(t, success, "Expected receiver modification to be rejected, but got %t", success)
|
||||
require.ErrorContainsf(t, err, "alertingApiServer", "Expected error message to contain 'alertingApiServer', but got %s", err)
|
||||
|
||||
// Revert the change.
|
||||
userDefinedReceiver.GrafanaManagedReceivers[0].DisableResolveMessage = !userDefinedReceiver.GrafanaManagedReceivers[0].DisableResolveMessage
|
||||
|
||||
// We add a receiver, this should be accepted.
|
||||
amConfig.AlertmanagerConfig.Receivers = append(amConfig.AlertmanagerConfig.Receivers, &definitions.PostableApiReceiver{
|
||||
Receiver: config.Receiver{
|
||||
Name: "new receiver",
|
||||
},
|
||||
PostableGrafanaReceivers: definitions.PostableGrafanaReceivers{
|
||||
GrafanaManagedReceivers: []*definitions.PostableGrafanaReceiver{
|
||||
{
|
||||
Name: "new receiver",
|
||||
Type: "email",
|
||||
Settings: []byte(`{"addresses": "<some@email.com>"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
success, err = legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Expected receiver modification to be accepted, but got %s", err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Sanity check.
|
||||
gettable, status, body := legacyCli.GetAlertmanagerConfigWithStatus(t)
|
||||
require.Equalf(t, http.StatusOK, status, body)
|
||||
require.Lenf(t, gettable.AlertmanagerConfig.Receivers, 3, "Expected 3 receivers, got %d", len(gettable.AlertmanagerConfig.Receivers))
|
||||
|
||||
// We remove the receiver, this should be accepted.
|
||||
amConfig.AlertmanagerConfig.Receivers = slices.DeleteFunc(amConfig.AlertmanagerConfig.Receivers, func(receiver *definitions.PostableApiReceiver) bool {
|
||||
return receiver.GrafanaManagedReceivers[0].Name == "new receiver"
|
||||
})
|
||||
success, err = legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Expected receiver modification to be accepted, but got %s", err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Sanity check.
|
||||
gettable, status, body = legacyCli.GetAlertmanagerConfigWithStatus(t)
|
||||
require.Equalf(t, http.StatusOK, status, body)
|
||||
require.Lenf(t, gettable.AlertmanagerConfig.Receivers, 2, "Expected 2 receivers, got %d", len(gettable.AlertmanagerConfig.Receivers))
|
||||
}
|
||||
|
||||
func TestIntegrationReferentialIntegrity(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
@@ -1171,14 +1109,14 @@ func TestIntegrationReferentialIntegrity(t *testing.T) {
|
||||
cliCfg := helper.Org1.Admin.NewRestConfig()
|
||||
legacyCli := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
// Prepare environment and create notification policy and rule that use time receiver
|
||||
alertmanagerRaw, err := testData.ReadFile(path.Join("test-data", "notification-settings.json"))
|
||||
require.NoError(t, err)
|
||||
var amConfig definitions.PostableUserConfig
|
||||
require.NoError(t, json.Unmarshal(alertmanagerRaw, &amConfig))
|
||||
|
||||
persistInitialConfig(t, amConfig, adminClient, legacyCli)
|
||||
persistInitialConfig(t, amConfig)
|
||||
|
||||
postGroupRaw, err := testData.ReadFile(path.Join("test-data", "rulegroup-1.json"))
|
||||
require.NoError(t, err)
|
||||
@@ -1283,7 +1221,7 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
var defaultReceiver *v0alpha1.Receiver
|
||||
t.Run("should list the default receiver", func(t *testing.T) {
|
||||
items, err := adminClient.List(ctx, v1.ListOptions{})
|
||||
@@ -1451,7 +1389,7 @@ func TestIntegrationReceiverListSelector(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
recv1 := &v0alpha1.Receiver{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
@@ -1533,13 +1471,14 @@ func TestIntegrationReceiverListSelector(t *testing.T) {
|
||||
|
||||
// persistInitialConfig helps create an initial config with new receivers using legacy json. Config API blocks receiver
|
||||
// modifications, so we need to use k8s API to create new receivers before posting the config.
|
||||
func persistInitialConfig(t *testing.T, amConfig definitions.PostableUserConfig, adminClient *apis.TypedClient[v0alpha1.Receiver, v0alpha1.ReceiverList], legacyCli alerting.LegacyApiClient) {
|
||||
func persistInitialConfig(t *testing.T, amConfig definitions.PostableUserConfig) {
|
||||
ctx := context.Background()
|
||||
|
||||
var defaultReceiver *definitions.PostableApiReceiver
|
||||
helper := getTestHelper(t)
|
||||
|
||||
receiverClient := test_common.NewReceiverClient(t, helper.Org1.Admin)
|
||||
for _, receiver := range amConfig.AlertmanagerConfig.Receivers {
|
||||
if receiver.Name == "grafana-default-email" {
|
||||
defaultReceiver = receiver
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1563,7 +1502,7 @@ func persistInitialConfig(t *testing.T, amConfig definitions.PostableUserConfig,
|
||||
})
|
||||
}
|
||||
|
||||
created, err := adminClient.Create(ctx, &toCreate, v1.CreateOptions{})
|
||||
created, err := receiverClient.Create(ctx, &toCreate, v1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, integration := range created.Spec.Integrations {
|
||||
@@ -1571,19 +1510,13 @@ func persistInitialConfig(t *testing.T, amConfig definitions.PostableUserConfig,
|
||||
}
|
||||
}
|
||||
|
||||
success, err := legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Failed to post Alertmanager configuration: %s", err)
|
||||
nsMapper := func(_ int64) string { return "default" }
|
||||
|
||||
gettable, status, body := legacyCli.GetAlertmanagerConfigWithStatus(t)
|
||||
require.Equalf(t, http.StatusOK, status, body)
|
||||
|
||||
idx := slices.IndexFunc(gettable.AlertmanagerConfig.Receivers, func(recv *definitions.GettableApiReceiver) bool {
|
||||
return recv.Name == "grafana-default-email"
|
||||
})
|
||||
gettableDefault := gettable.AlertmanagerConfig.Receivers[idx]
|
||||
|
||||
// Assign uid of default receiver as well.
|
||||
defaultReceiver.GrafanaManagedReceivers[0].UID = gettableDefault.GrafanaManagedReceivers[0].UID
|
||||
routeClient := test_common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
v1route, err := routingtree.ConvertToK8sResource(helper.Org1.AdminServiceAccount.OrgId, *amConfig.AlertmanagerConfig.Route, "", nsMapper)
|
||||
require.NoError(t, err)
|
||||
_, err = routeClient.Update(ctx, v1route, v1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func createIntegration(t *testing.T, integrationType string) v0alpha1.Integration {
|
||||
@@ -1609,19 +1542,3 @@ func createWildcardPermission(actions ...string) resourcepermissions.SetResource
|
||||
ResourceID: "*",
|
||||
}
|
||||
}
|
||||
|
||||
func newClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1.Receiver, v0alpha1.ReceiverList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1.Receiver, v0alpha1.ReceiverList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1.Kind().Group(),
|
||||
Version: v0alpha1.Kind().Version(),
|
||||
Resource: v0alpha1.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/alerting/definition"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
"github.com/prometheus/common/model"
|
||||
@@ -16,10 +15,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/routingtree/v0alpha1"
|
||||
v0alpha1_timeinterval "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1/fakes"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/tests/api/alerting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/apis/alerting/notifications/common"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -52,7 +54,7 @@ func TestIntegrationNotAllowedMethods(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
client := newClient(t, helper.Org1.Admin)
|
||||
client := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
|
||||
route := &v0alpha1.RoutingTree{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -156,11 +158,11 @@ func TestIntegrationAccessControl(t *testing.T) {
|
||||
}
|
||||
|
||||
admin := org1.Admin
|
||||
adminClient := newClient(t, admin)
|
||||
adminClient := common.NewRoutingTreeClient(t, admin)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("user '%s'", tc.user.Identity.GetLogin()), func(t *testing.T) {
|
||||
client := newClient(t, tc.user)
|
||||
client := common.NewRoutingTreeClient(t, tc.user)
|
||||
|
||||
if tc.canRead {
|
||||
t.Run("should be able to list routing trees", func(t *testing.T) {
|
||||
@@ -291,7 +293,7 @@ func TestIntegrationProvisioning(t *testing.T) {
|
||||
org := helper.Org1
|
||||
|
||||
admin := org.Admin
|
||||
adminClient := newClient(t, admin)
|
||||
adminClient := common.NewRoutingTreeClient(t, admin)
|
||||
|
||||
env := helper.GetEnv()
|
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles)
|
||||
@@ -342,7 +344,7 @@ func TestIntegrationOptimisticConcurrency(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
|
||||
current, err := adminClient.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
@@ -388,46 +390,30 @@ func TestIntegrationDataConsistency(t *testing.T) {
|
||||
cliCfg := helper.Org1.Admin.NewRestConfig()
|
||||
legacyCli := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
|
||||
|
||||
client := newClient(t, helper.Org1.Admin)
|
||||
client := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
|
||||
receiver := "grafana-default-email"
|
||||
timeInterval := "test-time-interval"
|
||||
createRoute := func(t *testing.T, route definitions.Route) {
|
||||
t.Helper()
|
||||
cfg, _, _ := legacyCli.GetAlertmanagerConfigWithStatus(t)
|
||||
var receivers []*definitions.PostableApiReceiver
|
||||
for _, apiReceiver := range cfg.AlertmanagerConfig.Receivers {
|
||||
var recv []*definitions.PostableGrafanaReceiver
|
||||
for _, r := range apiReceiver.GrafanaManagedReceivers {
|
||||
recv = append(recv, &definitions.PostableGrafanaReceiver{
|
||||
UID: r.UID,
|
||||
Name: r.Name,
|
||||
Type: r.Type,
|
||||
DisableResolveMessage: r.DisableResolveMessage,
|
||||
Settings: r.Settings,
|
||||
})
|
||||
}
|
||||
receivers = append(receivers, &definitions.PostableApiReceiver{
|
||||
Receiver: config.Receiver{Name: apiReceiver.Name},
|
||||
PostableGrafanaReceivers: definitions.PostableGrafanaReceivers{GrafanaManagedReceivers: recv},
|
||||
})
|
||||
}
|
||||
_, err := legacyCli.PostConfiguration(t, definitions.PostableUserConfig{
|
||||
AlertmanagerConfig: definitions.PostableApiAlertingConfig{
|
||||
Config: definition.Config{
|
||||
Route: &route,
|
||||
TimeIntervals: []config.TimeInterval{
|
||||
{
|
||||
Name: timeInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: receivers,
|
||||
},
|
||||
})
|
||||
routeClient := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
v1Route, err := routingtree.ConvertToK8sResource(helper.Org1.Admin.Identity.GetOrgID(), route, "", func(int64) string { return "default" })
|
||||
require.NoError(t, err)
|
||||
_, err = routeClient.Update(ctx, v1Route, v1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
_, err := common.NewTimeIntervalClient(t, helper.Org1.Admin).Create(ctx, &v0alpha1_timeinterval.TimeInterval{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v0alpha1_timeinterval.Spec{
|
||||
Name: timeInterval,
|
||||
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(1),
|
||||
},
|
||||
}, v1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
var regex config.Regexp
|
||||
require.NoError(t, json.Unmarshal([]byte(`".*"`), ®ex))
|
||||
|
||||
@@ -461,7 +447,7 @@ func TestIntegrationDataConsistency(t *testing.T) {
|
||||
createRoute(t, route)
|
||||
tree, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []v0alpha1.Matcher{
|
||||
expected := []v0alpha1.Matcher{
|
||||
{
|
||||
Label: "label_match",
|
||||
Type: v0alpha1.MatcherTypeEqual,
|
||||
@@ -482,7 +468,8 @@ func TestIntegrationDataConsistency(t *testing.T) {
|
||||
Type: v0alpha1.MatcherTypeNotEqualRegex,
|
||||
Value: "test-456",
|
||||
},
|
||||
}, tree.Spec.Routes[0].Matchers)
|
||||
}
|
||||
assert.ElementsMatch(t, expected, tree.Spec.Routes[0].Matchers)
|
||||
})
|
||||
t.Run("should save into ObjectMatchers", func(t *testing.T) {
|
||||
route := definitions.Route{
|
||||
@@ -623,20 +610,55 @@ func TestIntegrationDataConsistency(t *testing.T) {
|
||||
require.Equalf(t, http.StatusOK, status, body)
|
||||
require.Equal(t, before, after)
|
||||
})
|
||||
}
|
||||
|
||||
func newClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1.RoutingTree, v0alpha1.RoutingTreeList] {
|
||||
t.Helper()
|
||||
t.Run("unicode support in groupBy and matchers", func(t *testing.T) {
|
||||
route := definitions.Route{
|
||||
Receiver: receiver,
|
||||
Routes: []*definitions.Route{
|
||||
{
|
||||
GroupByStr: []string{"foo🙂"},
|
||||
Matchers: config.Matchers{{
|
||||
Type: labels.MatchEqual,
|
||||
Name: "foo🙂",
|
||||
Value: "bar",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "_bar1",
|
||||
Value: "baz🙂",
|
||||
}, {
|
||||
Type: labels.MatchRegexp,
|
||||
Name: "0baz",
|
||||
Value: "[a-zA-Z0-9]+,?",
|
||||
}, {
|
||||
Type: labels.MatchNotRegexp,
|
||||
Name: "corge",
|
||||
Value: "^[0-9]+((,[0-9]{3})*(,[0-9]{0,3})?)?$",
|
||||
}},
|
||||
ObjectMatchers: definitions.ObjectMatchers{{
|
||||
Type: labels.MatchEqual,
|
||||
Name: "Προμηθέας", // Prometheus in Greek
|
||||
Value: "Prom",
|
||||
}, {
|
||||
Type: labels.MatchNotEqual,
|
||||
Name: "犬", // Dog in Japanese (inu)
|
||||
Value: "Shiba Inu",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1.RoutingTree, v0alpha1.RoutingTreeList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1.Kind().Group(),
|
||||
Version: v0alpha1.Kind().Version(),
|
||||
Resource: v0alpha1.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
createRoute(t, route)
|
||||
tree, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "foo🙂", tree.Spec.Routes[0].GroupBy[0])
|
||||
expected := []v0alpha1.Matcher{
|
||||
{Label: "foo🙂", Type: v0alpha1.MatcherTypeEqual, Value: "bar"},
|
||||
{Label: "_bar1", Type: v0alpha1.MatcherTypeNotEqual, Value: "baz🙂"},
|
||||
{Label: "0baz", Type: v0alpha1.MatcherTypeEqualRegex, Value: "[a-zA-Z0-9]+,?"},
|
||||
{Label: "corge", Type: v0alpha1.MatcherTypeNotEqualRegex, Value: "^[0-9]+((,[0-9]{3})*(,[0-9]{0,3})?)?$"},
|
||||
{Label: "Προμηθέας", Type: v0alpha1.MatcherTypeEqual, Value: "Prom"},
|
||||
{Label: "犬", Type: v0alpha1.MatcherTypeNotEqual, Value: "Shiba Inu"},
|
||||
}
|
||||
assert.ElementsMatch(t, expected, tree.Spec.Routes[0].Matchers)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/templategroup/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -27,6 +25,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/apis/alerting/notifications/common"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -47,7 +46,7 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
client := newClient(t, helper.Org1.Admin)
|
||||
client := common.NewTemplateGroupClient(t, helper.Org1.Admin)
|
||||
|
||||
newTemplate := &v0alpha1.TemplateGroup{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -217,11 +216,11 @@ func TestIntegrationAccessControl(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
adminClient := newClient(t, org1.Admin)
|
||||
adminClient := common.NewTemplateGroupClient(t, org1.Admin)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("user '%s'", tc.user.Identity.GetLogin()), func(t *testing.T) {
|
||||
client := newClient(t, tc.user)
|
||||
client := common.NewTemplateGroupClient(t, tc.user)
|
||||
|
||||
var expected = &v0alpha1.TemplateGroup{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -377,7 +376,7 @@ func TestIntegrationProvisioning(t *testing.T) {
|
||||
org := helper.Org1
|
||||
|
||||
admin := org.Admin
|
||||
adminClient := newClient(t, admin)
|
||||
adminClient := common.NewTemplateGroupClient(t, admin)
|
||||
|
||||
env := helper.GetEnv()
|
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles)
|
||||
@@ -427,7 +426,7 @@ func TestIntegrationOptimisticConcurrency(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTemplateGroupClient(t, helper.Org1.Admin)
|
||||
|
||||
template := v0alpha1.TemplateGroup{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -511,7 +510,7 @@ func TestIntegrationPatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTemplateGroupClient(t, helper.Org1.Admin)
|
||||
|
||||
template := v0alpha1.TemplateGroup{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -571,7 +570,7 @@ func TestIntegrationListSelector(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTemplateGroupClient(t, helper.Org1.Admin)
|
||||
|
||||
template1 := &v0alpha1.TemplateGroup{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -664,19 +663,3 @@ func TestIntegrationListSelector(t *testing.T) {
|
||||
require.NotEqualf(t, templates.DefaultTemplateName, list.Items[1].Name, "Expected non-default template but got %s", list.Items[1].Name)
|
||||
})
|
||||
}
|
||||
|
||||
func newClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1.TemplateGroup, v0alpha1.TemplateGroupList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1.TemplateGroup, v0alpha1.TemplateGroupList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1.Kind().Group(),
|
||||
Version: v0alpha1.Kind().Version(),
|
||||
Resource: v0alpha1.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1/fakes"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/timeinterval"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/tests/api/alerting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/apis/alerting/notifications/common"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -57,7 +58,7 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
client := newClient(t, helper.Org1.Admin)
|
||||
client := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
newInterval := &v0alpha1.TimeInterval{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -191,11 +192,11 @@ func TestIntegrationTimeIntervalAccessControl(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("user '%s'", tc.user.Identity.GetLogin()), func(t *testing.T) {
|
||||
client := newClient(t, tc.user)
|
||||
client := common.NewTimeIntervalClient(t, tc.user)
|
||||
var expected = &v0alpha1.TimeInterval{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
@@ -349,7 +350,7 @@ func TestIntegrationTimeIntervalProvisioning(t *testing.T) {
|
||||
org := helper.Org1
|
||||
|
||||
admin := org.Admin
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
env := helper.GetEnv()
|
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles)
|
||||
@@ -401,7 +402,7 @@ func TestIntegrationTimeIntervalOptimisticConcurrency(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
interval := v0alpha1.TimeInterval{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -485,7 +486,7 @@ func TestIntegrationTimeIntervalPatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
interval := v0alpha1.TimeInterval{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -550,7 +551,7 @@ func TestIntegrationTimeIntervalListSelector(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
interval1 := &v0alpha1.TimeInterval{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -650,8 +651,26 @@ func TestIntegrationTimeIntervalReferentialIntegrity(t *testing.T) {
|
||||
var amConfig definitions.PostableUserConfig
|
||||
require.NoError(t, json.Unmarshal(alertmanagerRaw, &amConfig))
|
||||
|
||||
success, err := legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Failed to post Alertmanager configuration: %s", err)
|
||||
mtis := []definitions.MuteTimeInterval{}
|
||||
for _, interval := range amConfig.AlertmanagerConfig.MuteTimeIntervals {
|
||||
mtis = append(mtis, definitions.MuteTimeInterval{
|
||||
MuteTimeInterval: interval,
|
||||
})
|
||||
}
|
||||
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
v1intervals, err := timeinterval.ConvertToK8sResources(orgID, mtis, func(int64) string { return "default" }, nil)
|
||||
require.NoError(t, err)
|
||||
for _, interval := range v1intervals.Items {
|
||||
_, err := adminClient.Create(ctx, &interval, v1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
routeClient := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
||||
v1route, err := routingtree.ConvertToK8sResource(helper.Org1.Admin.Identity.GetOrgID(), *amConfig.AlertmanagerConfig.Route, "", func(int64) string { return "default" })
|
||||
require.NoError(t, err)
|
||||
_, err = routeClient.Update(ctx, v1route, v1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
postGroupRaw, err := testData.ReadFile(path.Join("test-data", "rulegroup-1.json"))
|
||||
require.NoError(t, err)
|
||||
@@ -667,8 +686,6 @@ func TestIntegrationTimeIntervalReferentialIntegrity(t *testing.T) {
|
||||
currentRuleGroup, status := legacyCli.GetRulesGroup(t, folderUID, ruleGroup.Name)
|
||||
require.Equal(t, http.StatusAccepted, status)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
|
||||
intervals, err := adminClient.List(ctx, v1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, intervals.Items, 2)
|
||||
@@ -769,7 +786,7 @@ func TestIntegrationTimeIntervalValidation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
adminClient := newClient(t, helper.Org1.Admin)
|
||||
adminClient := common.NewTimeIntervalClient(t, helper.Org1.Admin)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -809,19 +826,3 @@ func TestIntegrationTimeIntervalValidation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1.TimeInterval, v0alpha1.TimeIntervalList] {
|
||||
t.Helper()
|
||||
|
||||
client, err := dynamic.NewForConfig(user.NewRestConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &apis.TypedClient[v0alpha1.TimeInterval, v0alpha1.TimeIntervalList]{
|
||||
Client: client.Resource(
|
||||
schema.GroupVersionResource{
|
||||
Group: v0alpha1.Kind().Group(),
|
||||
Version: v0alpha1.Kind().Version(),
|
||||
Resource: v0alpha1.Kind().Plural(),
|
||||
}).Namespace("default"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user