github.com/thanos-io/thanos@v0.32.5/pkg/rules/rules_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package rules 5 6 import ( 7 "context" 8 "path" 9 "path/filepath" 10 "testing" 11 "time" 12 13 "github.com/gogo/protobuf/proto" 14 "github.com/prometheus/prometheus/model/labels" 15 "github.com/prometheus/prometheus/storage" 16 17 "github.com/efficientgo/core/testutil" 18 "github.com/thanos-io/thanos/pkg/rules/rulespb" 19 "github.com/thanos-io/thanos/pkg/store/labelpb" 20 "github.com/thanos-io/thanos/pkg/store/storepb" 21 "github.com/thanos-io/thanos/pkg/testutil/custom" 22 ) 23 24 func TestMain(m *testing.M) { 25 custom.TolerantVerifyLeakMain(m) 26 } 27 28 // testRulesAgainstExamples tests against alerts.yaml and rules.yaml examples. 29 func testRulesAgainstExamples(t *testing.T, dir string, server rulespb.RulesServer) { 30 t.Helper() 31 32 // We don't test internals, just if groups are expected. 33 // TODO(bwplotka): Test internals as well, especially labels! 34 someAlert := &rulespb.Rule{Result: &rulespb.Rule_Alert{Alert: &rulespb.Alert{Name: "some"}}} 35 someRecording := &rulespb.Rule{Result: &rulespb.Rule_Recording{Recording: &rulespb.RecordingRule{Name: "some"}}} 36 37 expected := []*rulespb.RuleGroup{ 38 { 39 Name: "thanos-bucket-replicate", 40 File: filepath.Join(dir, "alerts.yaml"), 41 Rules: []*rulespb.Rule{someAlert, someAlert}, 42 Interval: 60, 43 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 44 }, 45 { 46 Name: "thanos-compact", 47 File: filepath.Join(dir, "alerts.yaml"), 48 Rules: []*rulespb.Rule{someAlert, someAlert, someAlert, someAlert, someAlert}, 49 Interval: 60, 50 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 51 }, 52 { 53 Name: "thanos-component-absent", 54 File: filepath.Join(dir, "alerts.yaml"), 55 Rules: []*rulespb.Rule{someAlert, someAlert, someAlert, someAlert, someAlert, someAlert}, 56 Interval: 60, 57 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 58 }, 59 { 60 Name: "thanos-query", 61 File: filepath.Join(dir, "alerts.yaml"), 62 Rules: []*rulespb.Rule{ 63 someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, 64 }, 65 Interval: 60, 66 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 67 }, 68 { 69 Name: "thanos-receive", 70 File: filepath.Join(dir, "alerts.yaml"), 71 Rules: []*rulespb.Rule{ 72 someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, 73 }, 74 Interval: 60, 75 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 76 }, 77 { 78 Name: "thanos-rule", 79 File: filepath.Join(dir, "alerts.yaml"), 80 Rules: []*rulespb.Rule{someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert, someAlert}, 81 Interval: 60, 82 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 83 }, 84 { 85 Name: "thanos-sidecar", 86 File: filepath.Join(dir, "alerts.yaml"), 87 Rules: []*rulespb.Rule{someAlert, someAlert}, 88 Interval: 60, 89 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 90 }, 91 { 92 Name: "thanos-store", 93 File: filepath.Join(dir, "alerts.yaml"), 94 Rules: []*rulespb.Rule{ 95 someAlert, someAlert, someAlert, someAlert, 96 }, 97 Interval: 60, 98 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 99 }, 100 { 101 Name: "thanos-bucket-replicate.rules", 102 File: filepath.Join(dir, "rules.yaml"), 103 Rules: nil, 104 Interval: 60, 105 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 106 }, 107 { 108 Name: "thanos-query.rules", 109 File: filepath.Join(dir, "rules.yaml"), 110 Rules: []*rulespb.Rule{ 111 someRecording, someRecording, someRecording, someRecording, someRecording, 112 }, 113 Interval: 60, 114 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 115 }, 116 { 117 Name: "thanos-receive.rules", 118 File: filepath.Join(dir, "rules.yaml"), 119 Rules: []*rulespb.Rule{ 120 someRecording, someRecording, someRecording, someRecording, someRecording, someRecording, someRecording, 121 }, 122 Interval: 60, 123 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 124 }, 125 { 126 Name: "thanos-store.rules", 127 File: filepath.Join(dir, "rules.yaml"), 128 Rules: []*rulespb.Rule{ 129 someRecording, someRecording, someRecording, someRecording, 130 }, 131 Interval: 60, 132 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 133 }, 134 } 135 136 for _, tcase := range []struct { 137 requestedType rulespb.RulesRequest_Type 138 expectedErr error 139 }{ 140 { 141 requestedType: rulespb.RulesRequest_ALL, 142 }, 143 { 144 requestedType: rulespb.RulesRequest_ALERT, 145 }, 146 { 147 requestedType: rulespb.RulesRequest_RECORD, 148 }, 149 } { 150 t.Run(tcase.requestedType.String(), func(t *testing.T) { 151 groups, w, err := NewGRPCClientWithDedup(server, nil).Rules(context.Background(), &rulespb.RulesRequest{ 152 Type: tcase.requestedType, 153 }) 154 testutil.Equals(t, storage.Warnings(nil), w) 155 if tcase.expectedErr != nil { 156 testutil.NotOk(t, err) 157 testutil.Equals(t, tcase.expectedErr.Error(), err.Error()) 158 return 159 } 160 testutil.Ok(t, err) 161 162 expectedForType := expected 163 if tcase.requestedType != rulespb.RulesRequest_ALL { 164 expectedForType = make([]*rulespb.RuleGroup, len(expected)) 165 for i, g := range expected { 166 expectedForType[i] = proto.Clone(g).(*rulespb.RuleGroup) 167 expectedForType[i].Rules = nil 168 169 for _, r := range g.Rules { 170 switch tcase.requestedType { 171 case rulespb.RulesRequest_ALERT: 172 if r.GetAlert() != nil { 173 expectedForType[i].Rules = append(expectedForType[i].Rules, someAlert) 174 } 175 case rulespb.RulesRequest_RECORD: 176 if r.GetRecording() != nil { 177 expectedForType[i].Rules = append(expectedForType[i].Rules, someRecording) 178 } 179 } 180 } 181 } 182 } 183 184 got := groups.Groups 185 186 // We don't want to be picky, just check what number and types of rules within group are. 187 for i := range got { 188 for j, r := range got[i].Rules { 189 if r.GetAlert() != nil { 190 got[i].Rules[j] = someAlert 191 continue 192 } 193 if r.GetRecording() != nil { 194 got[i].Rules[j] = someRecording 195 continue 196 } 197 t.Fatalf("Found rule in group %s that is neither recording not alert.", got[i].Name) 198 } 199 if len(got[i].Rules) == 0 { 200 // Fix, for test purposes. 201 got[i].Rules = nil 202 } 203 // Mask nondeterministic fields. 204 got[i].EvaluationDurationSeconds = 0 205 got[i].LastEvaluation = time.Time{} 206 207 t.Run(got[i].Name+" "+path.Base(got[i].File), func(t *testing.T) { 208 testutil.Equals(t, expectedForType[i], got[i]) 209 }) 210 } 211 testutil.Equals(t, expectedForType, got) 212 }) 213 } 214 } 215 216 func TestDedupRules(t *testing.T) { 217 for _, tc := range []struct { 218 name string 219 rules, want []*rulespb.Rule 220 replicaLabels []string 221 }{ 222 { 223 name: "nil slice", 224 rules: nil, 225 want: nil, 226 }, 227 { 228 name: "empty rule slice", 229 rules: []*rulespb.Rule{}, 230 want: []*rulespb.Rule{}, 231 }, 232 { 233 name: "single recording rule", 234 rules: []*rulespb.Rule{ 235 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 236 }, 237 want: []*rulespb.Rule{ 238 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 239 }, 240 }, 241 { 242 name: "single alert", 243 rules: []*rulespb.Rule{ 244 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 245 }, 246 want: []*rulespb.Rule{ 247 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 248 }, 249 }, 250 { 251 name: "rule type", 252 rules: []*rulespb.Rule{ 253 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 254 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 255 }, 256 want: []*rulespb.Rule{ 257 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 258 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 259 }, 260 }, 261 { 262 name: "rule name", 263 rules: []*rulespb.Rule{ 264 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 265 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 266 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 267 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 268 }, 269 want: []*rulespb.Rule{ 270 rulespb.NewAlertingRule(&rulespb.Alert{Name: "a1"}), 271 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 272 }, 273 }, 274 { 275 name: "rule labels", 276 rules: []*rulespb.Rule{ 277 rulespb.NewAlertingRule(&rulespb.Alert{ 278 Name: "a1", 279 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 280 {Name: "a", Value: "1"}, 281 }}}), 282 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 283 Name: "a1", 284 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 285 {Name: "a", Value: "1"}, 286 }}}), 287 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 288 Name: "a1", 289 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 290 {Name: "a", Value: "1"}, 291 }}}), 292 rulespb.NewAlertingRule(&rulespb.Alert{ 293 Name: "a1", 294 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 295 {Name: "a", Value: "1"}, 296 }}}), 297 }, 298 want: []*rulespb.Rule{ 299 rulespb.NewAlertingRule(&rulespb.Alert{ 300 Name: "a1", 301 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 302 {Name: "a", Value: "1"}, 303 }}}), 304 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 305 Name: "a1", 306 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 307 {Name: "a", Value: "1"}, 308 }}}), 309 }, 310 }, 311 { 312 name: "rule expression", 313 rules: []*rulespb.Rule{ 314 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 315 Name: "a1", 316 Query: "up", 317 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 318 {Name: "a", Value: "1"}, 319 }}}), 320 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 321 Name: "a1", 322 Query: "up", 323 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 324 {Name: "a", Value: "1"}, 325 }}}), 326 rulespb.NewAlertingRule(&rulespb.Alert{ 327 Name: "a1", 328 Query: "up", 329 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 330 {Name: "a", Value: "1"}, 331 }}}), 332 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 333 Name: "a1", 334 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 335 {Name: "a", Value: "1"}, 336 }}}), 337 rulespb.NewAlertingRule(&rulespb.Alert{ 338 Name: "a1", 339 Query: "up", 340 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 341 {Name: "a", Value: "1"}, 342 }}}), 343 }, 344 want: []*rulespb.Rule{ 345 rulespb.NewAlertingRule(&rulespb.Alert{ 346 Name: "a1", 347 Query: "up", 348 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 349 {Name: "a", Value: "1"}, 350 }}}), 351 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 352 Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 353 {Name: "a", Value: "1"}, 354 }}}), 355 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 356 Name: "a1", 357 Query: "up", 358 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 359 {Name: "a", Value: "1"}, 360 }}}), 361 }, 362 }, 363 { 364 name: "alert duration", 365 rules: []*rulespb.Rule{ 366 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 367 Name: "a1", 368 Query: "up", 369 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 370 {Name: "a", Value: "1"}, 371 }}}), 372 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 373 Name: "a1", 374 Query: "up", 375 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 376 {Name: "a", Value: "1"}, 377 }}}), 378 rulespb.NewAlertingRule(&rulespb.Alert{ 379 Name: "a1", 380 Query: "up", 381 DurationSeconds: 1.0, 382 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 383 {Name: "a", Value: "1"}, 384 }}}), 385 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 386 Name: "a1", 387 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 388 {Name: "a", Value: "1"}, 389 }}}), 390 rulespb.NewAlertingRule(&rulespb.Alert{ 391 Name: "a1", 392 Query: "up", 393 DurationSeconds: 1.0, 394 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 395 {Name: "a", Value: "1"}, 396 }}}), 397 rulespb.NewAlertingRule(&rulespb.Alert{ 398 Name: "a1", 399 Query: "up", 400 DurationSeconds: 2.0, 401 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 402 {Name: "a", Value: "1"}, 403 }}}), 404 }, 405 want: []*rulespb.Rule{ 406 rulespb.NewAlertingRule(&rulespb.Alert{ 407 Name: "a1", 408 Query: "up", 409 DurationSeconds: 1.0, 410 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 411 {Name: "a", Value: "1"}, 412 }}}), 413 rulespb.NewAlertingRule(&rulespb.Alert{ 414 Name: "a1", 415 Query: "up", 416 DurationSeconds: 2.0, 417 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 418 {Name: "a", Value: "1"}, 419 }}}), 420 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 421 Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 422 {Name: "a", Value: "1"}, 423 }}}), 424 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 425 Name: "a1", Query: "up", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 426 {Name: "a", Value: "1"}, 427 }}}), 428 }, 429 }, 430 { 431 name: "alert duration with replicas", 432 rules: []*rulespb.Rule{ 433 rulespb.NewAlertingRule(&rulespb.Alert{ 434 Name: "a1", 435 Query: "up", 436 DurationSeconds: 1.0, 437 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 438 {Name: "a", Value: "1"}, 439 {Name: "replica", Value: "1"}, 440 }}}), 441 rulespb.NewAlertingRule(&rulespb.Alert{ 442 Name: "a1", 443 Query: "up", 444 DurationSeconds: 2.0, 445 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 446 {Name: "a", Value: "1"}, 447 }}}), 448 }, 449 want: []*rulespb.Rule{ 450 rulespb.NewAlertingRule(&rulespb.Alert{ 451 Name: "a1", 452 Query: "up", 453 DurationSeconds: 1.0, 454 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 455 {Name: "a", Value: "1"}, 456 }}}), 457 rulespb.NewAlertingRule(&rulespb.Alert{ 458 Name: "a1", 459 Query: "up", 460 DurationSeconds: 2.0, 461 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 462 {Name: "a", Value: "1"}, 463 }}}), 464 }, 465 replicaLabels: []string{"replica"}, 466 }, 467 { 468 name: "replica labels", 469 rules: []*rulespb.Rule{ 470 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 471 {Name: "a", Value: "1"}, 472 {Name: "replica", Value: "3"}, 473 }}}), 474 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 475 {Name: "a", Value: "1"}, 476 {Name: "replica", Value: "1"}, 477 }}}), 478 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 479 {Name: "a", Value: "1"}, 480 {Name: "replica", Value: "2"}, 481 }}}), 482 }, 483 want: []*rulespb.Rule{ 484 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 485 {Name: "a", Value: "1"}, 486 }}}), 487 }, 488 replicaLabels: []string{"replica"}, 489 }, 490 { 491 name: "ambiguous replica labels", 492 rules: []*rulespb.Rule{ 493 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 494 {Name: "replica", Value: "1"}, 495 {Name: "a", Value: "1"}, 496 }}}), 497 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 498 {Name: "replica", Value: "1"}, 499 }}}), 500 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 501 {Name: "replica", Value: "1"}, 502 {Name: "a", Value: "2"}, 503 }}}), 504 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 505 {Name: "replica", Value: "1"}, 506 {Name: "a", Value: "1"}, 507 }}}), 508 }, 509 want: []*rulespb.Rule{ 510 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 511 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 512 {Name: "a", Value: "1"}, 513 }}}), 514 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 515 {Name: "a", Value: "2"}, 516 }}}), 517 }, 518 replicaLabels: []string{"replica"}, 519 }, 520 { 521 name: "youngest recording rule", 522 rules: []*rulespb.Rule{ 523 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 524 Name: "a1", 525 Query: "up", 526 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 527 {Name: "replica", Value: "2"}, 528 }}, 529 LastEvaluation: time.Unix(0, 0), 530 }), 531 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 532 Name: "a1", 533 Query: "up", 534 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 535 {Name: "replica", Value: "1"}, 536 }}, 537 LastEvaluation: time.Unix(1, 0), 538 }), 539 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 540 Name: "a1", 541 Query: "up", 542 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 543 {Name: "replica", Value: "3"}, 544 }}, 545 LastEvaluation: time.Unix(3, 0), 546 }), 547 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 548 Name: "a1", 549 Query: "up", 550 LastEvaluation: time.Unix(2, 0), 551 }), 552 }, 553 want: []*rulespb.Rule{ 554 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 555 Name: "a1", 556 Query: "up", 557 LastEvaluation: time.Unix(3, 0), 558 }), 559 }, 560 replicaLabels: []string{"replica"}, 561 }, 562 { 563 name: "youngest firing alert", 564 rules: []*rulespb.Rule{ 565 rulespb.NewAlertingRule(&rulespb.Alert{ 566 Name: "a1", 567 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 568 {Name: "replica", Value: "2"}, 569 }}, 570 LastEvaluation: time.Unix(4, 0), 571 }), 572 rulespb.NewAlertingRule(&rulespb.Alert{ 573 Name: "a1", 574 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 575 {Name: "replica", Value: "2"}, 576 {Name: "foo", Value: "bar"}, 577 }}, 578 LastEvaluation: time.Unix(2, 0), 579 }), 580 rulespb.NewAlertingRule(&rulespb.Alert{ 581 Name: "a2", 582 LastEvaluation: time.Unix(2, 0), 583 State: rulespb.AlertState_PENDING, 584 }), 585 rulespb.NewAlertingRule(&rulespb.Alert{ 586 Name: "a1", 587 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 588 {Name: "replica", Value: "1"}, 589 }}, 590 LastEvaluation: time.Unix(3, 0), 591 }), 592 rulespb.NewAlertingRule(&rulespb.Alert{ 593 Name: "a2", 594 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 595 {Name: "replica", Value: "1"}, 596 }}, 597 LastEvaluation: time.Unix(3, 0), 598 State: rulespb.AlertState_PENDING, 599 }), 600 rulespb.NewAlertingRule(&rulespb.Alert{ 601 Name: "a1", 602 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 603 {Name: "replica", Value: "3"}, 604 }}, 605 LastEvaluation: time.Unix(2, 0), 606 State: rulespb.AlertState_FIRING, 607 }), 608 rulespb.NewAlertingRule(&rulespb.Alert{ 609 Name: "a1", 610 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 611 {Name: "foo", Value: "bar"}, 612 }}, 613 State: rulespb.AlertState_FIRING, 614 LastEvaluation: time.Unix(1, 0), 615 }), 616 }, 617 want: []*rulespb.Rule{ 618 rulespb.NewAlertingRule(&rulespb.Alert{ 619 State: rulespb.AlertState_FIRING, 620 Name: "a1", 621 LastEvaluation: time.Unix(2, 0), 622 }), 623 rulespb.NewAlertingRule(&rulespb.Alert{ 624 State: rulespb.AlertState_FIRING, 625 Name: "a1", 626 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 627 {Name: "foo", Value: "bar"}, 628 }}, 629 LastEvaluation: time.Unix(1, 0), 630 }), 631 rulespb.NewAlertingRule(&rulespb.Alert{ 632 State: rulespb.AlertState_PENDING, 633 Name: "a2", 634 LastEvaluation: time.Unix(3, 0), 635 }), 636 }, 637 replicaLabels: []string{"replica"}, 638 }, 639 { 640 name: "alerts with different severity", 641 rules: []*rulespb.Rule{ 642 rulespb.NewAlertingRule(&rulespb.Alert{ 643 Name: "a1", 644 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 645 {Name: "replica", Value: "1"}, 646 {Name: "severity", Value: "warning"}, 647 }}, 648 LastEvaluation: time.Unix(1, 0), 649 }), 650 rulespb.NewAlertingRule(&rulespb.Alert{ 651 Name: "a1", 652 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 653 {Name: "replica", Value: "1"}, 654 {Name: "severity", Value: "critical"}, 655 }}, 656 LastEvaluation: time.Unix(1, 0), 657 }), 658 rulespb.NewAlertingRule(&rulespb.Alert{ 659 Name: "a1", 660 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 661 {Name: "replica", Value: "2"}, 662 {Name: "severity", Value: "warning"}, 663 }}, 664 LastEvaluation: time.Unix(1, 0), 665 }), 666 rulespb.NewAlertingRule(&rulespb.Alert{ 667 Name: "a1", 668 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 669 {Name: "replica", Value: "2"}, 670 {Name: "severity", Value: "critical"}, 671 }}, 672 LastEvaluation: time.Unix(1, 0), 673 }), 674 }, 675 want: []*rulespb.Rule{ 676 rulespb.NewAlertingRule(&rulespb.Alert{ 677 Name: "a1", 678 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 679 {Name: "severity", Value: "critical"}, 680 }}, 681 LastEvaluation: time.Unix(1, 0), 682 }), 683 rulespb.NewAlertingRule(&rulespb.Alert{ 684 Name: "a1", 685 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 686 {Name: "severity", Value: "warning"}, 687 }}, 688 LastEvaluation: time.Unix(1, 0), 689 }), 690 }, 691 replicaLabels: []string{"replica"}, 692 }, 693 { 694 name: "alerts with missing replica labels", 695 rules: []*rulespb.Rule{ 696 rulespb.NewAlertingRule(&rulespb.Alert{ 697 Name: "a1", 698 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 699 {Name: "replica", Value: "1"}, 700 {Name: "label", Value: "foo"}, 701 }}, 702 LastEvaluation: time.Unix(1, 0), 703 }), 704 rulespb.NewAlertingRule(&rulespb.Alert{ 705 Name: "a1", 706 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 707 {Name: "replica", Value: "2"}, 708 {Name: "label", Value: "foo"}, 709 }}, 710 LastEvaluation: time.Unix(1, 0), 711 }), 712 rulespb.NewAlertingRule(&rulespb.Alert{ 713 Name: "a1", 714 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 715 {Name: "label", Value: "foo"}, 716 }}, 717 LastEvaluation: time.Unix(1, 0), 718 }), 719 }, 720 want: []*rulespb.Rule{ 721 rulespb.NewAlertingRule(&rulespb.Alert{ 722 Name: "a1", 723 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 724 {Name: "label", Value: "foo"}, 725 }}, 726 LastEvaluation: time.Unix(1, 0), 727 }), 728 }, 729 replicaLabels: []string{"replica"}, 730 }, 731 } { 732 t.Run(tc.name, func(t *testing.T) { 733 replicaLabels := make(map[string]struct{}) 734 for _, lbl := range tc.replicaLabels { 735 replicaLabels[lbl] = struct{}{} 736 } 737 testutil.Equals(t, tc.want, dedupRules(tc.rules, replicaLabels)) 738 }) 739 } 740 } 741 742 func TestDedupGroups(t *testing.T) { 743 for _, tc := range []struct { 744 name string 745 groups, want []*rulespb.RuleGroup 746 }{ 747 { 748 name: "no groups", 749 groups: nil, 750 want: nil, 751 }, 752 { 753 name: "empty group", 754 groups: []*rulespb.RuleGroup{ 755 {Name: "a"}, 756 }, 757 want: []*rulespb.RuleGroup{ 758 {Name: "a"}, 759 }, 760 }, 761 { 762 name: "multiple empty groups", 763 groups: []*rulespb.RuleGroup{ 764 {Name: "a"}, 765 {Name: "b"}, 766 }, 767 want: []*rulespb.RuleGroup{ 768 {Name: "a"}, 769 {Name: "b"}, 770 }, 771 }, 772 { 773 name: "single group", 774 groups: []*rulespb.RuleGroup{ 775 { 776 Name: "a", 777 Rules: []*rulespb.Rule{ 778 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 779 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 780 }, 781 }, 782 }, 783 want: []*rulespb.RuleGroup{ 784 { 785 Name: "a", 786 Rules: []*rulespb.Rule{ 787 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 788 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 789 }, 790 }, 791 }, 792 }, 793 { 794 name: "separate groups", 795 groups: []*rulespb.RuleGroup{ 796 { 797 Name: "a", 798 Rules: []*rulespb.Rule{ 799 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 800 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 801 }, 802 }, 803 { 804 Name: "b", 805 Rules: []*rulespb.Rule{ 806 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}), 807 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}), 808 }, 809 }, 810 }, 811 want: []*rulespb.RuleGroup{ 812 { 813 Name: "a", 814 Rules: []*rulespb.Rule{ 815 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 816 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 817 }, 818 }, 819 { 820 Name: "b", 821 Rules: []*rulespb.Rule{ 822 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}), 823 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}), 824 }, 825 }, 826 }, 827 }, 828 { 829 name: "duplicate groups", 830 groups: []*rulespb.RuleGroup{ 831 { 832 Name: "a", 833 Rules: []*rulespb.Rule{ 834 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 835 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 836 }, 837 }, 838 { 839 Name: "b", 840 Rules: []*rulespb.Rule{ 841 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}), 842 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}), 843 }, 844 }, 845 { 846 Name: "c", 847 }, 848 { 849 Name: "a", 850 Rules: []*rulespb.Rule{ 851 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 852 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 853 }, 854 }, 855 }, 856 want: []*rulespb.RuleGroup{ 857 { 858 Name: "a", 859 Rules: []*rulespb.Rule{ 860 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 861 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 862 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 863 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 864 }, 865 }, 866 { 867 Name: "b", 868 Rules: []*rulespb.Rule{ 869 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}), 870 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}), 871 }, 872 }, 873 { 874 Name: "c", 875 }, 876 }, 877 }, 878 { 879 name: "distinct file names", 880 groups: []*rulespb.RuleGroup{ 881 { 882 Name: "a", 883 File: "foo.yaml", 884 Rules: []*rulespb.Rule{ 885 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 886 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 887 }, 888 }, 889 { 890 Name: "a", 891 Rules: []*rulespb.Rule{ 892 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 893 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 894 }, 895 }, 896 { 897 Name: "b", 898 Rules: []*rulespb.Rule{ 899 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}), 900 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}), 901 }, 902 }, 903 { 904 Name: "c", 905 }, 906 { 907 Name: "a", 908 File: "bar.yaml", 909 Rules: []*rulespb.Rule{ 910 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 911 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 912 }, 913 }, 914 }, 915 want: []*rulespb.RuleGroup{ 916 { 917 Name: "a", 918 Rules: []*rulespb.Rule{ 919 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 920 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 921 }, 922 }, 923 { 924 Name: "b", 925 Rules: []*rulespb.Rule{ 926 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b1"}), 927 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "b2"}), 928 }, 929 }, 930 { 931 Name: "c", 932 }, 933 { 934 Name: "a", 935 File: "bar.yaml", 936 Rules: []*rulespb.Rule{ 937 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 938 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 939 }, 940 }, 941 { 942 Name: "a", 943 File: "foo.yaml", 944 Rules: []*rulespb.Rule{ 945 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a1"}), 946 rulespb.NewRecordingRule(&rulespb.RecordingRule{Name: "a2"}), 947 }, 948 }, 949 }, 950 }, 951 } { 952 t.Run(tc.name, func(t *testing.T) { 953 t.Run(tc.name, func(t *testing.T) { 954 testutil.Equals(t, tc.want, dedupGroups(tc.groups)) 955 }) 956 }) 957 } 958 } 959 960 func TestFilterRules(t *testing.T) { 961 for _, tc := range []struct { 962 name string 963 matcherSets [][]*labels.Matcher 964 groups, want []*rulespb.RuleGroup 965 }{ 966 { 967 name: "no groups", 968 groups: nil, 969 want: nil, 970 }, 971 { 972 name: "empty group with no matcher", 973 groups: []*rulespb.RuleGroup{ 974 {Name: "a"}, 975 }, 976 want: []*rulespb.RuleGroup{ 977 {Name: "a"}, 978 }, 979 }, 980 { 981 name: "multiple empty groups with no matcher", 982 groups: []*rulespb.RuleGroup{ 983 {Name: "a"}, 984 {Name: "b"}, 985 }, 986 want: []*rulespb.RuleGroup{ 987 {Name: "a"}, 988 {Name: "b"}, 989 }, 990 }, 991 { 992 name: "single group with labeled rules and no matcher", 993 groups: []*rulespb.RuleGroup{ 994 { 995 Name: "a", 996 Rules: []*rulespb.Rule{ 997 rulespb.NewAlertingRule(&rulespb.Alert{ 998 Name: "a1", 999 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1000 {Name: "replica", Value: "1"}, 1001 {Name: "label", Value: "foo"}, 1002 }}, 1003 }), 1004 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1005 Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1006 {Name: "label", Value: "foo"}, 1007 }}, 1008 }), 1009 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1010 Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1011 {Name: "otherlabel", Value: "bar"}, 1012 }}, 1013 }), 1014 }, 1015 }, 1016 }, 1017 want: []*rulespb.RuleGroup{ 1018 { 1019 Name: "a", 1020 Rules: []*rulespb.Rule{ 1021 rulespb.NewAlertingRule(&rulespb.Alert{ 1022 Name: "a1", 1023 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1024 {Name: "replica", Value: "1"}, 1025 {Name: "label", Value: "foo"}, 1026 }}, 1027 }), 1028 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1029 Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1030 {Name: "label", Value: "foo"}, 1031 }}, 1032 }), 1033 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1034 Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1035 {Name: "otherlabel", Value: "bar"}, 1036 }}, 1037 }), 1038 }, 1039 }, 1040 }, 1041 }, 1042 { 1043 name: "single group with labeled rules and matcher", 1044 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "label", Value: "foo", Type: labels.MatchEqual}}}, 1045 groups: []*rulespb.RuleGroup{ 1046 { 1047 Name: "a", 1048 Rules: []*rulespb.Rule{ 1049 rulespb.NewAlertingRule(&rulespb.Alert{ 1050 Name: "a1", 1051 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1052 {Name: "replica", Value: "1"}, 1053 {Name: "label", Value: "foo"}, 1054 }}, 1055 }), 1056 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1057 Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1058 {Name: "label", Value: "foo"}, 1059 }}, 1060 }), 1061 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1062 Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1063 {Name: "otherlabel", Value: "bar"}, 1064 }}, 1065 }), 1066 }, 1067 }, 1068 }, 1069 want: []*rulespb.RuleGroup{ 1070 { 1071 Name: "a", 1072 Rules: []*rulespb.Rule{ 1073 rulespb.NewAlertingRule(&rulespb.Alert{ 1074 Name: "a1", 1075 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1076 {Name: "replica", Value: "1"}, 1077 {Name: "label", Value: "foo"}, 1078 }}, 1079 }), 1080 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1081 Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1082 {Name: "label", Value: "foo"}, 1083 }}, 1084 }), 1085 }, 1086 }, 1087 }, 1088 }, 1089 { 1090 name: "single group with no match for matcher", 1091 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "foo", Value: "bar", Type: labels.MatchEqual}}}, 1092 groups: []*rulespb.RuleGroup{ 1093 { 1094 Name: "a", 1095 Rules: []*rulespb.Rule{ 1096 rulespb.NewAlertingRule(&rulespb.Alert{ 1097 Name: "a1", 1098 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1099 {Name: "replica", Value: "1"}, 1100 {Name: "label", Value: "foo"}, 1101 }}, 1102 }), 1103 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1104 Name: "r1", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1105 {Name: "label", Value: "foo"}, 1106 }}, 1107 }), 1108 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1109 Name: "r2", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1110 {Name: "otherlabel", Value: "bar"}, 1111 }}, 1112 }), 1113 }, 1114 }, 1115 }, 1116 want: []*rulespb.RuleGroup{}, 1117 }, 1118 { 1119 name: "single group with templated labels", 1120 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "templatedlabel", Value: "{{ $externalURL }}", Type: labels.MatchEqual}}}, 1121 groups: []*rulespb.RuleGroup{ 1122 { 1123 Name: "a", 1124 Rules: []*rulespb.Rule{ 1125 rulespb.NewAlertingRule(&rulespb.Alert{ 1126 Name: "a1", 1127 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1128 {Name: "label", Value: "foo"}, 1129 {Name: "templatedlabel", Value: "{{ $externalURL }}"}, 1130 }}, 1131 }), 1132 rulespb.NewAlertingRule(&rulespb.Alert{ 1133 Name: "a2", 1134 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1135 {Name: "label", Value: "foo"}, 1136 }}, 1137 }), 1138 }, 1139 }, 1140 }, 1141 want: []*rulespb.RuleGroup{}, 1142 }, 1143 { 1144 name: "multiple group with labeled rules and matcher", 1145 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "label", Value: "foo", Type: labels.MatchEqual}}}, 1146 groups: []*rulespb.RuleGroup{ 1147 { 1148 Name: "a", 1149 Rules: []*rulespb.Rule{ 1150 rulespb.NewAlertingRule(&rulespb.Alert{ 1151 Name: "a1a", 1152 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1153 {Name: "label", Value: "foo"}, 1154 }}, 1155 }), 1156 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1157 Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1158 {Name: "label", Value: "foo"}, 1159 }}, 1160 }), 1161 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1162 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1163 {Name: "otherlabel", Value: "bar"}, 1164 }}, 1165 }), 1166 }, 1167 }, 1168 { 1169 Name: "b", 1170 Rules: []*rulespb.Rule{ 1171 rulespb.NewAlertingRule(&rulespb.Alert{ 1172 Name: "a1b", 1173 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1174 {Name: "some", Value: "label"}, 1175 }}, 1176 }), 1177 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1178 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1179 {Name: "label", Value: "foo"}, 1180 }}, 1181 }), 1182 }, 1183 }, 1184 }, 1185 want: []*rulespb.RuleGroup{ 1186 { 1187 Name: "a", 1188 Rules: []*rulespb.Rule{ 1189 rulespb.NewAlertingRule(&rulespb.Alert{ 1190 Name: "a1a", 1191 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1192 {Name: "label", Value: "foo"}, 1193 }}, 1194 }), 1195 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1196 Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1197 {Name: "label", Value: "foo"}, 1198 }}, 1199 }), 1200 }, 1201 }, 1202 { 1203 Name: "b", 1204 Rules: []*rulespb.Rule{ 1205 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1206 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1207 {Name: "label", Value: "foo"}, 1208 }}, 1209 }), 1210 }, 1211 }, 1212 }, 1213 }, 1214 { 1215 name: "multiple group with labeled rules and no match", 1216 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "foo", Value: "bar", Type: labels.MatchEqual}}}, 1217 groups: []*rulespb.RuleGroup{ 1218 { 1219 Name: "a", 1220 Rules: []*rulespb.Rule{ 1221 rulespb.NewAlertingRule(&rulespb.Alert{ 1222 Name: "a1a", 1223 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1224 {Name: "label", Value: "foo"}, 1225 }}, 1226 }), 1227 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1228 Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1229 {Name: "label", Value: "foo"}, 1230 }}, 1231 }), 1232 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1233 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1234 {Name: "otherlabel", Value: "bar"}, 1235 }}, 1236 }), 1237 }, 1238 }, 1239 { 1240 Name: "b", 1241 Rules: []*rulespb.Rule{ 1242 rulespb.NewAlertingRule(&rulespb.Alert{ 1243 Name: "a1b", 1244 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1245 {Name: "some", Value: "label"}, 1246 }}, 1247 }), 1248 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1249 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1250 {Name: "label", Value: "foo"}, 1251 }}, 1252 }), 1253 }, 1254 }, 1255 }, 1256 want: []*rulespb.RuleGroup{}, 1257 }, 1258 { 1259 name: "multiple group with templated labels", 1260 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "templatedlabel", Value: "{{ $externalURL }}", Type: labels.MatchEqual}}}, 1261 groups: []*rulespb.RuleGroup{ 1262 { 1263 Name: "a", 1264 Rules: []*rulespb.Rule{ 1265 rulespb.NewAlertingRule(&rulespb.Alert{ 1266 Name: "a1a", 1267 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1268 {Name: "templatedlabel", Value: "{{ $externalURL }}"}, 1269 }}, 1270 }), 1271 }, 1272 }, 1273 { 1274 Name: "b", 1275 Rules: []*rulespb.Rule{ 1276 rulespb.NewAlertingRule(&rulespb.Alert{ 1277 Name: "a1b", 1278 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1279 {Name: "templated", Value: "{{ $externalURL }}"}, 1280 }}, 1281 }), 1282 }, 1283 }, 1284 }, 1285 want: []*rulespb.RuleGroup{}, 1286 }, 1287 { 1288 name: "multiple group with templated labels and non templated matcher", 1289 matcherSets: [][]*labels.Matcher{{&labels.Matcher{Name: "templatedlabel", Value: "foo", Type: labels.MatchEqual}}}, 1290 groups: []*rulespb.RuleGroup{ 1291 { 1292 Name: "a", 1293 Rules: []*rulespb.Rule{ 1294 rulespb.NewAlertingRule(&rulespb.Alert{ 1295 Name: "a1a", 1296 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1297 {Name: "templatedlabel", Value: "{{ $externalURL }}"}, 1298 }}, 1299 }), 1300 }, 1301 }, 1302 { 1303 Name: "b", 1304 Rules: []*rulespb.Rule{ 1305 rulespb.NewAlertingRule(&rulespb.Alert{ 1306 Name: "a1b", 1307 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1308 {Name: "templated", Value: "{{ $externalURL }}"}, 1309 }}, 1310 }), 1311 }, 1312 }, 1313 }, 1314 want: []*rulespb.RuleGroup{}, 1315 }, 1316 { 1317 name: "multiple group with regex matcher", 1318 matcherSets: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchRegexp, "label", "f.*")}}, 1319 groups: []*rulespb.RuleGroup{ 1320 { 1321 Name: "a", 1322 Rules: []*rulespb.Rule{ 1323 rulespb.NewAlertingRule(&rulespb.Alert{ 1324 Name: "a1a", 1325 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1326 {Name: "label", Value: "foo"}, 1327 }}, 1328 }), 1329 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1330 Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1331 {Name: "label", Value: "foo"}, 1332 }}, 1333 }), 1334 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1335 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1336 {Name: "otherlabel", Value: "bar"}, 1337 }}, 1338 }), 1339 }, 1340 }, 1341 { 1342 Name: "b", 1343 Rules: []*rulespb.Rule{ 1344 rulespb.NewAlertingRule(&rulespb.Alert{ 1345 Name: "a1b", 1346 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1347 {Name: "some", Value: "label"}, 1348 }}, 1349 }), 1350 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1351 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1352 {Name: "label", Value: "foo"}, 1353 }}, 1354 }), 1355 }, 1356 }, 1357 }, 1358 want: []*rulespb.RuleGroup{ 1359 { 1360 Name: "a", 1361 Rules: []*rulespb.Rule{ 1362 rulespb.NewAlertingRule(&rulespb.Alert{ 1363 Name: "a1a", 1364 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1365 {Name: "label", Value: "foo"}, 1366 }}, 1367 }), 1368 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1369 Name: "r1a", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1370 {Name: "label", Value: "foo"}, 1371 }}, 1372 }), 1373 }, 1374 }, 1375 { 1376 Name: "b", 1377 Rules: []*rulespb.Rule{ 1378 rulespb.NewRecordingRule(&rulespb.RecordingRule{ 1379 Name: "r1b", Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 1380 {Name: "label", Value: "foo"}, 1381 }}, 1382 }), 1383 }, 1384 }, 1385 }, 1386 }, 1387 } { 1388 t.Run(tc.name, func(t *testing.T) { 1389 testutil.Equals(t, tc.want, filterRules(tc.groups, tc.matcherSets)) 1390 }) 1391 } 1392 }