mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 12:22:11 +08:00
666 lines
22 KiB
Go
666 lines
22 KiB
Go
package routingtree
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/alertmanager/config"
|
|
"github.com/prometheus/alertmanager/pkg/labels"
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1"
|
|
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/routingtree"
|
|
|
|
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/fakes"
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
|
"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"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
testsuite.Run(m)
|
|
}
|
|
|
|
func getTestHelper(t *testing.T) *apis.K8sTestHelper {
|
|
return apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{})
|
|
}
|
|
|
|
func TestIntegrationNotAllowedMethods(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
helper := getTestHelper(t)
|
|
client := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
|
|
|
route := &v0alpha1.RoutingTree{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
},
|
|
Spec: v0alpha1.RoutingTreeSpec{},
|
|
}
|
|
_, err := client.Create(ctx, route, v1.CreateOptions{})
|
|
assert.Error(t, err)
|
|
require.Truef(t, errors.IsMethodNotSupported(err), "Expected MethodNotSupported but got %s", err)
|
|
|
|
err = client.Client.DeleteCollection(ctx, v1.DeleteOptions{}, v1.ListOptions{})
|
|
assert.Error(t, err)
|
|
require.Truef(t, errors.IsMethodNotSupported(err), "Expected MethodNotSupported but got %s", err)
|
|
}
|
|
|
|
func TestIntegrationAccessControl(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
helper := getTestHelper(t)
|
|
|
|
org1 := helper.Org1
|
|
|
|
type testCase struct {
|
|
user apis.User
|
|
canRead bool
|
|
canUpdate bool
|
|
}
|
|
|
|
reader := helper.CreateUser("RoutesReader", apis.Org1, org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{
|
|
{
|
|
Actions: []string{
|
|
accesscontrol.ActionAlertingRoutesRead,
|
|
},
|
|
},
|
|
})
|
|
writer := helper.CreateUser("RoutesWriter", "Org1", org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{
|
|
{
|
|
Actions: []string{
|
|
accesscontrol.ActionAlertingRoutesRead,
|
|
accesscontrol.ActionAlertingRoutesWrite,
|
|
},
|
|
},
|
|
})
|
|
none := helper.CreateUser("RoutesNone", "Org1", org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{})
|
|
legacyReader := helper.CreateUser("LegacyRoutesReader", "Org1", org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{
|
|
{
|
|
Actions: []string{
|
|
accesscontrol.ActionAlertingNotificationsRead,
|
|
},
|
|
},
|
|
})
|
|
legacyWriter := helper.CreateUser("LegacyRoutesWriter", "Org1", org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{
|
|
{
|
|
Actions: []string{
|
|
accesscontrol.ActionAlertingNotificationsRead,
|
|
accesscontrol.ActionAlertingNotificationsWrite,
|
|
},
|
|
},
|
|
})
|
|
|
|
testCases := []testCase{
|
|
{
|
|
user: none,
|
|
},
|
|
{
|
|
user: org1.Admin,
|
|
canRead: true,
|
|
canUpdate: true,
|
|
},
|
|
{
|
|
user: org1.Editor,
|
|
canRead: true,
|
|
canUpdate: true,
|
|
},
|
|
{
|
|
user: org1.Viewer,
|
|
canRead: true,
|
|
},
|
|
{
|
|
user: reader,
|
|
canRead: true,
|
|
},
|
|
{
|
|
user: writer,
|
|
canRead: true,
|
|
canUpdate: true,
|
|
},
|
|
{
|
|
user: legacyReader,
|
|
canRead: true,
|
|
},
|
|
{
|
|
user: legacyWriter,
|
|
canRead: true,
|
|
canUpdate: true,
|
|
},
|
|
}
|
|
|
|
admin := org1.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 := common.NewRoutingTreeClient(t, tc.user)
|
|
|
|
if tc.canRead {
|
|
t.Run("should be able to list routing trees", func(t *testing.T) {
|
|
list, err := client.List(ctx, v1.ListOptions{})
|
|
require.NoError(t, err)
|
|
require.Len(t, list.Items, 1)
|
|
require.Equal(t, v0alpha1.UserDefinedRoutingTreeName, list.Items[0].Name)
|
|
})
|
|
|
|
t.Run("should be able to read routing trees by resource identifier", func(t *testing.T) {
|
|
_, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("should get NotFound if resource does not exist", func(t *testing.T) {
|
|
_, err := client.Get(ctx, "Notfound", v1.GetOptions{})
|
|
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err)
|
|
})
|
|
})
|
|
} else {
|
|
t.Run("should be forbidden to list routing trees", func(t *testing.T) {
|
|
_, err := client.List(ctx, v1.ListOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
|
|
t.Run("should be forbidden to read routing tree by name", func(t *testing.T) {
|
|
_, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
|
|
t.Run("should get forbidden even if name does not exist", func(t *testing.T) {
|
|
_, err := client.Get(ctx, "Notfound", v1.GetOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
})
|
|
}
|
|
|
|
current, err := adminClient.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
expected := current.Copy().(*v0alpha1.RoutingTree)
|
|
expected.Spec.Routes = []v0alpha1.RoutingTreeRoute{
|
|
{
|
|
Matchers: []v0alpha1.RoutingTreeMatcher{
|
|
{
|
|
Label: "test",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeEqual,
|
|
Value: "test",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
d, err := json.Marshal(expected)
|
|
require.NoError(t, err)
|
|
|
|
if tc.canUpdate {
|
|
t.Run("should be able to update routing tree", func(t *testing.T) {
|
|
updated, err := client.Update(ctx, expected, v1.UpdateOptions{})
|
|
require.NoErrorf(t, err, "Payload %s", string(d))
|
|
|
|
expected = updated
|
|
|
|
t.Run("should get NotFound if name does not exist", func(t *testing.T) {
|
|
up := expected.Copy().(*v0alpha1.RoutingTree)
|
|
up.Name = "notFound"
|
|
_, err := client.Update(ctx, up, v1.UpdateOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err)
|
|
})
|
|
})
|
|
} else {
|
|
t.Run("should be forbidden to update routing tree", func(t *testing.T) {
|
|
_, err := client.Update(ctx, expected, v1.UpdateOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
|
|
t.Run("should get forbidden even if resource does not exist", func(t *testing.T) {
|
|
up := expected.Copy().(*v0alpha1.RoutingTree)
|
|
up.Name = "notFound"
|
|
_, err := client.Update(ctx, up, v1.UpdateOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
})
|
|
}
|
|
|
|
if tc.canUpdate {
|
|
t.Run("should be able to reset routing tree", func(t *testing.T) {
|
|
err := client.Delete(ctx, expected.Name, v1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("should get NotFound if name does not exist", func(t *testing.T) {
|
|
err := client.Delete(ctx, "notfound", v1.DeleteOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err)
|
|
})
|
|
})
|
|
} else {
|
|
t.Run("should be forbidden to reset routing tree", func(t *testing.T) {
|
|
err := client.Delete(ctx, expected.Name, v1.DeleteOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
|
|
t.Run("should be forbidden even if resource does not exist", func(t *testing.T) {
|
|
err := client.Delete(ctx, "notfound", v1.DeleteOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
})
|
|
require.NoError(t, adminClient.Delete(ctx, expected.Name, v1.DeleteOptions{}))
|
|
}
|
|
})
|
|
|
|
err := adminClient.Delete(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
|
|
func TestIntegrationProvisioning(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
helper := getTestHelper(t)
|
|
|
|
org := helper.Org1
|
|
|
|
admin := org.Admin
|
|
adminClient := common.NewRoutingTreeClient(t, 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()))
|
|
require.NoError(t, err)
|
|
|
|
current, err := adminClient.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, "none", current.GetProvenanceStatus())
|
|
|
|
t.Run("should provide provenance status", func(t *testing.T) {
|
|
require.NoError(t, db.SetProvenance(ctx, &definitions.Route{}, admin.Identity.GetOrgID(), "API"))
|
|
|
|
got, err := adminClient.Get(ctx, current.Name, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, "API", got.GetProvenanceStatus())
|
|
})
|
|
t.Run("should not let update if provisioned", func(t *testing.T) {
|
|
updated := current.Copy().(*v0alpha1.RoutingTree)
|
|
updated.Spec.Routes = []v0alpha1.RoutingTreeRoute{
|
|
{
|
|
Matchers: []v0alpha1.RoutingTreeMatcher{
|
|
{
|
|
Label: "test",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeNotEqual,
|
|
Value: "123",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
|
|
t.Run("should not let delete if provisioned", func(t *testing.T) {
|
|
err := adminClient.Delete(ctx, current.Name, v1.DeleteOptions{})
|
|
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
}
|
|
|
|
func TestIntegrationOptimisticConcurrency(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
helper := getTestHelper(t)
|
|
|
|
adminClient := common.NewRoutingTreeClient(t, helper.Org1.Admin)
|
|
|
|
current, err := adminClient.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, current.ResourceVersion)
|
|
|
|
t.Run("should forbid if version does not match", func(t *testing.T) {
|
|
updated := current.Copy().(*v0alpha1.RoutingTree)
|
|
updated.ResourceVersion = "test"
|
|
_, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
|
|
require.Error(t, err)
|
|
require.Truef(t, errors.IsConflict(err), "should get Forbidden error but got %s", err)
|
|
})
|
|
t.Run("should update if version matches", func(t *testing.T) {
|
|
updated := current.Copy().(*v0alpha1.RoutingTree)
|
|
updated.Spec.Defaults.GroupBy = append(updated.Spec.Defaults.GroupBy, "data")
|
|
actualUpdated, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, updated.Spec, actualUpdated.Spec)
|
|
require.NotEqual(t, updated.ResourceVersion, actualUpdated.ResourceVersion)
|
|
})
|
|
t.Run("should update if version is empty", func(t *testing.T) {
|
|
current, err = adminClient.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
updated := current.Copy().(*v0alpha1.RoutingTree)
|
|
updated.ResourceVersion = ""
|
|
updated.Spec.Routes = append(updated.Spec.Routes, v0alpha1.RoutingTreeRoute{Continue: true})
|
|
|
|
actualUpdated, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, updated.Spec, actualUpdated.Spec)
|
|
require.NotEqual(t, current.ResourceVersion, actualUpdated.ResourceVersion)
|
|
})
|
|
}
|
|
|
|
func TestIntegrationDataConsistency(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
helper := getTestHelper(t)
|
|
|
|
cliCfg := helper.Org1.Admin.NewRestConfig()
|
|
legacyCli := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
|
|
|
|
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()
|
|
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{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
},
|
|
Spec: v0alpha1.TimeIntervalSpec{
|
|
Name: timeInterval,
|
|
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(1),
|
|
},
|
|
}, v1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
var regex config.Regexp
|
|
require.NoError(t, json.Unmarshal([]byte(`".*"`), ®ex))
|
|
|
|
ensureMatcher := func(t *testing.T, mt labels.MatchType, lbl, val string) *labels.Matcher {
|
|
m, err := labels.NewMatcher(mt, lbl, val)
|
|
require.NoError(t, err)
|
|
return m
|
|
}
|
|
|
|
t.Run("all matchers are handled", func(t *testing.T) {
|
|
t.Run("can read all legacy matchers", func(t *testing.T) {
|
|
route := definitions.Route{
|
|
Receiver: receiver,
|
|
Routes: []*definitions.Route{
|
|
{
|
|
Match: map[string]string{
|
|
"label_match": "test-123",
|
|
},
|
|
MatchRE: map[string]config.Regexp{
|
|
"label_re": regex,
|
|
},
|
|
Matchers: config.Matchers{
|
|
ensureMatcher(t, labels.MatchRegexp, "label_matchers", "test-321"),
|
|
},
|
|
ObjectMatchers: definitions.ObjectMatchers{
|
|
ensureMatcher(t, labels.MatchNotRegexp, "object-label-matchers", "test-456"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
createRoute(t, route)
|
|
tree, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
expected := []v0alpha1.RoutingTreeMatcher{
|
|
{
|
|
Label: "label_match",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeEqual,
|
|
Value: "test-123",
|
|
},
|
|
{
|
|
Label: "label_re",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeEqualRegex,
|
|
Value: ".*",
|
|
},
|
|
{
|
|
Label: "label_matchers",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeEqualRegex,
|
|
Value: "test-321",
|
|
},
|
|
{
|
|
Label: "object-label-matchers",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeNotEqualRegex,
|
|
Value: "test-456",
|
|
},
|
|
}
|
|
assert.ElementsMatch(t, expected, tree.Spec.Routes[0].Matchers)
|
|
})
|
|
t.Run("should save into ObjectMatchers", func(t *testing.T) {
|
|
route := definitions.Route{
|
|
Receiver: receiver,
|
|
Routes: []*definitions.Route{
|
|
{
|
|
Match: map[string]string{
|
|
"oldmatch": "123",
|
|
},
|
|
},
|
|
{
|
|
MatchRE: map[string]config.Regexp{
|
|
"oldmatchre": regex,
|
|
},
|
|
},
|
|
{
|
|
Matchers: config.Matchers{
|
|
ensureMatcher(t, labels.MatchNotEqual, "matchers", "v"),
|
|
},
|
|
},
|
|
{
|
|
ObjectMatchers: definitions.ObjectMatchers{
|
|
ensureMatcher(t, labels.MatchEqual, "t2", "v2"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
createRoute(t, route)
|
|
cfg, _, _ := legacyCli.GetAlertmanagerConfigWithStatus(t)
|
|
expectedRoutes := cfg.AlertmanagerConfig.Route.Routes // autogenerated route is the first one
|
|
expectedRoutes[1].Match = nil
|
|
expectedRoutes[1].ObjectMatchers = definitions.ObjectMatchers{
|
|
ensureMatcher(t, labels.MatchEqual, "oldmatch", "123"),
|
|
}
|
|
expectedRoutes[2].MatchRE = nil
|
|
expectedRoutes[2].ObjectMatchers = definitions.ObjectMatchers{
|
|
ensureMatcher(t, labels.MatchRegexp, "oldmatchre", ".*"),
|
|
}
|
|
expectedRoutes[3].Matchers = nil
|
|
expectedRoutes[3].ObjectMatchers = definitions.ObjectMatchers{
|
|
ensureMatcher(t, labels.MatchNotEqual, "matchers", "v"),
|
|
}
|
|
|
|
tree, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
_, err = client.Update(ctx, tree, v1.UpdateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
cfg, _, _ = legacyCli.GetAlertmanagerConfigWithStatus(t)
|
|
routes := cfg.AlertmanagerConfig.Route.Routes
|
|
require.EqualValues(t, expectedRoutes, routes)
|
|
})
|
|
})
|
|
|
|
route := definitions.Route{
|
|
Receiver: receiver,
|
|
GroupByStr: []string{"test-123", "test-456"},
|
|
GroupWait: util.Pointer(model.Duration(30 * time.Second)),
|
|
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
|
RepeatInterval: util.Pointer(model.Duration(24 * time.Hour)),
|
|
Routes: []*definitions.Route{
|
|
{
|
|
ObjectMatchers: definitions.ObjectMatchers{
|
|
ensureMatcher(t, labels.MatchNotEqual, "m", "1"),
|
|
ensureMatcher(t, labels.MatchEqual, "n", "1"),
|
|
ensureMatcher(t, labels.MatchRegexp, "o", "1"),
|
|
ensureMatcher(t, labels.MatchNotRegexp, "p", "1"),
|
|
},
|
|
Receiver: receiver,
|
|
GroupByStr: []string{"test-789"},
|
|
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
|
GroupInterval: util.Pointer(model.Duration(5 * time.Minute)),
|
|
RepeatInterval: util.Pointer(model.Duration(30 * time.Hour)),
|
|
MuteTimeIntervals: []string{timeInterval},
|
|
ActiveTimeIntervals: []string{timeInterval},
|
|
Continue: true,
|
|
},
|
|
},
|
|
}
|
|
createRoute(t, route)
|
|
|
|
t.Run("correctly reads all fields", func(t *testing.T) {
|
|
tree, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, v0alpha1.RoutingTreeRouteDefaults{
|
|
Receiver: receiver,
|
|
GroupBy: []string{"test-123", "test-456"},
|
|
GroupWait: util.Pointer("30s"),
|
|
GroupInterval: util.Pointer("1m"),
|
|
RepeatInterval: util.Pointer("1d"),
|
|
}, tree.Spec.Defaults)
|
|
assert.Len(t, tree.Spec.Routes, 1)
|
|
assert.Equal(t, v0alpha1.RoutingTreeRoute{
|
|
Continue: true,
|
|
Receiver: util.Pointer(receiver),
|
|
GroupBy: []string{"test-789"},
|
|
GroupWait: util.Pointer("2m"),
|
|
GroupInterval: util.Pointer("5m"),
|
|
RepeatInterval: util.Pointer("1d6h"),
|
|
MuteTimeIntervals: []string{timeInterval},
|
|
ActiveTimeIntervals: []string{timeInterval},
|
|
Matchers: []v0alpha1.RoutingTreeMatcher{
|
|
{
|
|
Label: "m",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeNotEqual,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Label: "n",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeEqual,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Label: "o",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeEqualRegex,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Label: "p",
|
|
Type: v0alpha1.RoutingTreeMatcherTypeNotEqualRegex,
|
|
Value: "1",
|
|
},
|
|
},
|
|
}, tree.Spec.Routes[0])
|
|
})
|
|
|
|
t.Run("correctly save all fields", func(t *testing.T) {
|
|
before, status, body := legacyCli.GetAlertmanagerConfigWithStatus(t)
|
|
require.Equalf(t, http.StatusOK, status, body)
|
|
tree, err := client.Get(ctx, v0alpha1.UserDefinedRoutingTreeName, v1.GetOptions{})
|
|
tree.Spec.Defaults.GroupBy = []string{"test-123", "test-456", "test-789"}
|
|
require.NoError(t, err)
|
|
_, err = client.Update(ctx, tree, v1.UpdateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
before.AlertmanagerConfig.Route.GroupByStr = []string{"test-123", "test-456", "test-789"}
|
|
before.AlertmanagerConfig.Route.GroupBy = []model.LabelName{"test-123", "test-456", "test-789"}
|
|
|
|
after, status, body := legacyCli.GetAlertmanagerConfigWithStatus(t)
|
|
require.Equalf(t, http.StatusOK, status, body)
|
|
require.Equal(t, before, after)
|
|
})
|
|
|
|
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",
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
|
|
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.RoutingTreeMatcher{
|
|
{Label: "foo🙂", Type: v0alpha1.RoutingTreeMatcherTypeEqual, Value: "bar"},
|
|
{Label: "_bar1", Type: v0alpha1.RoutingTreeMatcherTypeNotEqual, Value: "baz🙂"},
|
|
{Label: "0baz", Type: v0alpha1.RoutingTreeMatcherTypeEqualRegex, Value: "[a-zA-Z0-9]+,?"},
|
|
{Label: "corge", Type: v0alpha1.RoutingTreeMatcherTypeNotEqualRegex, Value: "^[0-9]+((,[0-9]{3})*(,[0-9]{0,3})?)?$"},
|
|
{Label: "Προμηθέας", Type: v0alpha1.RoutingTreeMatcherTypeEqual, Value: "Prom"},
|
|
{Label: "犬", Type: v0alpha1.RoutingTreeMatcherTypeNotEqual, Value: "Shiba Inu"},
|
|
}
|
|
assert.ElementsMatch(t, expected, tree.Spec.Routes[0].Matchers)
|
|
})
|
|
}
|