github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ruler/base/ruler_test.go (about) 1 package base 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "math/rand" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "reflect" 12 "sort" 13 "strings" 14 "sync" 15 "testing" 16 "time" 17 "unsafe" 18 19 "github.com/go-kit/log" 20 "github.com/go-kit/log/level" 21 "github.com/gorilla/mux" 22 "github.com/grafana/dskit/flagext" 23 "github.com/grafana/dskit/kv" 24 "github.com/grafana/dskit/kv/consul" 25 "github.com/grafana/dskit/ring" 26 "github.com/grafana/dskit/services" 27 "github.com/prometheus/client_golang/prometheus" 28 prom_testutil "github.com/prometheus/client_golang/prometheus/testutil" 29 "github.com/prometheus/common/model" 30 "github.com/prometheus/prometheus/model/labels" 31 "github.com/prometheus/prometheus/model/rulefmt" 32 "github.com/prometheus/prometheus/notifier" 33 "github.com/prometheus/prometheus/promql" 34 promRules "github.com/prometheus/prometheus/rules" 35 "github.com/prometheus/prometheus/storage" 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/require" 38 "github.com/weaveworks/common/user" 39 "go.uber.org/atomic" 40 "google.golang.org/grpc" 41 "gopkg.in/yaml.v2" 42 43 "github.com/grafana/dskit/tenant" 44 45 "github.com/grafana/loki/pkg/logproto" 46 "github.com/grafana/loki/pkg/querier/series" 47 "github.com/grafana/loki/pkg/ruler/rulespb" 48 "github.com/grafana/loki/pkg/ruler/rulestore" 49 "github.com/grafana/loki/pkg/ruler/rulestore/objectclient" 50 loki_storage "github.com/grafana/loki/pkg/storage" 51 "github.com/grafana/loki/pkg/storage/chunk/client/hedging" 52 "github.com/grafana/loki/pkg/storage/chunk/client/testutils" 53 "github.com/grafana/loki/pkg/util" 54 ) 55 56 func defaultRulerConfig(t testing.TB, store rulestore.RuleStore) Config { 57 t.Helper() 58 59 // Create a new temporary directory for the rules, so that 60 // each test will run in isolation. 61 rulesDir := t.TempDir() 62 63 codec := ring.GetCodec() 64 consul, closer := consul.NewInMemoryClient(codec, log.NewNopLogger(), nil) 65 t.Cleanup(func() { assert.NoError(t, closer.Close()) }) 66 67 cfg := Config{} 68 flagext.DefaultValues(&cfg) 69 cfg.RulePath = rulesDir 70 cfg.StoreConfig.mock = store 71 cfg.Ring.KVStore.Mock = consul 72 cfg.Ring.NumTokens = 1 73 cfg.Ring.ListenPort = 0 74 cfg.Ring.InstanceAddr = "localhost" 75 cfg.Ring.InstanceID = "localhost" 76 cfg.EnableQueryStats = false 77 78 return cfg 79 } 80 81 type ruleLimits struct { 82 evalDelay time.Duration 83 tenantShard int 84 maxRulesPerRuleGroup int 85 maxRuleGroups int 86 } 87 88 func (r ruleLimits) EvaluationDelay(_ string) time.Duration { 89 return r.evalDelay 90 } 91 92 func (r ruleLimits) RulerTenantShardSize(_ string) int { 93 return r.tenantShard 94 } 95 96 func (r ruleLimits) RulerMaxRuleGroupsPerTenant(_ string) int { 97 return r.maxRuleGroups 98 } 99 100 func (r ruleLimits) RulerMaxRulesPerRuleGroup(_ string) int { 101 return r.maxRulesPerRuleGroup 102 } 103 104 func testQueryableFunc(q storage.Querier) storage.QueryableFunc { 105 if q != nil { 106 return func(ctx context.Context, mint, maxt int64) (storage.Querier, error) { 107 return q, nil 108 } 109 } 110 111 return func(ctx context.Context, mint, maxt int64) (storage.Querier, error) { 112 return storage.NoopQuerier(), nil 113 } 114 } 115 116 func testSetup(t *testing.T, q storage.Querier) (*promql.Engine, storage.QueryableFunc, Pusher, log.Logger, RulesLimits, prometheus.Registerer) { 117 dir := t.TempDir() 118 119 tracker := promql.NewActiveQueryTracker(dir, 20, log.NewNopLogger()) 120 121 engine := promql.NewEngine(promql.EngineOpts{ 122 MaxSamples: 1e6, 123 ActiveQueryTracker: tracker, 124 Timeout: 2 * time.Minute, 125 }) 126 127 // Mock the pusher 128 pusher := newPusherMock() 129 pusher.MockPush(&logproto.WriteResponse{}, nil) 130 131 l := log.NewLogfmtLogger(os.Stdout) 132 l = level.NewFilter(l, level.AllowInfo()) 133 134 reg := prometheus.NewRegistry() 135 queryable := testQueryableFunc(q) 136 137 return engine, queryable, pusher, l, ruleLimits{evalDelay: 0, maxRuleGroups: 20, maxRulesPerRuleGroup: 15}, reg 138 } 139 140 func newManager(t *testing.T, cfg Config, q storage.Querier) *DefaultMultiTenantManager { 141 engine, queryable, pusher, logger, overrides, reg := testSetup(t, q) 142 manager, err := NewDefaultMultiTenantManager(cfg, DefaultTenantManagerFactory(cfg, pusher, queryable, engine, overrides, nil), reg, logger) 143 require.NoError(t, err) 144 145 return manager 146 } 147 148 type mockRulerClientsPool struct { 149 ClientsPool 150 cfg Config 151 rulerAddrMap map[string]*Ruler 152 numberOfCalls atomic.Int32 153 } 154 155 type mockRulerClient struct { 156 ruler *Ruler 157 numberOfCalls *atomic.Int32 158 } 159 160 func (c *mockRulerClient) Rules(ctx context.Context, in *RulesRequest, _ ...grpc.CallOption) (*RulesResponse, error) { 161 c.numberOfCalls.Inc() 162 return c.ruler.Rules(ctx, in) 163 } 164 165 func (p *mockRulerClientsPool) GetClientFor(addr string) (RulerClient, error) { 166 for _, r := range p.rulerAddrMap { 167 if r.lifecycler.GetInstanceAddr() == addr { 168 return &mockRulerClient{ 169 ruler: r, 170 numberOfCalls: &p.numberOfCalls, 171 }, nil 172 } 173 } 174 175 return nil, fmt.Errorf("unable to find ruler for add %s", addr) 176 } 177 178 func newMockClientsPool(cfg Config, logger log.Logger, reg prometheus.Registerer, rulerAddrMap map[string]*Ruler) *mockRulerClientsPool { 179 return &mockRulerClientsPool{ 180 ClientsPool: newRulerClientPool(cfg.ClientTLSConfig, logger, reg), 181 cfg: cfg, 182 rulerAddrMap: rulerAddrMap, 183 } 184 } 185 186 func buildRuler(t *testing.T, rulerConfig Config, q storage.Querier, clientMetrics loki_storage.ClientMetrics, rulerAddrMap map[string]*Ruler) *Ruler { 187 engine, queryable, pusher, logger, overrides, reg := testSetup(t, q) 188 storage, err := NewLegacyRuleStore(rulerConfig.StoreConfig, hedging.Config{}, clientMetrics, promRules.FileLoader{}, log.NewNopLogger()) 189 require.NoError(t, err) 190 191 managerFactory := DefaultTenantManagerFactory(rulerConfig, pusher, queryable, engine, overrides, reg) 192 manager, err := NewDefaultMultiTenantManager(rulerConfig, managerFactory, reg, log.NewNopLogger()) 193 require.NoError(t, err) 194 195 ruler, err := newRuler( 196 rulerConfig, 197 manager, 198 reg, 199 logger, 200 storage, 201 overrides, 202 newMockClientsPool(rulerConfig, logger, reg, rulerAddrMap), 203 ) 204 require.NoError(t, err) 205 return ruler 206 } 207 208 func newTestRuler(t *testing.T, rulerConfig Config) *Ruler { 209 m := loki_storage.NewClientMetrics() 210 defer m.Unregister() 211 ruler := buildRuler(t, rulerConfig, nil, m, nil) 212 require.NoError(t, services.StartAndAwaitRunning(context.Background(), ruler)) 213 214 // Ensure all rules are loaded before usage 215 ruler.syncRules(context.Background(), rulerSyncReasonInitial) 216 217 return ruler 218 } 219 220 var _ MultiTenantManager = &DefaultMultiTenantManager{} 221 222 func TestNotifierSendsUserIDHeader(t *testing.T) { 223 var wg sync.WaitGroup 224 225 // We do expect 1 API call for the user create with the getOrCreateNotifier() 226 wg.Add(1) 227 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 228 userID, _, err := tenant.ExtractTenantIDFromHTTPRequest(r) 229 assert.NoError(t, err) 230 assert.Equal(t, userID, "1") 231 wg.Done() 232 })) 233 defer ts.Close() 234 235 // We create an empty rule store so that the ruler will not load any rule from it. 236 cfg := defaultRulerConfig(t, newMockRuleStore(nil)) 237 238 cfg.AlertmanagerURL = ts.URL 239 cfg.AlertmanagerDiscovery = false 240 241 manager := newManager(t, cfg, nil) 242 defer manager.Stop() 243 244 n, err := manager.getOrCreateNotifier("1") 245 require.NoError(t, err) 246 247 // Loop until notifier discovery syncs up 248 for len(n.Alertmanagers()) == 0 { 249 time.Sleep(10 * time.Millisecond) 250 } 251 n.Send(¬ifier.Alert{ 252 Labels: labels.Labels{labels.Label{Name: "alertname", Value: "testalert"}}, 253 }) 254 255 wg.Wait() 256 257 // Ensure we have metrics in the notifier. 258 assert.NoError(t, prom_testutil.GatherAndCompare(manager.registry.(*prometheus.Registry), strings.NewReader(` 259 # HELP cortex_prometheus_notifications_dropped_total Total number of alerts dropped due to errors when sending to Alertmanager. 260 # TYPE cortex_prometheus_notifications_dropped_total counter 261 cortex_prometheus_notifications_dropped_total{user="1"} 0 262 `), "cortex_prometheus_notifications_dropped_total")) 263 } 264 265 func TestRuler_Rules(t *testing.T) { 266 cfg := defaultRulerConfig(t, newMockRuleStore(mockRules)) 267 268 r := newTestRuler(t, cfg) 269 defer services.StopAndAwaitTerminated(context.Background(), r) //nolint:errcheck 270 271 // test user1 272 ctx := user.InjectOrgID(context.Background(), "user1") 273 rls, err := r.Rules(ctx, &RulesRequest{}) 274 require.NoError(t, err) 275 require.Len(t, rls.Groups, 1) 276 rg := rls.Groups[0] 277 expectedRg := mockRules["user1"][0] 278 compareRuleGroupDescToStateDesc(t, expectedRg, rg) 279 280 // test user2 281 ctx = user.InjectOrgID(context.Background(), "user2") 282 rls, err = r.Rules(ctx, &RulesRequest{}) 283 require.NoError(t, err) 284 require.Len(t, rls.Groups, 1) 285 rg = rls.Groups[0] 286 expectedRg = mockRules["user2"][0] 287 compareRuleGroupDescToStateDesc(t, expectedRg, rg) 288 } 289 290 func compareRuleGroupDescToStateDesc(t *testing.T, expected *rulespb.RuleGroupDesc, got *GroupStateDesc) { 291 require.Equal(t, got.Group.Name, expected.Name) 292 require.Equal(t, got.Group.Namespace, expected.Namespace) 293 require.Len(t, expected.Rules, len(got.ActiveRules)) 294 for i := range got.ActiveRules { 295 require.Equal(t, expected.Rules[i].Record, got.ActiveRules[i].Rule.Record) 296 require.Equal(t, expected.Rules[i].Alert, got.ActiveRules[i].Rule.Alert) 297 } 298 } 299 300 func TestGetRules(t *testing.T) { 301 // ruler ID -> (user ID -> list of groups). 302 type expectedRulesMap map[string]map[string]rulespb.RuleGroupList 303 304 type testCase struct { 305 sharding bool 306 shardingStrategy string 307 shuffleShardSize int 308 } 309 310 expectedRules := expectedRulesMap{ 311 "ruler1": map[string]rulespb.RuleGroupList{ 312 "user1": { 313 &rulespb.RuleGroupDesc{User: "user1", Namespace: "namespace", Name: "first", Interval: 10 * time.Second}, 314 &rulespb.RuleGroupDesc{User: "user1", Namespace: "namespace", Name: "second", Interval: 10 * time.Second}, 315 }, 316 "user2": { 317 &rulespb.RuleGroupDesc{User: "user2", Namespace: "namespace", Name: "third", Interval: 10 * time.Second}, 318 }, 319 }, 320 "ruler2": map[string]rulespb.RuleGroupList{ 321 "user1": { 322 &rulespb.RuleGroupDesc{User: "user1", Namespace: "namespace", Name: "third", Interval: 10 * time.Second}, 323 }, 324 "user2": { 325 &rulespb.RuleGroupDesc{User: "user2", Namespace: "namespace", Name: "first", Interval: 10 * time.Second}, 326 &rulespb.RuleGroupDesc{User: "user2", Namespace: "namespace", Name: "second", Interval: 10 * time.Second}, 327 }, 328 }, 329 "ruler3": map[string]rulespb.RuleGroupList{ 330 "user3": { 331 &rulespb.RuleGroupDesc{User: "user3", Namespace: "namespace", Name: "third", Interval: 10 * time.Second}, 332 }, 333 "user2": { 334 &rulespb.RuleGroupDesc{User: "user2", Namespace: "namespace", Name: "forth", Interval: 10 * time.Second}, 335 &rulespb.RuleGroupDesc{User: "user2", Namespace: "namespace", Name: "fifty", Interval: 10 * time.Second}, 336 }, 337 }, 338 } 339 340 testCases := map[string]testCase{ 341 "No Sharding": { 342 sharding: false, 343 }, 344 "Default Sharding": { 345 sharding: true, 346 shardingStrategy: util.ShardingStrategyDefault, 347 }, 348 "Shuffle Sharding and ShardSize = 2": { 349 sharding: true, 350 shuffleShardSize: 2, 351 shardingStrategy: util.ShardingStrategyShuffle, 352 }, 353 } 354 355 for name, tc := range testCases { 356 t.Run(name, func(t *testing.T) { 357 kvStore, cleanUp := consul.NewInMemoryClient(ring.GetCodec(), log.NewNopLogger(), nil) 358 t.Cleanup(func() { assert.NoError(t, cleanUp.Close()) }) 359 allRulesByUser := map[string]rulespb.RuleGroupList{} 360 allRulesByRuler := map[string]rulespb.RuleGroupList{} 361 allTokensByRuler := map[string][]uint32{} 362 rulerAddrMap := map[string]*Ruler{} 363 364 createRuler := func(id string) *Ruler { 365 cfg := defaultRulerConfig(t, newMockRuleStore(allRulesByUser)) 366 367 cfg.ShardingStrategy = tc.shardingStrategy 368 cfg.EnableSharding = tc.sharding 369 370 cfg.Ring = RingConfig{ 371 InstanceID: id, 372 InstanceAddr: id, 373 KVStore: kv.Config{ 374 Mock: kvStore, 375 }, 376 } 377 m := loki_storage.NewClientMetrics() 378 defer m.Unregister() 379 r := buildRuler(t, cfg, nil, m, rulerAddrMap) 380 r.limits = ruleLimits{evalDelay: 0, tenantShard: tc.shuffleShardSize} 381 rulerAddrMap[id] = r 382 if r.ring != nil { 383 require.NoError(t, services.StartAndAwaitRunning(context.Background(), r.ring)) 384 t.Cleanup(r.ring.StopAsync) 385 } 386 return r 387 } 388 389 for rID, r := range expectedRules { 390 createRuler(rID) 391 for user, rules := range r { 392 allRulesByUser[user] = append(allRulesByUser[user], rules...) 393 allRulesByRuler[rID] = append(allRulesByRuler[rID], rules...) 394 allTokensByRuler[rID] = generateTokenForGroups(rules, 1) 395 } 396 } 397 398 if tc.sharding { 399 err := kvStore.CAS(context.Background(), ringKey, func(in interface{}) (out interface{}, retry bool, err error) { 400 d, _ := in.(*ring.Desc) 401 if d == nil { 402 d = ring.NewDesc() 403 } 404 for rID, tokens := range allTokensByRuler { 405 d.AddIngester(rID, rulerAddrMap[rID].lifecycler.GetInstanceAddr(), "", tokens, ring.ACTIVE, time.Now()) 406 } 407 return d, true, nil 408 }) 409 require.NoError(t, err) 410 // Wait a bit to make sure ruler's ring is updated. 411 time.Sleep(100 * time.Millisecond) 412 } 413 414 forEachRuler := func(f func(rID string, r *Ruler)) { 415 for rID, r := range rulerAddrMap { 416 f(rID, r) 417 } 418 } 419 420 // Sync Rules 421 forEachRuler(func(_ string, r *Ruler) { 422 r.syncRules(context.Background(), rulerSyncReasonInitial) 423 }) 424 425 for u := range allRulesByUser { 426 ctx := user.InjectOrgID(context.Background(), u) 427 forEachRuler(func(_ string, r *Ruler) { 428 rules, err := r.GetRules(ctx) 429 require.NoError(t, err) 430 require.Equal(t, len(allRulesByUser[u]), len(rules)) 431 if tc.sharding { 432 mockPoolClient := r.clientsPool.(*mockRulerClientsPool) 433 434 if tc.shardingStrategy == util.ShardingStrategyShuffle { 435 require.Equal(t, int32(tc.shuffleShardSize), mockPoolClient.numberOfCalls.Load()) 436 } else { 437 require.Equal(t, int32(len(rulerAddrMap)), mockPoolClient.numberOfCalls.Load()) 438 } 439 mockPoolClient.numberOfCalls.Store(0) 440 } 441 }) 442 } 443 444 totalLoadedRules := 0 445 totalConfiguredRules := 0 446 447 forEachRuler(func(rID string, r *Ruler) { 448 localRules, err := r.listRules(context.Background()) 449 require.NoError(t, err) 450 for _, rules := range localRules { 451 totalLoadedRules += len(rules) 452 } 453 totalConfiguredRules += len(allRulesByRuler[rID]) 454 }) 455 456 if tc.sharding { 457 require.Equal(t, totalConfiguredRules, totalLoadedRules) 458 } else { 459 // Not sharding means that all rules will be loaded on all rulers 460 numberOfRulers := len(rulerAddrMap) 461 require.Equal(t, totalConfiguredRules*numberOfRulers, totalLoadedRules) 462 } 463 }) 464 } 465 } 466 467 func TestSharding(t *testing.T) { 468 const ( 469 user1 = "user1" 470 user2 = "user2" 471 user3 = "user3" 472 ) 473 474 user1Group1 := &rulespb.RuleGroupDesc{User: user1, Namespace: "namespace", Name: "first"} 475 user1Group2 := &rulespb.RuleGroupDesc{User: user1, Namespace: "namespace", Name: "second"} 476 user2Group1 := &rulespb.RuleGroupDesc{User: user2, Namespace: "namespace", Name: "first"} 477 user3Group1 := &rulespb.RuleGroupDesc{User: user3, Namespace: "namespace", Name: "first"} 478 479 // Must be distinct for test to work. 480 user1Group1Token := tokenForGroup(user1Group1) 481 user1Group2Token := tokenForGroup(user1Group2) 482 user2Group1Token := tokenForGroup(user2Group1) 483 user3Group1Token := tokenForGroup(user3Group1) 484 485 noRules := map[string]rulespb.RuleGroupList{} 486 allRules := map[string]rulespb.RuleGroupList{ 487 user1: {user1Group1, user1Group2}, 488 user2: {user2Group1}, 489 user3: {user3Group1}, 490 } 491 492 // ruler ID -> (user ID -> list of groups). 493 type expectedRulesMap map[string]map[string]rulespb.RuleGroupList 494 495 type testCase struct { 496 sharding bool 497 shardingStrategy string 498 shuffleShardSize int 499 setupRing func(*ring.Desc) 500 enabledUsers []string 501 disabledUsers []string 502 503 expectedRules expectedRulesMap 504 } 505 506 const ( 507 ruler1 = "ruler-1" 508 ruler1Host = "1.1.1.1" 509 ruler1Port = 9999 510 ruler1Addr = "1.1.1.1:9999" 511 512 ruler2 = "ruler-2" 513 ruler2Host = "2.2.2.2" 514 ruler2Port = 9999 515 ruler2Addr = "2.2.2.2:9999" 516 517 ruler3 = "ruler-3" 518 ruler3Host = "3.3.3.3" 519 ruler3Port = 9999 520 ruler3Addr = "3.3.3.3:9999" 521 ) 522 523 testCases := map[string]testCase{ 524 "no sharding": { 525 sharding: false, 526 expectedRules: expectedRulesMap{ruler1: allRules}, 527 }, 528 529 "no sharding, single user allowed": { 530 sharding: false, 531 enabledUsers: []string{user1}, 532 expectedRules: expectedRulesMap{ruler1: map[string]rulespb.RuleGroupList{ 533 user1: {user1Group1, user1Group2}, 534 }}, 535 }, 536 537 "no sharding, single user disabled": { 538 sharding: false, 539 disabledUsers: []string{user1}, 540 expectedRules: expectedRulesMap{ruler1: map[string]rulespb.RuleGroupList{ 541 user2: {user2Group1}, 542 user3: {user3Group1}, 543 }}, 544 }, 545 546 "default sharding, single ruler": { 547 sharding: true, 548 shardingStrategy: util.ShardingStrategyDefault, 549 setupRing: func(desc *ring.Desc) { 550 desc.AddIngester(ruler1, ruler1Addr, "", []uint32{0}, ring.ACTIVE, time.Now()) 551 }, 552 expectedRules: expectedRulesMap{ruler1: allRules}, 553 }, 554 555 "default sharding, single ruler, single enabled user": { 556 sharding: true, 557 shardingStrategy: util.ShardingStrategyDefault, 558 enabledUsers: []string{user1}, 559 setupRing: func(desc *ring.Desc) { 560 desc.AddIngester(ruler1, ruler1Addr, "", []uint32{0}, ring.ACTIVE, time.Now()) 561 }, 562 expectedRules: expectedRulesMap{ruler1: map[string]rulespb.RuleGroupList{ 563 user1: {user1Group1, user1Group2}, 564 }}, 565 }, 566 567 "default sharding, single ruler, single disabled user": { 568 sharding: true, 569 shardingStrategy: util.ShardingStrategyDefault, 570 disabledUsers: []string{user1}, 571 setupRing: func(desc *ring.Desc) { 572 desc.AddIngester(ruler1, ruler1Addr, "", []uint32{0}, ring.ACTIVE, time.Now()) 573 }, 574 expectedRules: expectedRulesMap{ruler1: map[string]rulespb.RuleGroupList{ 575 user2: {user2Group1}, 576 user3: {user3Group1}, 577 }}, 578 }, 579 580 "default sharding, multiple ACTIVE rulers": { 581 sharding: true, 582 shardingStrategy: util.ShardingStrategyDefault, 583 setupRing: func(desc *ring.Desc) { 584 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{user1Group1Token + 1, user2Group1Token + 1}), ring.ACTIVE, time.Now()) 585 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group2Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 586 }, 587 588 expectedRules: expectedRulesMap{ 589 ruler1: map[string]rulespb.RuleGroupList{ 590 user1: {user1Group1}, 591 user2: {user2Group1}, 592 }, 593 594 ruler2: map[string]rulespb.RuleGroupList{ 595 user1: {user1Group2}, 596 user3: {user3Group1}, 597 }, 598 }, 599 }, 600 601 "default sharding, multiple ACTIVE rulers, single enabled user": { 602 sharding: true, 603 shardingStrategy: util.ShardingStrategyDefault, 604 enabledUsers: []string{user1}, 605 setupRing: func(desc *ring.Desc) { 606 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{user1Group1Token + 1, user2Group1Token + 1}), ring.ACTIVE, time.Now()) 607 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group2Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 608 }, 609 610 expectedRules: expectedRulesMap{ 611 ruler1: map[string]rulespb.RuleGroupList{ 612 user1: {user1Group1}, 613 }, 614 615 ruler2: map[string]rulespb.RuleGroupList{ 616 user1: {user1Group2}, 617 }, 618 }, 619 }, 620 621 "default sharding, multiple ACTIVE rulers, single disabled user": { 622 sharding: true, 623 shardingStrategy: util.ShardingStrategyDefault, 624 disabledUsers: []string{user1}, 625 setupRing: func(desc *ring.Desc) { 626 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{user1Group1Token + 1, user2Group1Token + 1}), ring.ACTIVE, time.Now()) 627 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group2Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 628 }, 629 630 expectedRules: expectedRulesMap{ 631 ruler1: map[string]rulespb.RuleGroupList{ 632 user2: {user2Group1}, 633 }, 634 635 ruler2: map[string]rulespb.RuleGroupList{ 636 user3: {user3Group1}, 637 }, 638 }, 639 }, 640 641 "default sharding, unhealthy ACTIVE ruler": { 642 sharding: true, 643 shardingStrategy: util.ShardingStrategyDefault, 644 645 setupRing: func(desc *ring.Desc) { 646 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{user1Group1Token + 1, user2Group1Token + 1}), ring.ACTIVE, time.Now()) 647 desc.Ingesters[ruler2] = ring.InstanceDesc{ 648 Addr: ruler2Addr, 649 Timestamp: time.Now().Add(-time.Hour).Unix(), 650 State: ring.ACTIVE, 651 Tokens: sortTokens([]uint32{user1Group2Token + 1, user3Group1Token + 1}), 652 } 653 }, 654 655 expectedRules: expectedRulesMap{ 656 // This ruler doesn't get rules from unhealthy ruler (RF=1). 657 ruler1: map[string]rulespb.RuleGroupList{ 658 user1: {user1Group1}, 659 user2: {user2Group1}, 660 }, 661 ruler2: noRules, 662 }, 663 }, 664 665 "default sharding, LEAVING ruler": { 666 sharding: true, 667 shardingStrategy: util.ShardingStrategyDefault, 668 669 setupRing: func(desc *ring.Desc) { 670 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{user1Group1Token + 1, user2Group1Token + 1}), ring.LEAVING, time.Now()) 671 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group2Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 672 }, 673 674 expectedRules: expectedRulesMap{ 675 // LEAVING ruler doesn't get any rules. 676 ruler1: noRules, 677 ruler2: allRules, 678 }, 679 }, 680 681 "default sharding, JOINING ruler": { 682 sharding: true, 683 shardingStrategy: util.ShardingStrategyDefault, 684 685 setupRing: func(desc *ring.Desc) { 686 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{user1Group1Token + 1, user2Group1Token + 1}), ring.JOINING, time.Now()) 687 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group2Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 688 }, 689 690 expectedRules: expectedRulesMap{ 691 // JOINING ruler has no rules yet. 692 ruler1: noRules, 693 ruler2: allRules, 694 }, 695 }, 696 697 "shuffle sharding, single ruler": { 698 sharding: true, 699 shardingStrategy: util.ShardingStrategyShuffle, 700 701 setupRing: func(desc *ring.Desc) { 702 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{0}), ring.ACTIVE, time.Now()) 703 }, 704 705 expectedRules: expectedRulesMap{ 706 ruler1: allRules, 707 }, 708 }, 709 710 "shuffle sharding, multiple rulers, shard size 1": { 711 sharding: true, 712 shardingStrategy: util.ShardingStrategyShuffle, 713 shuffleShardSize: 1, 714 715 setupRing: func(desc *ring.Desc) { 716 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1, userToken(user2, 0) + 1, userToken(user3, 0) + 1}), ring.ACTIVE, time.Now()) 717 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group1Token + 1, user1Group2Token + 1, user2Group1Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 718 }, 719 720 expectedRules: expectedRulesMap{ 721 ruler1: allRules, 722 ruler2: noRules, 723 }, 724 }, 725 726 // Same test as previous one, but with shard size=2. Second ruler gets all the rules. 727 "shuffle sharding, two rulers, shard size 2": { 728 sharding: true, 729 shardingStrategy: util.ShardingStrategyShuffle, 730 shuffleShardSize: 2, 731 732 setupRing: func(desc *ring.Desc) { 733 // Exact same tokens setup as previous test. 734 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1, userToken(user2, 0) + 1, userToken(user3, 0) + 1}), ring.ACTIVE, time.Now()) 735 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{user1Group1Token + 1, user1Group2Token + 1, user2Group1Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 736 }, 737 738 expectedRules: expectedRulesMap{ 739 ruler1: noRules, 740 ruler2: allRules, 741 }, 742 }, 743 744 "shuffle sharding, two rulers, shard size 1, distributed users": { 745 sharding: true, 746 shardingStrategy: util.ShardingStrategyShuffle, 747 shuffleShardSize: 1, 748 749 setupRing: func(desc *ring.Desc) { 750 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1}), ring.ACTIVE, time.Now()) 751 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{userToken(user2, 0) + 1, userToken(user3, 0) + 1}), ring.ACTIVE, time.Now()) 752 }, 753 754 expectedRules: expectedRulesMap{ 755 ruler1: map[string]rulespb.RuleGroupList{ 756 user1: {user1Group1, user1Group2}, 757 }, 758 ruler2: map[string]rulespb.RuleGroupList{ 759 user2: {user2Group1}, 760 user3: {user3Group1}, 761 }, 762 }, 763 }, 764 "shuffle sharding, three rulers, shard size 2": { 765 sharding: true, 766 shardingStrategy: util.ShardingStrategyShuffle, 767 shuffleShardSize: 2, 768 769 setupRing: func(desc *ring.Desc) { 770 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1, user1Group1Token + 1}), ring.ACTIVE, time.Now()) 771 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{userToken(user1, 1) + 1, user1Group2Token + 1, userToken(user2, 1) + 1, userToken(user3, 1) + 1}), ring.ACTIVE, time.Now()) 772 desc.AddIngester(ruler3, ruler3Addr, "", sortTokens([]uint32{userToken(user2, 0) + 1, userToken(user3, 0) + 1, user2Group1Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 773 }, 774 775 expectedRules: expectedRulesMap{ 776 ruler1: map[string]rulespb.RuleGroupList{ 777 user1: {user1Group1}, 778 }, 779 ruler2: map[string]rulespb.RuleGroupList{ 780 user1: {user1Group2}, 781 }, 782 ruler3: map[string]rulespb.RuleGroupList{ 783 user2: {user2Group1}, 784 user3: {user3Group1}, 785 }, 786 }, 787 }, 788 "shuffle sharding, three rulers, shard size 2, ruler2 has no users": { 789 sharding: true, 790 shardingStrategy: util.ShardingStrategyShuffle, 791 shuffleShardSize: 2, 792 793 setupRing: func(desc *ring.Desc) { 794 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1, userToken(user2, 1) + 1, user1Group1Token + 1, user1Group2Token + 1}), ring.ACTIVE, time.Now()) 795 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{userToken(user1, 1) + 1, userToken(user3, 1) + 1, user2Group1Token + 1}), ring.ACTIVE, time.Now()) 796 desc.AddIngester(ruler3, ruler3Addr, "", sortTokens([]uint32{userToken(user2, 0) + 1, userToken(user3, 0) + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 797 }, 798 799 expectedRules: expectedRulesMap{ 800 ruler1: map[string]rulespb.RuleGroupList{ 801 user1: {user1Group1, user1Group2}, 802 }, 803 ruler2: noRules, // Ruler2 owns token for user2group1, but user-2 will only be handled by ruler-1 and 3. 804 ruler3: map[string]rulespb.RuleGroupList{ 805 user2: {user2Group1}, 806 user3: {user3Group1}, 807 }, 808 }, 809 }, 810 811 "shuffle sharding, three rulers, shard size 2, single enabled user": { 812 sharding: true, 813 shardingStrategy: util.ShardingStrategyShuffle, 814 shuffleShardSize: 2, 815 enabledUsers: []string{user1}, 816 817 setupRing: func(desc *ring.Desc) { 818 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1, user1Group1Token + 1}), ring.ACTIVE, time.Now()) 819 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{userToken(user1, 1) + 1, user1Group2Token + 1, userToken(user2, 1) + 1, userToken(user3, 1) + 1}), ring.ACTIVE, time.Now()) 820 desc.AddIngester(ruler3, ruler3Addr, "", sortTokens([]uint32{userToken(user2, 0) + 1, userToken(user3, 0) + 1, user2Group1Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 821 }, 822 823 expectedRules: expectedRulesMap{ 824 ruler1: map[string]rulespb.RuleGroupList{ 825 user1: {user1Group1}, 826 }, 827 ruler2: map[string]rulespb.RuleGroupList{ 828 user1: {user1Group2}, 829 }, 830 ruler3: map[string]rulespb.RuleGroupList{}, 831 }, 832 }, 833 834 "shuffle sharding, three rulers, shard size 2, single disabled user": { 835 sharding: true, 836 shardingStrategy: util.ShardingStrategyShuffle, 837 shuffleShardSize: 2, 838 disabledUsers: []string{user1}, 839 840 setupRing: func(desc *ring.Desc) { 841 desc.AddIngester(ruler1, ruler1Addr, "", sortTokens([]uint32{userToken(user1, 0) + 1, user1Group1Token + 1}), ring.ACTIVE, time.Now()) 842 desc.AddIngester(ruler2, ruler2Addr, "", sortTokens([]uint32{userToken(user1, 1) + 1, user1Group2Token + 1, userToken(user2, 1) + 1, userToken(user3, 1) + 1}), ring.ACTIVE, time.Now()) 843 desc.AddIngester(ruler3, ruler3Addr, "", sortTokens([]uint32{userToken(user2, 0) + 1, userToken(user3, 0) + 1, user2Group1Token + 1, user3Group1Token + 1}), ring.ACTIVE, time.Now()) 844 }, 845 846 expectedRules: expectedRulesMap{ 847 ruler1: map[string]rulespb.RuleGroupList{}, 848 ruler2: map[string]rulespb.RuleGroupList{}, 849 ruler3: map[string]rulespb.RuleGroupList{ 850 user2: {user2Group1}, 851 user3: {user3Group1}, 852 }, 853 }, 854 }, 855 } 856 857 for name, tc := range testCases { 858 t.Run(name, func(t *testing.T) { 859 kvStore, closer := consul.NewInMemoryClient(ring.GetCodec(), log.NewNopLogger(), nil) 860 t.Cleanup(func() { assert.NoError(t, closer.Close()) }) 861 862 setupRuler := func(id string, host string, port int, forceRing *ring.Ring) *Ruler { 863 cfg := Config{ 864 StoreConfig: RuleStoreConfig{mock: newMockRuleStore(allRules)}, 865 EnableSharding: tc.sharding, 866 ShardingStrategy: tc.shardingStrategy, 867 Ring: RingConfig{ 868 InstanceID: id, 869 InstanceAddr: host, 870 InstancePort: port, 871 KVStore: kv.Config{ 872 Mock: kvStore, 873 }, 874 HeartbeatTimeout: 1 * time.Minute, 875 }, 876 FlushCheckPeriod: 0, 877 EnabledTenants: tc.enabledUsers, 878 DisabledTenants: tc.disabledUsers, 879 } 880 881 m := loki_storage.NewClientMetrics() 882 defer m.Unregister() 883 r := buildRuler(t, cfg, nil, m, nil) 884 r.limits = ruleLimits{evalDelay: 0, tenantShard: tc.shuffleShardSize} 885 886 if forceRing != nil { 887 r.ring = forceRing 888 } 889 return r 890 } 891 892 r1 := setupRuler(ruler1, ruler1Host, ruler1Port, nil) 893 894 rulerRing := r1.ring 895 896 // We start ruler's ring, but nothing else (not even lifecycler). 897 if rulerRing != nil { 898 require.NoError(t, services.StartAndAwaitRunning(context.Background(), rulerRing)) 899 t.Cleanup(rulerRing.StopAsync) 900 } 901 902 var r2, r3 *Ruler 903 if rulerRing != nil { 904 // Reuse ring from r1. 905 r2 = setupRuler(ruler2, ruler2Host, ruler2Port, rulerRing) 906 r3 = setupRuler(ruler3, ruler3Host, ruler3Port, rulerRing) 907 } 908 909 if tc.setupRing != nil { 910 err := kvStore.CAS(context.Background(), ringKey, func(in interface{}) (out interface{}, retry bool, err error) { 911 d, _ := in.(*ring.Desc) 912 if d == nil { 913 d = ring.NewDesc() 914 } 915 916 tc.setupRing(d) 917 918 return d, true, nil 919 }) 920 require.NoError(t, err) 921 // Wait a bit to make sure ruler's ring is updated. 922 time.Sleep(100 * time.Millisecond) 923 } 924 925 // Always add ruler1 to expected rulers, even if there is no ring (no sharding). 926 loadedRules1, err := r1.listRules(context.Background()) 927 require.NoError(t, err) 928 929 expected := expectedRulesMap{ 930 ruler1: loadedRules1, 931 } 932 933 addToExpected := func(id string, r *Ruler) { 934 // Only expect rules from other rulers when using ring, and they are present in the ring. 935 if r != nil && rulerRing != nil && rulerRing.HasInstance(id) { 936 loaded, err := r.listRules(context.Background()) 937 require.NoError(t, err) 938 // Normalize nil map to empty one. 939 if loaded == nil { 940 loaded = map[string]rulespb.RuleGroupList{} 941 } 942 expected[id] = loaded 943 } 944 } 945 946 addToExpected(ruler2, r2) 947 addToExpected(ruler3, r3) 948 949 require.Equal(t, tc.expectedRules, expected) 950 }) 951 } 952 } 953 954 // User shuffle shard token. 955 func userToken(user string, skip int) uint32 { 956 r := rand.New(rand.NewSource(util.ShuffleShardSeed(user, ""))) 957 958 for ; skip > 0; skip-- { 959 _ = r.Uint32() 960 } 961 return r.Uint32() 962 } 963 964 func sortTokens(tokens []uint32) []uint32 { 965 sort.Slice(tokens, func(i, j int) bool { 966 return tokens[i] < tokens[j] 967 }) 968 return tokens 969 } 970 971 func TestDeleteTenantRuleGroups(t *testing.T) { 972 ruleGroups := []ruleGroupKey{ 973 {user: "userA", namespace: "namespace", group: "group"}, 974 {user: "userB", namespace: "namespace1", group: "group"}, 975 {user: "userB", namespace: "namespace2", group: "group"}, 976 } 977 978 obj, rs := setupRuleGroupsStore(t, ruleGroups) 979 require.Equal(t, 3, obj.GetObjectCount()) 980 981 api, err := NewRuler(Config{}, nil, nil, log.NewNopLogger(), rs, nil) 982 require.NoError(t, err) 983 984 { 985 req := &http.Request{} 986 resp := httptest.NewRecorder() 987 api.DeleteTenantConfiguration(resp, req) 988 989 require.Equal(t, http.StatusUnauthorized, resp.Code) 990 } 991 992 { 993 callDeleteTenantAPI(t, api, "user-with-no-rule-groups") 994 require.Equal(t, 3, obj.GetObjectCount()) 995 996 verifyExpectedDeletedRuleGroupsForUser(t, api, "user-with-no-rule-groups", true) // Has no rule groups 997 verifyExpectedDeletedRuleGroupsForUser(t, api, "userA", false) 998 verifyExpectedDeletedRuleGroupsForUser(t, api, "userB", false) 999 } 1000 1001 { 1002 callDeleteTenantAPI(t, api, "userA") 1003 require.Equal(t, 2, obj.GetObjectCount()) 1004 1005 verifyExpectedDeletedRuleGroupsForUser(t, api, "user-with-no-rule-groups", true) // Has no rule groups 1006 verifyExpectedDeletedRuleGroupsForUser(t, api, "userA", true) // Just deleted. 1007 verifyExpectedDeletedRuleGroupsForUser(t, api, "userB", false) 1008 } 1009 1010 // Deleting same user again works fine and reports no problems. 1011 { 1012 callDeleteTenantAPI(t, api, "userA") 1013 require.Equal(t, 2, obj.GetObjectCount()) 1014 1015 verifyExpectedDeletedRuleGroupsForUser(t, api, "user-with-no-rule-groups", true) // Has no rule groups 1016 verifyExpectedDeletedRuleGroupsForUser(t, api, "userA", true) // Already deleted before. 1017 verifyExpectedDeletedRuleGroupsForUser(t, api, "userB", false) 1018 } 1019 1020 { 1021 callDeleteTenantAPI(t, api, "userB") 1022 require.Equal(t, 0, obj.GetObjectCount()) 1023 1024 verifyExpectedDeletedRuleGroupsForUser(t, api, "user-with-no-rule-groups", true) // Has no rule groups 1025 verifyExpectedDeletedRuleGroupsForUser(t, api, "userA", true) // Deleted previously 1026 verifyExpectedDeletedRuleGroupsForUser(t, api, "userB", true) // Just deleted 1027 } 1028 } 1029 1030 func generateTokenForGroups(groups []*rulespb.RuleGroupDesc, offset uint32) []uint32 { 1031 var tokens []uint32 1032 1033 for _, g := range groups { 1034 tokens = append(tokens, tokenForGroup(g)+offset) 1035 } 1036 1037 return tokens 1038 } 1039 1040 func callDeleteTenantAPI(t *testing.T, api *Ruler, userID string) { 1041 ctx := user.InjectOrgID(context.Background(), userID) 1042 1043 req := &http.Request{} 1044 resp := httptest.NewRecorder() 1045 api.DeleteTenantConfiguration(resp, req.WithContext(ctx)) 1046 1047 require.Equal(t, http.StatusOK, resp.Code) 1048 } 1049 1050 func verifyExpectedDeletedRuleGroupsForUser(t *testing.T, r *Ruler, userID string, expectedDeleted bool) { 1051 list, err := r.store.ListRuleGroupsForUserAndNamespace(context.Background(), userID, "") 1052 require.NoError(t, err) 1053 1054 if expectedDeleted { 1055 require.Equal(t, 0, len(list)) 1056 } else { 1057 require.NotEqual(t, 0, len(list)) 1058 } 1059 } 1060 1061 func setupRuleGroupsStore(t *testing.T, ruleGroups []ruleGroupKey) (*testutils.MockStorage, rulestore.RuleStore) { 1062 obj := testutils.NewMockStorage() 1063 rs := objectclient.NewRuleStore(obj, 5, log.NewNopLogger()) 1064 testutils.ResetMockStorage() 1065 // "upload" rule groups 1066 for _, key := range ruleGroups { 1067 desc := rulespb.ToProto(key.user, key.namespace, rulefmt.RuleGroup{Name: key.group}) 1068 require.NoError(t, rs.SetRuleGroup(context.Background(), key.user, key.namespace, desc)) 1069 } 1070 1071 return obj, rs 1072 } 1073 1074 type ruleGroupKey struct { 1075 user, namespace, group string 1076 } 1077 1078 func TestRuler_ListAllRules(t *testing.T) { 1079 cfg := defaultRulerConfig(t, newMockRuleStore(mockRules)) 1080 1081 r := newTestRuler(t, cfg) 1082 defer services.StopAndAwaitTerminated(context.Background(), r) //nolint:errcheck 1083 1084 router := mux.NewRouter() 1085 router.Path("/ruler/rule_groups").Methods(http.MethodGet).HandlerFunc(r.ListAllRules) 1086 1087 req := requestFor(t, http.MethodGet, "https://localhost:8080/ruler/rule_groups", nil, "") 1088 w := httptest.NewRecorder() 1089 router.ServeHTTP(w, req) 1090 1091 resp := w.Result() 1092 body, _ := ioutil.ReadAll(resp.Body) 1093 1094 // Check status code and header 1095 require.Equal(t, http.StatusOK, resp.StatusCode) 1096 require.Equal(t, "application/yaml", resp.Header.Get("Content-Type")) 1097 1098 gs := make(map[string]map[string][]rulefmt.RuleGroup) // user:namespace:[]rulefmt.RuleGroup 1099 for userID := range mockRules { 1100 gs[userID] = mockRules[userID].Formatted() 1101 } 1102 expectedResponse, err := yaml.Marshal(gs) 1103 require.NoError(t, err) 1104 require.YAMLEq(t, string(expectedResponse), string(body)) 1105 } 1106 1107 type senderFunc func(alerts ...*notifier.Alert) 1108 1109 func (s senderFunc) Send(alerts ...*notifier.Alert) { 1110 s(alerts...) 1111 } 1112 1113 func TestSendAlerts(t *testing.T) { 1114 testCases := []struct { 1115 in []*promRules.Alert 1116 exp []*notifier.Alert 1117 }{ 1118 { 1119 in: []*promRules.Alert{ 1120 { 1121 Labels: []labels.Label{{Name: "l1", Value: "v1"}}, 1122 Annotations: []labels.Label{{Name: "a2", Value: "v2"}}, 1123 ActiveAt: time.Unix(1, 0), 1124 FiredAt: time.Unix(2, 0), 1125 ValidUntil: time.Unix(3, 0), 1126 }, 1127 }, 1128 exp: []*notifier.Alert{ 1129 { 1130 Labels: []labels.Label{{Name: "l1", Value: "v1"}}, 1131 Annotations: []labels.Label{{Name: "a2", Value: "v2"}}, 1132 StartsAt: time.Unix(2, 0), 1133 EndsAt: time.Unix(3, 0), 1134 GeneratorURL: "http://localhost:9090/graph?g0.expr=up&g0.tab=1", 1135 }, 1136 }, 1137 }, 1138 { 1139 in: []*promRules.Alert{ 1140 { 1141 Labels: []labels.Label{{Name: "l1", Value: "v1"}}, 1142 Annotations: []labels.Label{{Name: "a2", Value: "v2"}}, 1143 ActiveAt: time.Unix(1, 0), 1144 FiredAt: time.Unix(2, 0), 1145 ResolvedAt: time.Unix(4, 0), 1146 }, 1147 }, 1148 exp: []*notifier.Alert{ 1149 { 1150 Labels: []labels.Label{{Name: "l1", Value: "v1"}}, 1151 Annotations: []labels.Label{{Name: "a2", Value: "v2"}}, 1152 StartsAt: time.Unix(2, 0), 1153 EndsAt: time.Unix(4, 0), 1154 GeneratorURL: "http://localhost:9090/graph?g0.expr=up&g0.tab=1", 1155 }, 1156 }, 1157 }, 1158 { 1159 in: []*promRules.Alert{}, 1160 }, 1161 } 1162 1163 for i, tc := range testCases { 1164 tc := tc 1165 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 1166 senderFunc := senderFunc(func(alerts ...*notifier.Alert) { 1167 if len(tc.in) == 0 { 1168 t.Fatalf("sender called with 0 alert") 1169 } 1170 require.Equal(t, tc.exp, alerts) 1171 }) 1172 SendAlerts(senderFunc, "http://localhost:9090")(context.TODO(), "up", tc.in...) 1173 }) 1174 } 1175 } 1176 1177 type fakeQuerier struct { 1178 fn func(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet 1179 } 1180 1181 func (f *fakeQuerier) Select(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { 1182 return f.fn(sortSeries, hints, matchers...) 1183 } 1184 1185 func (f *fakeQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 1186 return nil, nil, nil 1187 } 1188 1189 func (f *fakeQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 1190 return nil, nil, nil 1191 } 1192 func (f *fakeQuerier) Close() error { return nil } 1193 1194 // Tests for whether the Ruler is able to recover ALERTS_FOR_STATE state 1195 func TestRecoverAlertsPostOutage(t *testing.T) { 1196 // Test Setup 1197 // alert FOR 30m, already ran for 10m, outage down at 15m prior to now(), outage tolerance set to 1hr 1198 // EXPECTATION: for state for alert restores to 10m+(now-15m) 1199 1200 // FIRST set up 1 Alert rule with 30m FOR duration 1201 alertForDuration, _ := time.ParseDuration("30m") 1202 mockRules := map[string]rulespb.RuleGroupList{ 1203 "user1": { 1204 &rulespb.RuleGroupDesc{ 1205 Name: "group1", 1206 Namespace: "namespace1", 1207 User: "user1", 1208 Rules: []*rulespb.RuleDesc{ 1209 { 1210 Alert: "UP_ALERT", 1211 Expr: "1", // always fire for this test 1212 For: alertForDuration, 1213 }, 1214 }, 1215 Interval: interval, 1216 }, 1217 }, 1218 } 1219 1220 // NEXT, set up ruler config with outage tolerance = 1hr 1221 rulerCfg := defaultRulerConfig(t, newMockRuleStore(mockRules)) 1222 rulerCfg.OutageTolerance, _ = time.ParseDuration("1h") 1223 1224 // NEXT, set up mock distributor containing sample, 1225 // metric: ALERTS_FOR_STATE{alertname="UP_ALERT"}, ts: time.now()-15m, value: time.now()-25m 1226 currentTime := time.Now().UTC() 1227 downAtTime := currentTime.Add(time.Minute * -15) 1228 downAtTimeMs := downAtTime.UnixNano() / int64(time.Millisecond) 1229 downAtActiveAtTime := currentTime.Add(time.Minute * -25) 1230 downAtActiveSec := downAtActiveAtTime.Unix() 1231 1232 m := loki_storage.NewClientMetrics() 1233 defer m.Unregister() 1234 // create a ruler but don't start it. instead, we'll evaluate the rule groups manually. 1235 r := buildRuler(t, rulerCfg, &fakeQuerier{ 1236 fn: func(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { 1237 return series.NewConcreteSeriesSet([]storage.Series{ 1238 series.NewConcreteSeries( 1239 labels.Labels{ 1240 {Name: labels.MetricName, Value: "ALERTS_FOR_STATE"}, 1241 {Name: labels.AlertName, Value: mockRules["user1"][0].GetRules()[0].Alert}, 1242 }, 1243 []model.SamplePair{{Timestamp: model.Time(downAtTimeMs), Value: model.SampleValue(downAtActiveSec)}}, 1244 ), 1245 }) 1246 }, 1247 }, m, nil) 1248 r.syncRules(context.Background(), rulerSyncReasonInitial) 1249 1250 // assert initial state of rule group 1251 ruleGroup := r.manager.GetRules("user1")[0] 1252 require.Equal(t, time.Time{}, ruleGroup.GetLastEvaluation()) 1253 require.Equal(t, "group1", ruleGroup.Name()) 1254 require.Equal(t, 1, len(ruleGroup.Rules())) 1255 1256 // assert initial state of rule within rule group 1257 alertRule := ruleGroup.Rules()[0] 1258 require.Equal(t, time.Time{}, alertRule.GetEvaluationTimestamp()) 1259 require.Equal(t, "UP_ALERT", alertRule.Name()) 1260 require.Equal(t, promRules.HealthUnknown, alertRule.Health()) 1261 1262 // NEXT, evaluate the rule group the first time and assert 1263 ctx := user.InjectOrgID(context.Background(), "user1") 1264 ruleGroup.Eval(ctx, currentTime) 1265 1266 // since the eval is done at the current timestamp, the activeAt timestamp of alert should equal current timestamp 1267 require.Equal(t, "UP_ALERT", alertRule.Name()) 1268 require.Equal(t, promRules.HealthGood, alertRule.Health()) 1269 1270 activeMapRaw := reflect.ValueOf(alertRule).Elem().FieldByName("active") 1271 activeMapKeys := activeMapRaw.MapKeys() 1272 require.True(t, len(activeMapKeys) == 1) 1273 1274 activeAlertRuleRaw := activeMapRaw.MapIndex(activeMapKeys[0]).Elem() 1275 activeAtTimeRaw := activeAlertRuleRaw.FieldByName("ActiveAt") 1276 1277 require.Equal(t, promRules.StatePending, promRules.AlertState(activeAlertRuleRaw.FieldByName("State").Int())) 1278 require.Equal(t, reflect.NewAt(activeAtTimeRaw.Type(), unsafe.Pointer(activeAtTimeRaw.UnsafeAddr())).Elem().Interface().(time.Time), currentTime) 1279 1280 // NEXT, restore the FOR state and assert 1281 ruleGroup.RestoreForState(currentTime) 1282 1283 require.Equal(t, "UP_ALERT", alertRule.Name()) 1284 require.Equal(t, promRules.HealthGood, alertRule.Health()) 1285 require.Equal(t, promRules.StatePending, promRules.AlertState(activeAlertRuleRaw.FieldByName("State").Int())) 1286 require.Equal(t, reflect.NewAt(activeAtTimeRaw.Type(), unsafe.Pointer(activeAtTimeRaw.UnsafeAddr())).Elem().Interface().(time.Time), downAtActiveAtTime.Add(currentTime.Sub(downAtTime))) 1287 1288 // NEXT, 20 minutes is expected to be left, eval timestamp at currentTimestamp +20m 1289 currentTime = currentTime.Add(time.Minute * 20) 1290 ruleGroup.Eval(ctx, currentTime) 1291 1292 // assert alert state after alert is firing 1293 firedAtRaw := activeAlertRuleRaw.FieldByName("FiredAt") 1294 firedAtTime := reflect.NewAt(firedAtRaw.Type(), unsafe.Pointer(firedAtRaw.UnsafeAddr())).Elem().Interface().(time.Time) 1295 require.Equal(t, firedAtTime, currentTime) 1296 1297 require.Equal(t, promRules.StateFiring, promRules.AlertState(activeAlertRuleRaw.FieldByName("State").Int())) 1298 }