github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/rules/validator/validator_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package validator 22 23 import ( 24 "fmt" 25 "math" 26 "strings" 27 "testing" 28 29 "github.com/m3db/m3/src/cluster/generated/proto/commonpb" 30 "github.com/m3db/m3/src/cluster/kv/mem" 31 "github.com/m3db/m3/src/metrics/aggregation" 32 "github.com/m3db/m3/src/metrics/errors" 33 "github.com/m3db/m3/src/metrics/filters" 34 "github.com/m3db/m3/src/metrics/metric" 35 "github.com/m3db/m3/src/metrics/pipeline" 36 "github.com/m3db/m3/src/metrics/policy" 37 "github.com/m3db/m3/src/metrics/rules/validator/namespace" 38 "github.com/m3db/m3/src/metrics/rules/validator/namespace/kv" 39 "github.com/m3db/m3/src/metrics/rules/view" 40 "github.com/m3db/m3/src/metrics/transformation" 41 42 "github.com/fortytw2/leaktest" 43 "github.com/stretchr/testify/assert" 44 "github.com/stretchr/testify/require" 45 ) 46 47 const ( 48 testTypeTag = "type" 49 testCounterType = "counter" 50 testTimerType = "timer" 51 testGaugeType = "gauge" 52 testNamespacesKey = "testNamespaces" 53 ) 54 55 var ( 56 testNamespaces = []string{"foo", "bar"} 57 ) 58 59 func TestValidatorDefaultNamespaceValidator(t *testing.T) { 60 v := NewValidator(testValidatorOptions()).(*validator) 61 62 inputs := []string{"foo", "bar", "baz"} 63 for _, input := range inputs { 64 require.NoError(t, v.validateNamespace(input)) 65 } 66 } 67 68 func TestValidatorInvalidNamespace(t *testing.T) { 69 defer leaktest.Check(t)() 70 71 nsValidator := testKVNamespaceValidator(t) 72 opts := testValidatorOptions().SetNamespaceValidator(nsValidator) 73 v := NewValidator(opts) 74 defer v.Close() 75 76 view := view.RuleSet{Namespace: "baz"} 77 require.Error(t, v.ValidateSnapshot(view)) 78 } 79 80 func TestValidatorValidNamespace(t *testing.T) { 81 defer leaktest.Check(t)() 82 83 nsValidator := testKVNamespaceValidator(t) 84 opts := testValidatorOptions().SetNamespaceValidator(nsValidator) 85 v := NewValidator(opts) 86 defer v.Close() 87 88 view := view.RuleSet{Namespace: "foo"} 89 require.NoError(t, v.ValidateSnapshot(view)) 90 } 91 92 func TestValidatorValidateDuplicateMappingRules(t *testing.T) { 93 view := view.RuleSet{ 94 MappingRules: []view.MappingRule{ 95 { 96 Name: "snapshot1", 97 Filter: "tag1:value1", 98 StoragePolicies: testStoragePolicies(), 99 }, 100 { 101 Name: "snapshot1", 102 Filter: "tag1:value1", 103 StoragePolicies: testStoragePolicies(), 104 }, 105 }, 106 } 107 validator := NewValidator(testValidatorOptions()) 108 err := validator.ValidateSnapshot(view) 109 require.Error(t, err) 110 _, ok := err.(errors.InvalidInputError) 111 require.True(t, ok) 112 } 113 114 func TestValidatorValidateNoDuplicateMappingRulesWithTombstone(t *testing.T) { 115 view := view.RuleSet{ 116 MappingRules: []view.MappingRule{ 117 { 118 Name: "snapshot1", 119 Filter: "tag1:value1", 120 Tombstoned: true, 121 StoragePolicies: testStoragePolicies(), 122 }, 123 { 124 Name: "snapshot1", 125 Filter: "tag1:value1", 126 StoragePolicies: testStoragePolicies(), 127 }, 128 }, 129 } 130 131 validator := NewValidator(testValidatorOptions()) 132 require.NoError(t, validator.ValidateSnapshot(view)) 133 } 134 135 func TestValidatorValidateMappingRuleInvalidFilterExpr(t *testing.T) { 136 view := view.RuleSet{ 137 MappingRules: []view.MappingRule{ 138 { 139 Name: "snapshot1", 140 Filter: "randomTag:*too*many*wildcards*", 141 }, 142 }, 143 } 144 validator := NewValidator(testValidatorOptions()) 145 require.Error(t, validator.ValidateSnapshot(view)) 146 } 147 148 func TestValidatorValidateMappingRuleInvalidFilterTagName(t *testing.T) { 149 invalidChars := []rune{'$'} 150 view := view.RuleSet{ 151 MappingRules: []view.MappingRule{ 152 { 153 Name: "snapshot1", 154 Filter: "random$Tag:foo", 155 }, 156 }, 157 } 158 validator := NewValidator(testValidatorOptions().SetTagNameInvalidChars(invalidChars)) 159 require.Error(t, validator.ValidateSnapshot(view)) 160 } 161 162 func TestValidatorValidateMappingRuleInvalidMetricType(t *testing.T) { 163 view := view.RuleSet{ 164 MappingRules: []view.MappingRule{ 165 { 166 Name: "snapshot1", 167 Filter: testTypeTag + ":nonexistent", 168 StoragePolicies: testStoragePolicies(), 169 }, 170 }, 171 } 172 173 validator := NewValidator(testValidatorOptions()) 174 require.Error(t, validator.ValidateSnapshot(view)) 175 } 176 177 func TestValidatorValidateMappingRuleInvalidAggregationType(t *testing.T) { 178 view := view.RuleSet{ 179 MappingRules: []view.MappingRule{ 180 { 181 Name: "snapshot1", 182 Filter: testTypeTag + ":" + testCounterType, 183 AggregationID: aggregation.ID{1234567789}, 184 }, 185 }, 186 } 187 188 validator := NewValidator(testValidatorOptions()) 189 require.Error(t, validator.ValidateSnapshot(view)) 190 } 191 192 func TestValidatorValidateMappingRuleMultipleAggregationTypes(t *testing.T) { 193 testAggregationTypes := []aggregation.Type{aggregation.Count, aggregation.Max} 194 view := view.RuleSet{ 195 MappingRules: []view.MappingRule{ 196 { 197 Name: "snapshot1", 198 Filter: testTypeTag + ":" + testTimerType, 199 AggregationID: aggregation.MustCompressTypes(aggregation.Count, aggregation.Max), 200 StoragePolicies: testStoragePolicies(), 201 }, 202 }, 203 } 204 inputs := []struct { 205 opts Options 206 expectErr bool 207 }{ 208 { 209 // By default multiple aggregation types are allowed for timers. 210 opts: testValidatorOptions().SetAllowedFirstLevelAggregationTypesFor(metric.TimerType, testAggregationTypes), 211 expectErr: false, 212 }, 213 { 214 // Explicitly disallow multiple aggregation types for timers. 215 opts: testValidatorOptions().SetAllowedFirstLevelAggregationTypesFor(metric.TimerType, testAggregationTypes).SetMultiAggregationTypesEnabledFor(nil), 216 expectErr: true, 217 }, 218 } 219 220 for _, input := range inputs { 221 validator := NewValidator(input.opts) 222 if input.expectErr { 223 require.Error(t, validator.ValidateSnapshot(view)) 224 } else { 225 require.NoError(t, validator.ValidateSnapshot(view)) 226 } 227 } 228 } 229 230 func TestValidatorValidateMappingRuleFirstLevelAggregationType(t *testing.T) { 231 testAggregationTypes := []aggregation.Type{aggregation.Count, aggregation.Max} 232 view := view.RuleSet{ 233 MappingRules: []view.MappingRule{ 234 { 235 Name: "snapshot1", 236 Filter: testTypeTag + ":" + testTimerType, 237 AggregationID: aggregation.MustCompressTypes(aggregation.Count, aggregation.Max), 238 StoragePolicies: testStoragePolicies(), 239 }, 240 }, 241 } 242 inputs := []struct { 243 opts Options 244 expectErr bool 245 }{ 246 { 247 // By default no custom aggregation type is allowed. 248 opts: testValidatorOptions(), 249 expectErr: true, 250 }, 251 { 252 // Aggregation type is allowed through the default list of custom aggregation types. 253 opts: testValidatorOptions().SetDefaultAllowedFirstLevelAggregationTypes(testAggregationTypes), 254 expectErr: false, 255 }, 256 { 257 // Aggregation type is allowed through the list of custom aggregation types for timers. 258 opts: testValidatorOptions().SetAllowedFirstLevelAggregationTypesFor(metric.TimerType, testAggregationTypes), 259 expectErr: false, 260 }, 261 } 262 263 for _, input := range inputs { 264 validator := NewValidator(input.opts) 265 if input.expectErr { 266 require.Error(t, validator.ValidateSnapshot(view)) 267 } else { 268 require.NoError(t, validator.ValidateSnapshot(view)) 269 } 270 } 271 } 272 273 func TestValidatorValidateMappingRuleNoStoragePolicies(t *testing.T) { 274 view := view.RuleSet{ 275 MappingRules: []view.MappingRule{ 276 { 277 Name: "snapshot1", 278 Filter: testTypeTag + ":" + testCounterType, 279 }, 280 }, 281 } 282 283 validator := NewValidator(testValidatorOptions()) 284 err := validator.ValidateSnapshot(view) 285 require.Error(t, err) 286 require.True(t, strings.Contains(err.Error(), "no storage policies")) 287 } 288 289 func TestValidatorValidateMappingRuleDuplicateStoragePolicies(t *testing.T) { 290 view := view.RuleSet{ 291 MappingRules: []view.MappingRule{ 292 { 293 Name: "snapshot1", 294 Filter: testTypeTag + ":" + testCounterType, 295 StoragePolicies: policy.StoragePolicies{ 296 policy.MustParseStoragePolicy("10s:6h"), 297 policy.MustParseStoragePolicy("10s:6h"), 298 }, 299 }, 300 }, 301 } 302 303 validator := NewValidator(testValidatorOptions()) 304 err := validator.ValidateSnapshot(view) 305 require.Error(t, err) 306 require.True(t, strings.Contains(err.Error(), "duplicate storage policy '10s:6h'")) 307 } 308 309 func TestValidatorValidateMappingRuleDisallowedStoragePolicies(t *testing.T) { 310 view := view.RuleSet{ 311 MappingRules: []view.MappingRule{ 312 { 313 Name: "snapshot1", 314 Filter: testTypeTag + ":" + testCounterType, 315 StoragePolicies: policy.StoragePolicies{ 316 policy.MustParseStoragePolicy("1s:6h"), 317 }, 318 }, 319 }, 320 } 321 322 validator := NewValidator(testValidatorOptions()) 323 require.Error(t, validator.ValidateSnapshot(view)) 324 } 325 326 func TestValidatorValidateMappingRule(t *testing.T) { 327 view := view.RuleSet{ 328 MappingRules: []view.MappingRule{ 329 { 330 Name: "snapshot1", 331 Filter: testTypeTag + ":" + testCounterType, 332 StoragePolicies: testStoragePolicies(), 333 }, 334 }, 335 } 336 337 validator := NewValidator(testValidatorOptions()) 338 require.NoError(t, validator.ValidateSnapshot(view)) 339 } 340 341 func TestValidatorValidateDuplicateRollupRules(t *testing.T) { 342 view := view.RuleSet{ 343 RollupRules: []view.RollupRule{ 344 { 345 Name: "snapshot1", 346 Filter: "tag1:value1", 347 }, 348 { 349 Name: "snapshot1", 350 Filter: "tag1:value1", 351 }, 352 }, 353 } 354 validator := NewValidator(testValidatorOptions()) 355 err := validator.ValidateSnapshot(view) 356 require.Error(t, err) 357 _, ok := err.(errors.InvalidInputError) 358 require.True(t, ok) 359 } 360 361 func TestValidatorValidateNoDuplicateRollupRulesWithTombstone(t *testing.T) { 362 rr1, err := pipeline.NewRollupOp( 363 pipeline.GroupByRollupType, 364 "rName1", 365 []string{"rtagName1", "rtagName2"}, 366 aggregation.DefaultID, 367 ) 368 require.NoError(t, err) 369 rr2, err := pipeline.NewRollupOp( 370 pipeline.GroupByRollupType, 371 "rName1", 372 []string{"rtagName1", "rtagName2"}, 373 aggregation.DefaultID, 374 ) 375 require.NoError(t, err) 376 377 view := view.RuleSet{ 378 RollupRules: []view.RollupRule{ 379 { 380 Name: "snapshot1", 381 Filter: "tag1:value1", 382 Tombstoned: true, 383 Targets: []view.RollupTarget{ 384 { 385 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 386 { 387 Type: pipeline.RollupOpType, 388 Rollup: rr1, 389 }, 390 }), 391 StoragePolicies: testStoragePolicies(), 392 }, 393 }, 394 }, 395 { 396 Name: "snapshot1", 397 Filter: "tag1:value1", 398 Targets: []view.RollupTarget{ 399 { 400 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 401 { 402 Type: pipeline.RollupOpType, 403 Rollup: rr2, 404 }, 405 }), 406 StoragePolicies: testStoragePolicies(), 407 }, 408 }, 409 }, 410 }, 411 } 412 413 validator := NewValidator(testValidatorOptions()) 414 require.NoError(t, validator.ValidateSnapshot(view)) 415 } 416 417 func TestValidatorValidateRollupRuleInvalidFilterExpr(t *testing.T) { 418 rr1, err := pipeline.NewRollupOp( 419 pipeline.GroupByRollupType, 420 "rName1", 421 []string{"rtagName1", "rtagName2"}, 422 aggregation.DefaultID, 423 ) 424 require.NoError(t, err) 425 426 view := view.RuleSet{ 427 RollupRules: []view.RollupRule{ 428 { 429 Name: "snapshot1", 430 Filter: "randomTag:*too*many*wildcards*", 431 Targets: []view.RollupTarget{ 432 { 433 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 434 { 435 Type: pipeline.RollupOpType, 436 Rollup: rr1, 437 }, 438 }), 439 StoragePolicies: testStoragePolicies(), 440 }, 441 }, 442 }, 443 }, 444 } 445 446 validator := NewValidator(testValidatorOptions()) 447 require.Error(t, validator.ValidateSnapshot(view)) 448 } 449 450 func TestValidatorValidateRollupRuleInvalidFilterTagName(t *testing.T) { 451 rr1, err := pipeline.NewRollupOp( 452 pipeline.GroupByRollupType, 453 "rName1", 454 []string{"rtagName1", "rtagName2"}, 455 aggregation.DefaultID, 456 ) 457 require.NoError(t, err) 458 459 invalidChars := []rune{'$'} 460 view := view.RuleSet{ 461 RollupRules: []view.RollupRule{ 462 { 463 Name: "snapshot1", 464 Filter: "random$Tag:foo", 465 Targets: []view.RollupTarget{ 466 { 467 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 468 { 469 Type: pipeline.RollupOpType, 470 Rollup: rr1, 471 }, 472 }), 473 StoragePolicies: testStoragePolicies(), 474 }, 475 }, 476 }, 477 }, 478 } 479 validator := NewValidator(testValidatorOptions().SetTagNameInvalidChars(invalidChars)) 480 require.Error(t, validator.ValidateSnapshot(view)) 481 } 482 483 func TestValidatorValidateRollupRuleInvalidMetricType(t *testing.T) { 484 rr1, err := pipeline.NewRollupOp( 485 pipeline.GroupByRollupType, 486 "rName1", 487 []string{"rtagName1", "rtagName2"}, 488 aggregation.DefaultID, 489 ) 490 require.NoError(t, err) 491 492 view := view.RuleSet{ 493 RollupRules: []view.RollupRule{ 494 { 495 Name: "snapshot1", 496 Filter: testTypeTag + ":nonexistent", 497 Targets: []view.RollupTarget{ 498 { 499 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 500 { 501 Type: pipeline.RollupOpType, 502 Rollup: rr1, 503 }, 504 }), 505 StoragePolicies: testStoragePolicies(), 506 }, 507 }, 508 }, 509 }, 510 } 511 validator := NewValidator(testValidatorOptions()) 512 require.Error(t, validator.ValidateSnapshot(view)) 513 } 514 515 func TestValidatorValidateRollupRulePipelineEmptyPipeline(t *testing.T) { 516 view := view.RuleSet{ 517 RollupRules: []view.RollupRule{ 518 { 519 Name: "snapshot1", 520 Filter: testTypeTag + ":" + testCounterType, 521 Targets: []view.RollupTarget{ 522 { 523 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{}), 524 StoragePolicies: testStoragePolicies(), 525 }, 526 }, 527 }, 528 }, 529 } 530 validator := NewValidator(testValidatorOptions()) 531 err := validator.ValidateSnapshot(view) 532 require.Error(t, err) 533 require.True(t, strings.Contains(err.Error(), "empty pipeline")) 534 } 535 536 func TestValidatorValidateRollupRulePipelineInvalidPipelineOp(t *testing.T) { 537 view := view.RuleSet{ 538 RollupRules: []view.RollupRule{ 539 { 540 Name: "snapshot1", 541 Filter: testTypeTag + ":" + testCounterType, 542 Targets: []view.RollupTarget{ 543 { 544 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 545 {}, 546 }), 547 StoragePolicies: testStoragePolicies(), 548 }, 549 }, 550 }, 551 }, 552 } 553 validator := NewValidator(testValidatorOptions()) 554 err := validator.ValidateSnapshot(view) 555 require.Error(t, err) 556 require.True(t, strings.Contains(err.Error(), "operation at index 0 has invalid type")) 557 } 558 559 func TestValidatorValidateRollupRulePipelineMultipleAggregationOps(t *testing.T) { 560 view := view.RuleSet{ 561 RollupRules: []view.RollupRule{ 562 { 563 Name: "snapshot1", 564 Filter: testTypeTag + ":" + testCounterType, 565 Targets: []view.RollupTarget{ 566 { 567 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 568 { 569 Type: pipeline.AggregationOpType, 570 Aggregation: pipeline.AggregationOp{Type: aggregation.Sum}, 571 }, 572 { 573 Type: pipeline.AggregationOpType, 574 Aggregation: pipeline.AggregationOp{Type: aggregation.Sum}, 575 }, 576 }), 577 StoragePolicies: testStoragePolicies(), 578 }, 579 }, 580 }, 581 }, 582 } 583 allowedAggregationTypes := aggregation.Types{aggregation.Sum} 584 opts := testValidatorOptions(). 585 SetAllowedFirstLevelAggregationTypesFor(metric.CounterType, allowedAggregationTypes) 586 validator := NewValidator(opts) 587 err := validator.ValidateSnapshot(view) 588 require.Error(t, err) 589 require.True(t, strings.Contains(err.Error(), "more than one aggregation operation in pipeline")) 590 } 591 592 func TestValidatorValidateRollupRulePipelineAggregationOpNotFirst(t *testing.T) { 593 view := view.RuleSet{ 594 RollupRules: []view.RollupRule{ 595 { 596 Name: "snapshot1", 597 Filter: testTypeTag + ":" + testCounterType, 598 Targets: []view.RollupTarget{ 599 { 600 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 601 { 602 Type: pipeline.TransformationOpType, 603 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 604 }, 605 { 606 Type: pipeline.AggregationOpType, 607 Aggregation: pipeline.AggregationOp{Type: aggregation.Sum}, 608 }, 609 }), 610 StoragePolicies: testStoragePolicies(), 611 }, 612 }, 613 }, 614 }, 615 } 616 allowedAggregationTypes := aggregation.Types{aggregation.Sum} 617 opts := testValidatorOptions(). 618 SetAllowedFirstLevelAggregationTypesFor(metric.CounterType, allowedAggregationTypes) 619 validator := NewValidator(opts) 620 err := validator.ValidateSnapshot(view) 621 require.Error(t, err) 622 require.True(t, strings.Contains(err.Error(), "aggregation operation is not the first operation in pipeline")) 623 } 624 625 func TestValidatorValidateRollupRulePipelineAggregationOpInvalidAggregationType(t *testing.T) { 626 view := view.RuleSet{ 627 RollupRules: []view.RollupRule{ 628 { 629 Name: "snapshot1", 630 Filter: testTypeTag + ":" + testCounterType, 631 Targets: []view.RollupTarget{ 632 { 633 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 634 { 635 Type: pipeline.AggregationOpType, 636 Aggregation: pipeline.AggregationOp{}, 637 }, 638 }), 639 StoragePolicies: testStoragePolicies(), 640 }, 641 }, 642 }, 643 }, 644 } 645 allowedAggregationTypes := aggregation.Types{aggregation.Sum} 646 opts := testValidatorOptions(). 647 SetAllowedFirstLevelAggregationTypesFor(metric.CounterType, allowedAggregationTypes) 648 validator := NewValidator(opts) 649 err := validator.ValidateSnapshot(view) 650 require.Error(t, err) 651 require.True(t, strings.Contains(err.Error(), "invalid aggregation operation at index 0")) 652 } 653 654 func TestValidatorValidateRollupRulePipelineAggregationOpDisallowedAggregationType(t *testing.T) { 655 view := view.RuleSet{ 656 RollupRules: []view.RollupRule{ 657 { 658 Name: "snapshot1", 659 Filter: testTypeTag + ":" + testCounterType, 660 Targets: []view.RollupTarget{ 661 { 662 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 663 { 664 Type: pipeline.AggregationOpType, 665 Aggregation: pipeline.AggregationOp{Type: aggregation.Sum}, 666 }, 667 }), 668 StoragePolicies: testStoragePolicies(), 669 }, 670 }, 671 }, 672 }, 673 } 674 validator := NewValidator(testValidatorOptions()) 675 err := validator.ValidateSnapshot(view) 676 require.Error(t, err) 677 require.True(t, strings.Contains(err.Error(), "invalid aggregation operation at index 0")) 678 } 679 680 func TestValidatorValidateRollupRulePipelineTransformationDerivativeOrderNotSupported(t *testing.T) { 681 rr1, err := pipeline.NewRollupOp( 682 pipeline.GroupByRollupType, 683 "rName1", 684 []string{"rtagName1", "rtagName2"}, 685 aggregation.DefaultID, 686 ) 687 require.NoError(t, err) 688 689 view := view.RuleSet{ 690 RollupRules: []view.RollupRule{ 691 { 692 Name: "snapshot1", 693 Filter: testTypeTag + ":" + testCounterType, 694 Targets: []view.RollupTarget{ 695 { 696 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 697 { 698 Type: pipeline.RollupOpType, 699 Rollup: rr1, 700 }, 701 { 702 Type: pipeline.TransformationOpType, 703 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 704 }, 705 { 706 Type: pipeline.TransformationOpType, 707 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 708 }, 709 }), 710 StoragePolicies: testStoragePolicies(), 711 }, 712 }, 713 }, 714 }, 715 } 716 validator := NewValidator(testValidatorOptions()) 717 err = validator.ValidateSnapshot(view) 718 require.Error(t, err) 719 require.True(t, strings.Contains(err.Error(), "transformation derivative order is 2 higher than supported 1")) 720 } 721 722 func TestValidatorValidateRollupRulePipelineInvalidTransformationType(t *testing.T) { 723 view := view.RuleSet{ 724 RollupRules: []view.RollupRule{ 725 { 726 Name: "snapshot1", 727 Filter: testTypeTag + ":" + testCounterType, 728 Targets: []view.RollupTarget{ 729 { 730 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 731 { 732 Type: pipeline.TransformationOpType, 733 Transformation: pipeline.TransformationOp{}, 734 }, 735 }), 736 StoragePolicies: testStoragePolicies(), 737 }, 738 }, 739 }, 740 }, 741 } 742 validator := NewValidator(testValidatorOptions()) 743 err := validator.ValidateSnapshot(view) 744 require.Error(t, err) 745 require.True(t, strings.Contains(err.Error(), "invalid transformation operation at index 0")) 746 } 747 748 func TestValidatorValidateRollupRulePipelineNoRollupOp(t *testing.T) { 749 view := view.RuleSet{ 750 RollupRules: []view.RollupRule{ 751 { 752 Name: "snapshot1", 753 Filter: testTypeTag + ":" + testCounterType, 754 Targets: []view.RollupTarget{ 755 { 756 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 757 { 758 Type: pipeline.TransformationOpType, 759 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 760 }, 761 }), 762 StoragePolicies: testStoragePolicies(), 763 }, 764 }, 765 }, 766 }, 767 } 768 validator := NewValidator(testValidatorOptions()) 769 err := validator.ValidateSnapshot(view) 770 require.Error(t, err) 771 require.True(t, strings.Contains(err.Error(), "no rollup operation in pipeline")) 772 } 773 774 func TestValidatorValidateRollupRulePipelineRollupLevelHigherThanMax(t *testing.T) { 775 rr1, err := pipeline.NewRollupOp( 776 pipeline.GroupByRollupType, 777 "rName1", 778 []string{"rtagName1", "rtagName2"}, 779 aggregation.DefaultID, 780 ) 781 require.NoError(t, err) 782 rr2, err := pipeline.NewRollupOp( 783 pipeline.GroupByRollupType, 784 "rName2", 785 []string{"rtagName1", "rtagName2"}, 786 aggregation.DefaultID, 787 ) 788 require.NoError(t, err) 789 790 view := view.RuleSet{ 791 RollupRules: []view.RollupRule{ 792 { 793 Name: "snapshot1", 794 Filter: testTypeTag + ":" + testCounterType, 795 Targets: []view.RollupTarget{ 796 { 797 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 798 { 799 Type: pipeline.RollupOpType, 800 Rollup: rr1, 801 }, 802 { 803 Type: pipeline.RollupOpType, 804 Rollup: rr2, 805 }, 806 }), 807 StoragePolicies: testStoragePolicies(), 808 }, 809 }, 810 }, 811 }, 812 } 813 validator := NewValidator(testValidatorOptions()) 814 err = validator.ValidateSnapshot(view) 815 require.Error(t, err) 816 require.True(t, strings.Contains(err.Error(), "number of rollup levels is 2 higher than supported 1")) 817 } 818 819 func TestValidatorValidateRollupRulePipelineRollupTagNotFoundInPrevRollupOp(t *testing.T) { 820 rr1, err := pipeline.NewRollupOp( 821 pipeline.GroupByRollupType, 822 "rName1", 823 []string{"rtagName1", "rtagName2"}, 824 aggregation.DefaultID, 825 ) 826 require.NoError(t, err) 827 rr2, err := pipeline.NewRollupOp( 828 pipeline.GroupByRollupType, 829 "rName2", 830 []string{"rtagName1", "rtagName2", "rtagName3"}, 831 aggregation.DefaultID, 832 ) 833 require.NoError(t, err) 834 835 view := view.RuleSet{ 836 RollupRules: []view.RollupRule{ 837 { 838 Name: "snapshot1", 839 Filter: testTypeTag + ":" + testCounterType, 840 Targets: []view.RollupTarget{ 841 { 842 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 843 { 844 Type: pipeline.RollupOpType, 845 Rollup: rr1, 846 }, 847 { 848 Type: pipeline.RollupOpType, 849 Rollup: rr2, 850 }, 851 }), 852 StoragePolicies: testStoragePolicies(), 853 }, 854 }, 855 }, 856 }, 857 } 858 validator := NewValidator(testValidatorOptions().SetMaxRollupLevels(100)) 859 err = validator.ValidateSnapshot(view) 860 require.Error(t, err) 861 require.True(t, strings.Contains(err.Error(), "tag rtagName3 not found in previous rollup operations")) 862 } 863 864 func TestValidatorValidateRollupRulePipelineRollupTagUnchangedInConsecutiveRollupOps(t *testing.T) { 865 rr1, err := pipeline.NewRollupOp( 866 pipeline.GroupByRollupType, 867 "rName1", 868 []string{"rtagName1", "rtagName2"}, 869 aggregation.DefaultID, 870 ) 871 require.NoError(t, err) 872 rr2, err := pipeline.NewRollupOp( 873 pipeline.GroupByRollupType, 874 "rName2", 875 []string{"rtagName1", "rtagName2"}, 876 aggregation.DefaultID, 877 ) 878 require.NoError(t, err) 879 view := view.RuleSet{ 880 RollupRules: []view.RollupRule{ 881 { 882 Name: "snapshot1", 883 Filter: testTypeTag + ":" + testCounterType, 884 Targets: []view.RollupTarget{ 885 { 886 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 887 { 888 Type: pipeline.RollupOpType, 889 Rollup: rr1, 890 }, 891 { 892 Type: pipeline.RollupOpType, 893 Rollup: rr2, 894 }, 895 }), 896 StoragePolicies: testStoragePolicies(), 897 }, 898 }, 899 }, 900 }, 901 } 902 validator := NewValidator(testValidatorOptions().SetMaxRollupLevels(100)) 903 err = validator.ValidateSnapshot(view) 904 require.Error(t, err) 905 require.True(t, strings.Contains(err.Error(), "same set of 2 rollup tags in consecutive rollup operations")) 906 } 907 908 func TestValidatorValidateRollupRulePipelineMultiLevelRollup(t *testing.T) { 909 rr1, err := pipeline.NewRollupOp( 910 pipeline.GroupByRollupType, 911 "rName1", 912 []string{"rtagName1", "rtagName2", "rtagName3"}, 913 aggregation.DefaultID, 914 ) 915 require.NoError(t, err) 916 rr2, err := pipeline.NewRollupOp( 917 pipeline.GroupByRollupType, 918 "rName2", 919 []string{"rtagName1", "rtagName2"}, 920 aggregation.DefaultID, 921 ) 922 require.NoError(t, err) 923 rr3, err := pipeline.NewRollupOp( 924 pipeline.GroupByRollupType, 925 "rName2", 926 []string{"rtagName1"}, 927 aggregation.DefaultID, 928 ) 929 require.NoError(t, err) 930 931 view := view.RuleSet{ 932 RollupRules: []view.RollupRule{ 933 { 934 Name: "snapshot1", 935 Filter: testTypeTag + ":" + testCounterType, 936 Targets: []view.RollupTarget{ 937 { 938 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 939 { 940 Type: pipeline.RollupOpType, 941 Rollup: rr1, 942 }, 943 { 944 Type: pipeline.RollupOpType, 945 Rollup: rr2, 946 }, 947 { 948 Type: pipeline.RollupOpType, 949 Rollup: rr3, 950 }, 951 }), 952 StoragePolicies: testStoragePolicies(), 953 }, 954 }, 955 }, 956 }, 957 } 958 validator := NewValidator(testValidatorOptions().SetMaxRollupLevels(3)) 959 require.NoError(t, validator.ValidateSnapshot(view)) 960 } 961 962 func TestValidatorValidateRollupRuleRollupOpDuplicateRollupTag(t *testing.T) { 963 rr1, err := pipeline.NewRollupOp( 964 pipeline.GroupByRollupType, 965 "rName1", 966 []string{"rtagName1", "rtagName2", "rtagName2"}, 967 aggregation.DefaultID, 968 ) 969 require.NoError(t, err) 970 971 view := view.RuleSet{ 972 RollupRules: []view.RollupRule{ 973 { 974 Name: "snapshot1", 975 Filter: testTypeTag + ":" + testCounterType, 976 Targets: []view.RollupTarget{ 977 { 978 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 979 { 980 Type: pipeline.RollupOpType, 981 Rollup: rr1, 982 }, 983 }), 984 StoragePolicies: testStoragePolicies(), 985 }, 986 }, 987 }, 988 }, 989 } 990 validator := NewValidator(testValidatorOptions()) 991 err = validator.ValidateSnapshot(view) 992 require.Error(t, err) 993 require.True(t, strings.Contains(err.Error(), "duplicate rollup tag: 'rtagName2'")) 994 } 995 996 func TestValidatorValidateRollupRuleRollupOpMissingRequiredTag(t *testing.T) { 997 rr1, err := pipeline.NewRollupOp( 998 pipeline.GroupByRollupType, 999 "rName1", 1000 []string{"rtagName1", "rtagName2"}, 1001 aggregation.DefaultID, 1002 ) 1003 require.NoError(t, err) 1004 1005 view := view.RuleSet{ 1006 RollupRules: []view.RollupRule{ 1007 { 1008 Name: "snapshot1", 1009 Filter: testTypeTag + ":" + testCounterType, 1010 Targets: []view.RollupTarget{ 1011 { 1012 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1013 { 1014 Type: pipeline.RollupOpType, 1015 Rollup: rr1, 1016 }, 1017 }), 1018 StoragePolicies: testStoragePolicies(), 1019 }, 1020 }, 1021 }, 1022 }, 1023 } 1024 validator := NewValidator(testValidatorOptions().SetRequiredRollupTags([]string{"requiredTag"})) 1025 err = validator.ValidateSnapshot(view) 1026 require.Error(t, err) 1027 require.True(t, strings.Contains(err.Error(), "missing required rollup tag: 'requiredTag'")) 1028 } 1029 1030 func TestValidatorValidateRollupRuleRollupOpWithInvalidMetricName(t *testing.T) { 1031 rr1, err := pipeline.NewRollupOp( 1032 pipeline.GroupByRollupType, 1033 "rName$1", 1034 []string{"rtagName1", "rtagName2"}, 1035 aggregation.DefaultID, 1036 ) 1037 require.NoError(t, err) 1038 invalidChars := []rune{'$'} 1039 view := view.RuleSet{ 1040 RollupRules: []view.RollupRule{ 1041 { 1042 Name: "snapshot1", 1043 Filter: testTypeTag + ":" + testCounterType, 1044 Targets: []view.RollupTarget{ 1045 { 1046 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1047 { 1048 Type: pipeline.RollupOpType, 1049 Rollup: rr1, 1050 }, 1051 }), 1052 StoragePolicies: testStoragePolicies(), 1053 }, 1054 }, 1055 }, 1056 }, 1057 } 1058 1059 validator := NewValidator(testValidatorOptions().SetMetricNameInvalidChars(invalidChars)) 1060 require.Error(t, validator.ValidateSnapshot(view)) 1061 } 1062 1063 func TestValidatorValidateRollupRuleRollupOpWithEmptyMetricName(t *testing.T) { 1064 rr1, err := pipeline.NewRollupOp( 1065 pipeline.GroupByRollupType, 1066 "", 1067 []string{"rtagName1", "rtagName2"}, 1068 aggregation.DefaultID, 1069 ) 1070 require.NoError(t, err) 1071 invalidChars := []rune{'$'} 1072 view := view.RuleSet{ 1073 RollupRules: []view.RollupRule{ 1074 { 1075 Name: "snapshot1", 1076 Filter: testTypeTag + ":" + testCounterType, 1077 Targets: []view.RollupTarget{ 1078 { 1079 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1080 { 1081 Type: pipeline.RollupOpType, 1082 Rollup: rr1, 1083 }, 1084 }), 1085 StoragePolicies: testStoragePolicies(), 1086 }, 1087 }, 1088 }, 1089 }, 1090 } 1091 1092 validator := NewValidator(testValidatorOptions().SetMetricNameInvalidChars(invalidChars)) 1093 require.Error(t, validator.ValidateSnapshot(view)) 1094 } 1095 1096 func TestValidatorValidateRollupRuleRollupOpWithValidMetricName(t *testing.T) { 1097 rr1, err := pipeline.NewRollupOp( 1098 pipeline.GroupByRollupType, 1099 "", 1100 []string{"rtagName1", "rtagName2"}, 1101 aggregation.DefaultID, 1102 ) 1103 require.NoError(t, err) 1104 invalidChars := []rune{' ', '%'} 1105 view := view.RuleSet{ 1106 RollupRules: []view.RollupRule{ 1107 { 1108 Name: "snapshot1", 1109 Filter: testTypeTag + ":" + testCounterType, 1110 Targets: []view.RollupTarget{ 1111 { 1112 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1113 { 1114 Type: pipeline.RollupOpType, 1115 Rollup: rr1, 1116 }, 1117 }), 1118 StoragePolicies: testStoragePolicies(), 1119 }, 1120 }, 1121 }, 1122 }, 1123 } 1124 1125 validator := NewValidator(testValidatorOptions().SetMetricNameInvalidChars(invalidChars)) 1126 require.Error(t, validator.ValidateSnapshot(view)) 1127 } 1128 1129 func TestValidatorValidateRollupRuleRollupOpWithInvalidTagName(t *testing.T) { 1130 rr1, err := pipeline.NewRollupOp( 1131 pipeline.GroupByRollupType, 1132 "foo", 1133 []string{"rtagName1", "rtagName2", "$"}, 1134 aggregation.DefaultID, 1135 ) 1136 require.NoError(t, err) 1137 invalidChars := []rune{'$'} 1138 view := view.RuleSet{ 1139 RollupRules: []view.RollupRule{ 1140 { 1141 Name: "snapshot1", 1142 Filter: testTypeTag + ":" + testCounterType, 1143 Targets: []view.RollupTarget{ 1144 { 1145 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1146 { 1147 Type: pipeline.RollupOpType, 1148 Rollup: rr1, 1149 }, 1150 }), 1151 StoragePolicies: testStoragePolicies(), 1152 }, 1153 }, 1154 }, 1155 }, 1156 } 1157 1158 validator := NewValidator(testValidatorOptions().SetTagNameInvalidChars(invalidChars)) 1159 require.Error(t, validator.ValidateSnapshot(view)) 1160 } 1161 1162 func TestValidatorValidateRollupRuleRollupOpWithValidTagName(t *testing.T) { 1163 rr1, err := pipeline.NewRollupOp( 1164 pipeline.GroupByRollupType, 1165 "foo", 1166 []string{"rtagName1", "rtagName2", "$"}, 1167 aggregation.DefaultID, 1168 ) 1169 require.NoError(t, err) 1170 invalidChars := []rune{' ', '%'} 1171 view := view.RuleSet{ 1172 RollupRules: []view.RollupRule{ 1173 { 1174 Name: "snapshot1", 1175 Filter: testTypeTag + ":" + testCounterType, 1176 Targets: []view.RollupTarget{ 1177 { 1178 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1179 { 1180 Type: pipeline.RollupOpType, 1181 Rollup: rr1, 1182 }, 1183 }), 1184 StoragePolicies: testStoragePolicies(), 1185 }, 1186 }, 1187 }, 1188 }, 1189 } 1190 1191 validator := NewValidator(testValidatorOptions().SetMetricNameInvalidChars(invalidChars)) 1192 require.NoError(t, validator.ValidateSnapshot(view)) 1193 } 1194 1195 func TestValidatorValidateNoTimertypeFilter(t *testing.T) { 1196 for _, test := range []string{ 1197 "rollup", 1198 "mapping", 1199 } { 1200 t.Run(test, func(t *testing.T) { 1201 ruleView := view.RuleSet{} 1202 if test == "rollup" { 1203 ruleView.RollupRules = []view.RollupRule{ 1204 { 1205 Name: "foo", 1206 Filter: "service:bar timertype:count", 1207 }, 1208 } 1209 } else { 1210 ruleView.MappingRules = []view.MappingRule{ 1211 { 1212 Name: "foo", 1213 Filter: "service:bar timertype:count", 1214 DropPolicy: policy.DropMust, 1215 }, 1216 } 1217 } 1218 1219 validator := NewValidator(testValidatorOptions().SetFilterInvalidTagNames([]string{"timertype"})) 1220 assert.Error(t, validator.ValidateSnapshot(ruleView)) 1221 1222 if test == "rollup" { 1223 ruleView.RollupRules = ruleView.RollupRules[:0] 1224 } else { 1225 ruleView.MappingRules = ruleView.MappingRules[:0] 1226 } 1227 1228 assert.NoError(t, validator.ValidateSnapshot(ruleView)) 1229 }) 1230 } 1231 } 1232 1233 func TestValidatorValidateRollupRuleRollupOpMultipleAggregationTypes(t *testing.T) { 1234 rr1, err := pipeline.NewRollupOp( 1235 pipeline.GroupByRollupType, 1236 "rName1", 1237 []string{"rtagName1", "rtagName2"}, 1238 aggregation.MustCompressTypes(aggregation.Count, aggregation.Max), 1239 ) 1240 require.NoError(t, err) 1241 1242 testAggregationTypes := []aggregation.Type{aggregation.Count, aggregation.Max} 1243 view := view.RuleSet{ 1244 RollupRules: []view.RollupRule{ 1245 { 1246 Name: "snapshot1", 1247 Filter: testTypeTag + ":" + testTimerType, 1248 Targets: []view.RollupTarget{ 1249 { 1250 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1251 { 1252 Type: pipeline.RollupOpType, 1253 Rollup: rr1, 1254 }, 1255 }), 1256 StoragePolicies: policy.StoragePolicies{ 1257 policy.MustParseStoragePolicy("10s:6h"), 1258 }, 1259 }, 1260 }, 1261 }, 1262 }, 1263 } 1264 inputs := []struct { 1265 opts Options 1266 expectErr bool 1267 }{ 1268 { 1269 // By default multiple aggregation types are allowed for timers. 1270 opts: testValidatorOptions().SetDefaultAllowedFirstLevelAggregationTypes(testAggregationTypes), 1271 expectErr: false, 1272 }, 1273 { 1274 // Explicitly disallow multiple aggregation types for timers. 1275 opts: testValidatorOptions().SetDefaultAllowedFirstLevelAggregationTypes(testAggregationTypes).SetMultiAggregationTypesEnabledFor(nil), 1276 expectErr: true, 1277 }, 1278 } 1279 1280 for _, input := range inputs { 1281 validator := NewValidator(input.opts) 1282 if input.expectErr { 1283 require.Error(t, validator.ValidateSnapshot(view)) 1284 } else { 1285 require.NoError(t, validator.ValidateSnapshot(view)) 1286 } 1287 } 1288 } 1289 1290 func TestValidatorValidateRollupRuleRollupOpFirstLevelAggregationTypes(t *testing.T) { 1291 rr1, err := pipeline.NewRollupOp( 1292 pipeline.GroupByRollupType, 1293 "rName1", 1294 []string{"rtagName1", "rtagName2"}, 1295 aggregation.MustCompressTypes(aggregation.Count, aggregation.Max), 1296 ) 1297 require.NoError(t, err) 1298 testAggregationTypes := []aggregation.Type{aggregation.Count, aggregation.Max} 1299 view := view.RuleSet{ 1300 RollupRules: []view.RollupRule{ 1301 { 1302 Name: "snapshot1", 1303 Filter: testTypeTag + ":" + testTimerType, 1304 Targets: []view.RollupTarget{ 1305 { 1306 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1307 { 1308 Type: pipeline.RollupOpType, 1309 Rollup: rr1, 1310 }, 1311 }), 1312 StoragePolicies: policy.StoragePolicies{ 1313 policy.MustParseStoragePolicy("10s:6h"), 1314 }, 1315 }, 1316 }, 1317 }, 1318 }, 1319 } 1320 inputs := []struct { 1321 opts Options 1322 expectErr bool 1323 }{ 1324 { 1325 // By default no custom aggregation type is allowed. 1326 opts: testValidatorOptions(), 1327 expectErr: true, 1328 }, 1329 { 1330 // Aggregation type is allowed through the default list of custom aggregation types. 1331 opts: testValidatorOptions().SetDefaultAllowedFirstLevelAggregationTypes(testAggregationTypes), 1332 expectErr: false, 1333 }, 1334 { 1335 // Aggregation type is allowed through the list of custom aggregation types for timers. 1336 opts: testValidatorOptions().SetAllowedFirstLevelAggregationTypesFor(metric.TimerType, testAggregationTypes), 1337 expectErr: false, 1338 }, 1339 } 1340 1341 for _, input := range inputs { 1342 validator := NewValidator(input.opts) 1343 if input.expectErr { 1344 require.Error(t, validator.ValidateSnapshot(view)) 1345 } else { 1346 require.NoError(t, validator.ValidateSnapshot(view)) 1347 } 1348 } 1349 } 1350 1351 func TestValidatorValidateRollupRuleRollupOpNonFirstLevelAggregationTypes(t *testing.T) { 1352 rr1, err := pipeline.NewRollupOp( 1353 pipeline.GroupByRollupType, 1354 "rName1", 1355 []string{"rtagName1", "rtagName2"}, 1356 aggregation.MustCompressTypes(aggregation.Count, aggregation.Max), 1357 ) 1358 require.NoError(t, err) 1359 testAggregationTypes := []aggregation.Type{aggregation.Count, aggregation.Max} 1360 view := view.RuleSet{ 1361 RollupRules: []view.RollupRule{ 1362 { 1363 Name: "snapshot1", 1364 Filter: testTypeTag + ":" + testTimerType, 1365 Targets: []view.RollupTarget{ 1366 { 1367 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1368 { 1369 Type: pipeline.TransformationOpType, 1370 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 1371 }, 1372 { 1373 Type: pipeline.RollupOpType, 1374 Rollup: rr1, 1375 }, 1376 }), 1377 StoragePolicies: policy.StoragePolicies{ 1378 policy.MustParseStoragePolicy("10s:6h"), 1379 }, 1380 }, 1381 }, 1382 }, 1383 }, 1384 } 1385 inputs := []struct { 1386 opts Options 1387 expectErr bool 1388 }{ 1389 { 1390 // By default no custom aggregation type is allowed. 1391 opts: testValidatorOptions(), 1392 expectErr: true, 1393 }, 1394 { 1395 // Aggregation type is allowed through the default list of custom aggregation types. 1396 opts: testValidatorOptions().SetDefaultAllowedNonFirstLevelAggregationTypes(testAggregationTypes), 1397 expectErr: false, 1398 }, 1399 { 1400 // Aggregation type is allowed through the list of non-first-level aggregation types for timers. 1401 opts: testValidatorOptions().SetAllowedNonFirstLevelAggregationTypesFor(metric.TimerType, testAggregationTypes), 1402 expectErr: false, 1403 }, 1404 } 1405 1406 for _, input := range inputs { 1407 validator := NewValidator(input.opts) 1408 if input.expectErr { 1409 require.Error(t, validator.ValidateSnapshot(view)) 1410 } else { 1411 require.NoError(t, validator.ValidateSnapshot(view)) 1412 } 1413 } 1414 } 1415 1416 func TestValidatorValidateRollupRuleRollupTargetWithStoragePolicies(t *testing.T) { 1417 rr1, err := pipeline.NewRollupOp( 1418 pipeline.GroupByRollupType, 1419 "rName1", 1420 []string{"rtagName1", "rtagName2"}, 1421 aggregation.DefaultID, 1422 ) 1423 require.NoError(t, err) 1424 storagePolicies := testStoragePolicies() 1425 view := view.RuleSet{ 1426 RollupRules: []view.RollupRule{ 1427 { 1428 Name: "snapshot1", 1429 Filter: testTypeTag + ":" + testTimerType, 1430 Targets: []view.RollupTarget{ 1431 { 1432 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1433 { 1434 Type: pipeline.RollupOpType, 1435 Rollup: rr1, 1436 }, 1437 }), 1438 StoragePolicies: storagePolicies, 1439 }, 1440 }, 1441 }, 1442 }, 1443 } 1444 1445 inputs := []struct { 1446 opts Options 1447 expectErr bool 1448 }{ 1449 { 1450 // By default policy is allowed. 1451 opts: testValidatorOptions().SetDefaultAllowedStoragePolicies(policy.StoragePolicies{}), 1452 expectErr: true, 1453 }, 1454 { 1455 // Policy is allowed through the default list of policies. 1456 opts: testValidatorOptions().SetDefaultAllowedStoragePolicies(storagePolicies), 1457 expectErr: false, 1458 }, 1459 { 1460 // Policy is allowed through the list of policies allowed for timers. 1461 opts: testValidatorOptions().SetAllowedStoragePoliciesFor(metric.TimerType, storagePolicies), 1462 expectErr: false, 1463 }, 1464 } 1465 1466 for _, input := range inputs { 1467 validator := NewValidator(input.opts) 1468 if input.expectErr { 1469 require.Error(t, validator.ValidateSnapshot(view)) 1470 } else { 1471 require.NoError(t, validator.ValidateSnapshot(view)) 1472 } 1473 } 1474 } 1475 1476 func TestValidatorValidateRollupRuleRollupTargetWithNoStoragePolicies(t *testing.T) { 1477 rr1, err := pipeline.NewRollupOp( 1478 pipeline.GroupByRollupType, 1479 "rName1", 1480 []string{"rtagName1", "rtagName2"}, 1481 aggregation.DefaultID, 1482 ) 1483 require.NoError(t, err) 1484 view := view.RuleSet{ 1485 RollupRules: []view.RollupRule{ 1486 { 1487 Name: "snapshot1", 1488 Filter: testTypeTag + ":" + testTimerType, 1489 Targets: []view.RollupTarget{ 1490 { 1491 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1492 { 1493 Type: pipeline.RollupOpType, 1494 Rollup: rr1, 1495 }, 1496 }), 1497 }, 1498 }, 1499 }, 1500 }, 1501 } 1502 validator := NewValidator(testValidatorOptions()) 1503 err = validator.ValidateSnapshot(view) 1504 require.Error(t, err) 1505 require.True(t, strings.Contains(err.Error(), "no storage policies")) 1506 } 1507 1508 func TestValidatorValidateRollupRuleRollupOpWithDuplicateStoragePolicies(t *testing.T) { 1509 rr1, err := pipeline.NewRollupOp( 1510 pipeline.GroupByRollupType, 1511 "rName1", 1512 []string{"rtagName1", "rtagName2"}, 1513 aggregation.DefaultID, 1514 ) 1515 require.NoError(t, err) 1516 view := view.RuleSet{ 1517 RollupRules: []view.RollupRule{ 1518 { 1519 Name: "snapshot1", 1520 Filter: testTypeTag + ":" + testTimerType, 1521 Targets: []view.RollupTarget{ 1522 { 1523 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1524 { 1525 Type: pipeline.RollupOpType, 1526 Rollup: rr1, 1527 }, 1528 }), 1529 StoragePolicies: policy.StoragePolicies{ 1530 policy.MustParseStoragePolicy("10s:6h"), 1531 policy.MustParseStoragePolicy("10s:6h"), 1532 }, 1533 }, 1534 }, 1535 }, 1536 }, 1537 } 1538 1539 validator := NewValidator(testValidatorOptions()) 1540 err = validator.ValidateSnapshot(view) 1541 require.Error(t, err) 1542 require.True(t, strings.Contains(err.Error(), "duplicate storage policy '10s:6h'")) 1543 } 1544 1545 func TestValidatorValidateRollupRuleDisallowedStoragePolicies(t *testing.T) { 1546 rr1, err := pipeline.NewRollupOp( 1547 pipeline.GroupByRollupType, 1548 "rName1", 1549 []string{"rtagName1", "rtagName2"}, 1550 aggregation.DefaultID, 1551 ) 1552 require.NoError(t, err) 1553 view := view.RuleSet{ 1554 RollupRules: []view.RollupRule{ 1555 { 1556 Name: "snapshot1", 1557 Filter: testTypeTag + ":" + testTimerType, 1558 Targets: []view.RollupTarget{ 1559 { 1560 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1561 { 1562 Type: pipeline.RollupOpType, 1563 Rollup: rr1, 1564 }, 1565 }), 1566 StoragePolicies: policy.StoragePolicies{ 1567 policy.MustParseStoragePolicy("1s:6h"), 1568 }, 1569 }, 1570 }, 1571 }, 1572 }, 1573 } 1574 1575 validator := NewValidator(testValidatorOptions()) 1576 require.Error(t, validator.ValidateSnapshot(view)) 1577 } 1578 1579 func TestValidatorRollupRule(t *testing.T) { 1580 rr1, err := pipeline.NewRollupOp( 1581 pipeline.GroupByRollupType, 1582 "rName1", 1583 []string{"rtagName1", "rtagName2"}, 1584 aggregation.MustCompressTypes(aggregation.Sum), 1585 ) 1586 require.NoError(t, err) 1587 view := view.RuleSet{ 1588 RollupRules: []view.RollupRule{ 1589 { 1590 Name: "snapshot1", 1591 Filter: testTypeTag + ":" + testGaugeType, 1592 Targets: []view.RollupTarget{ 1593 { 1594 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1595 { 1596 Type: pipeline.AggregationOpType, 1597 Aggregation: pipeline.AggregationOp{Type: aggregation.Last}, 1598 }, 1599 { 1600 Type: pipeline.TransformationOpType, 1601 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 1602 }, 1603 { 1604 Type: pipeline.RollupOpType, 1605 Rollup: rr1, 1606 }, 1607 }), 1608 StoragePolicies: testStoragePolicies(), 1609 }, 1610 }, 1611 }, 1612 }, 1613 } 1614 1615 firstLevelAggregationTypes := aggregation.Types{aggregation.Last} 1616 nonFirstLevelAggregationTypes := aggregation.Types{aggregation.Sum} 1617 opts := testValidatorOptions(). 1618 SetAllowedFirstLevelAggregationTypesFor(metric.GaugeType, firstLevelAggregationTypes). 1619 SetAllowedNonFirstLevelAggregationTypesFor(metric.GaugeType, nonFirstLevelAggregationTypes) 1620 validator := NewValidator(opts) 1621 require.NoError(t, validator.ValidateSnapshot(view)) 1622 } 1623 1624 func TestValidatorValidateRollupRuleDuplicateRollupIDs(t *testing.T) { 1625 rr1, err := pipeline.NewRollupOp( 1626 pipeline.GroupByRollupType, 1627 "rName1", 1628 []string{"rtagName1", "rtagName2"}, 1629 aggregation.MustCompressTypes(aggregation.Sum), 1630 ) 1631 require.NoError(t, err) 1632 rr2, err := pipeline.NewRollupOp( 1633 pipeline.GroupByRollupType, 1634 "rName1", 1635 []string{"rtagName1", "rtagName2"}, 1636 aggregation.DefaultID, 1637 ) 1638 require.NoError(t, err) 1639 view := view.RuleSet{ 1640 RollupRules: []view.RollupRule{ 1641 { 1642 Name: "snapshot1", 1643 Filter: testTypeTag + ":" + testGaugeType, 1644 Targets: []view.RollupTarget{ 1645 { 1646 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1647 { 1648 Type: pipeline.AggregationOpType, 1649 Aggregation: pipeline.AggregationOp{Type: aggregation.Last}, 1650 }, 1651 { 1652 Type: pipeline.TransformationOpType, 1653 Transformation: pipeline.TransformationOp{Type: transformation.PerSecond}, 1654 }, 1655 { 1656 Type: pipeline.RollupOpType, 1657 Rollup: rr1, 1658 }, 1659 }), 1660 StoragePolicies: testStoragePolicies(), 1661 }, 1662 }, 1663 }, 1664 { 1665 Name: "snapshot2", 1666 Filter: testTypeTag + ":" + testGaugeType, 1667 Targets: []view.RollupTarget{ 1668 { 1669 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 1670 { 1671 Type: pipeline.RollupOpType, 1672 Rollup: rr2, 1673 }, 1674 }), 1675 StoragePolicies: testStoragePolicies(), 1676 }, 1677 }, 1678 }, 1679 }, 1680 } 1681 firstLevelAggregationTypes := aggregation.Types{aggregation.Last} 1682 nonFirstLevelAggregationTypes := aggregation.Types{aggregation.Sum} 1683 opts := testValidatorOptions(). 1684 SetAllowedFirstLevelAggregationTypesFor(metric.GaugeType, firstLevelAggregationTypes). 1685 SetAllowedNonFirstLevelAggregationTypesFor(metric.GaugeType, nonFirstLevelAggregationTypes) 1686 validator := NewValidator(opts) 1687 err = validator.ValidateSnapshot(view) 1688 require.Error(t, err) 1689 require.True(t, strings.Contains(err.Error(), "more than one rollup operations with name 'rName1' and tags '[rtagName1 rtagName2]' exist")) 1690 _, ok := err.(errors.InvalidInputError) 1691 require.True(t, ok) 1692 } 1693 1694 func TestValidatorValidateMappingRuleValidDropPolicy(t *testing.T) { 1695 view := view.RuleSet{ 1696 MappingRules: []view.MappingRule{ 1697 { 1698 Name: "snapshot1", 1699 Filter: "tag1:value1", 1700 DropPolicy: policy.DropMust, 1701 }, 1702 }, 1703 } 1704 validator := NewValidator(testValidatorOptions()) 1705 require.NoError(t, validator.ValidateSnapshot(view)) 1706 } 1707 1708 func TestValidatorValidateMappingRuleInvalidDropPolicy(t *testing.T) { 1709 type invalidDropPolicyTest struct { 1710 name string 1711 view view.RuleSet 1712 } 1713 1714 tests := []invalidDropPolicyTest{ 1715 { 1716 name: "invalid drop policy", 1717 view: view.RuleSet{ 1718 MappingRules: []view.MappingRule{ 1719 { 1720 Name: "snapshot1", 1721 Filter: "tag1:value1", 1722 DropPolicy: policy.DropPolicy(math.MaxUint32), 1723 }, 1724 }, 1725 }, 1726 }, 1727 } 1728 1729 for _, dropPolicy := range policy.ValidDropPolicies() { 1730 if dropPolicy == policy.DropNone { 1731 continue // The drop none policy is always valid, since its not active 1732 } 1733 1734 tests = append(tests, []invalidDropPolicyTest{ 1735 { 1736 name: dropPolicy.String() + " policy with storage policies", 1737 view: view.RuleSet{ 1738 MappingRules: []view.MappingRule{ 1739 { 1740 Name: "snapshot1", 1741 Filter: "tag1:value1", 1742 DropPolicy: policy.DropMust, 1743 StoragePolicies: testStoragePolicies(), 1744 }, 1745 }, 1746 }, 1747 }, 1748 { 1749 name: dropPolicy.String() + " policy with non-default aggregation ID", 1750 view: view.RuleSet{ 1751 MappingRules: []view.MappingRule{ 1752 { 1753 Name: "snapshot1", 1754 Filter: "tag1:value1", 1755 DropPolicy: policy.DropMust, 1756 AggregationID: aggregation.NewIDCompressor().MustCompress( 1757 aggregation.Types{aggregation.Last}, 1758 ), 1759 }, 1760 }, 1761 }, 1762 }, 1763 }...) 1764 } 1765 1766 validator := NewValidator(testValidatorOptions()) 1767 1768 for _, test := range tests { 1769 t.Run(test.name, func(t *testing.T) { 1770 require.Error(t, validator.ValidateSnapshot(test.view)) 1771 }) 1772 } 1773 } 1774 1775 func testKVNamespaceValidator(t *testing.T) namespace.Validator { 1776 store := mem.NewStore() 1777 _, err := store.Set(testNamespacesKey, &commonpb.StringArrayProto{Values: testNamespaces}) 1778 require.NoError(t, err) 1779 kvOpts := kv.NewNamespaceValidatorOptions(). 1780 SetKVStore(store). 1781 SetValidNamespacesKey(testNamespacesKey) 1782 nsValidator, err := kv.NewNamespaceValidator(kvOpts) 1783 require.NoError(t, err) 1784 return nsValidator 1785 } 1786 1787 func testMetricTypesFn() MetricTypesFn { 1788 return func(filters filters.TagFilterValueMap) ([]metric.Type, error) { 1789 fv, exists := filters[testTypeTag] 1790 if !exists { 1791 return []metric.Type{metric.UnknownType}, nil 1792 } 1793 switch fv.Pattern { 1794 case testCounterType: 1795 return []metric.Type{metric.CounterType}, nil 1796 case testTimerType: 1797 return []metric.Type{metric.TimerType}, nil 1798 case testGaugeType: 1799 return []metric.Type{metric.GaugeType}, nil 1800 default: 1801 return nil, fmt.Errorf("unknown metric type %v", fv.Pattern) 1802 } 1803 } 1804 } 1805 1806 func testStoragePolicies() policy.StoragePolicies { 1807 return policy.StoragePolicies{ 1808 policy.MustParseStoragePolicy("10s:6h"), 1809 policy.MustParseStoragePolicy("1m:24h"), 1810 } 1811 } 1812 1813 func testValidatorOptions() Options { 1814 return NewOptions(). 1815 SetDefaultAllowedStoragePolicies(testStoragePolicies()). 1816 SetDefaultAllowedFirstLevelAggregationTypes(nil). 1817 SetMetricTypesFn(testMetricTypesFn()). 1818 SetMultiAggregationTypesEnabledFor([]metric.Type{metric.TimerType}) 1819 }