github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/alertmanager/api_test.go (about) 1 package alertmanager 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "net/http/httptest" 10 "testing" 11 12 "github.com/go-kit/log" 13 "github.com/gorilla/mux" 14 "github.com/grafana/dskit/flagext" 15 "github.com/grafana/dskit/services" 16 "github.com/pkg/errors" 17 "github.com/prometheus/alertmanager/config" 18 "github.com/prometheus/client_golang/prometheus" 19 commoncfg "github.com/prometheus/common/config" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 "github.com/thanos-io/thanos/pkg/objstore" 23 "github.com/weaveworks/common/user" 24 "gopkg.in/yaml.v2" 25 26 "github.com/cortexproject/cortex/pkg/alertmanager/alertspb" 27 "github.com/cortexproject/cortex/pkg/alertmanager/alertstore/bucketclient" 28 util_log "github.com/cortexproject/cortex/pkg/util/log" 29 ) 30 31 func TestAMConfigValidationAPI(t *testing.T) { 32 testCases := []struct { 33 name string 34 cfg string 35 maxConfigSize int 36 maxTemplates int 37 maxTemplateSize int 38 39 response string 40 err error 41 }{ 42 { 43 name: "Should return error if the alertmanager config contains no receivers", 44 cfg: ` 45 alertmanager_config: | 46 route: 47 receiver: 'default-receiver' 48 group_wait: 30s 49 group_interval: 5m 50 repeat_interval: 4h 51 group_by: [cluster, alertname] 52 `, 53 err: fmt.Errorf("error validating Alertmanager config: undefined receiver \"default-receiver\" used in route"), 54 }, 55 { 56 name: "Should pass if the alertmanager config is valid", 57 cfg: ` 58 alertmanager_config: | 59 route: 60 receiver: 'default-receiver' 61 group_wait: 30s 62 group_interval: 5m 63 repeat_interval: 4h 64 group_by: [cluster, alertname] 65 receivers: 66 - name: default-receiver 67 `, 68 }, 69 { 70 name: "Should return error if the config is empty due to wrong indentation", 71 cfg: ` 72 alertmanager_config: | 73 route: 74 receiver: 'default-receiver' 75 group_wait: 30s 76 group_interval: 5m 77 repeat_interval: 4h 78 group_by: [cluster, alertname] 79 receivers: 80 - name: default-receiver 81 template_files: 82 "good.tpl": "good-templ" 83 "not/very/good.tpl": "bad-template" 84 `, 85 err: fmt.Errorf("error validating Alertmanager config: configuration provided is empty, if you'd like to remove your configuration please use the delete configuration endpoint"), 86 }, 87 { 88 name: "Should return error if the alertmanager config is empty due to wrong key", 89 cfg: ` 90 XWRONGalertmanager_config: | 91 route: 92 receiver: 'default-receiver' 93 group_wait: 30s 94 group_interval: 5m 95 repeat_interval: 4h 96 group_by: [cluster, alertname] 97 receivers: 98 - name: default-receiver 99 template_files: 100 "good.tpl": "good-templ" 101 `, 102 err: fmt.Errorf("error validating Alertmanager config: configuration provided is empty, if you'd like to remove your configuration please use the delete configuration endpoint"), 103 }, 104 { 105 name: "Should return error if the external template file name contains an absolute path", 106 cfg: ` 107 alertmanager_config: | 108 route: 109 receiver: 'default-receiver' 110 group_wait: 30s 111 group_interval: 5m 112 repeat_interval: 4h 113 group_by: [cluster, alertname] 114 receivers: 115 - name: default-receiver 116 template_files: 117 "/absolute/filepath": "a simple template" 118 `, 119 err: fmt.Errorf(`error validating Alertmanager config: invalid template name "/absolute/filepath": the template name cannot contain any path`), 120 }, 121 { 122 name: "Should return error if the external template file name contains a relative path", 123 cfg: ` 124 alertmanager_config: | 125 route: 126 receiver: 'default-receiver' 127 group_wait: 30s 128 group_interval: 5m 129 repeat_interval: 4h 130 group_by: [cluster, alertname] 131 receivers: 132 - name: default-receiver 133 template_files: 134 "../filepath": "a simple template" 135 `, 136 err: fmt.Errorf(`error validating Alertmanager config: invalid template name "../filepath": the template name cannot contain any path`), 137 }, 138 { 139 name: "Should return error if the external template file name is not a valid filename", 140 cfg: ` 141 alertmanager_config: | 142 route: 143 receiver: 'default-receiver' 144 group_wait: 30s 145 group_interval: 5m 146 repeat_interval: 4h 147 group_by: [cluster, alertname] 148 receivers: 149 - name: default-receiver 150 template_files: 151 "good.tpl": "good-templ" 152 ".": "bad-template" 153 `, 154 err: fmt.Errorf("error validating Alertmanager config: unable to store template file '.'"), 155 }, 156 { 157 name: "Should return error if the referenced template contains the root /", 158 cfg: ` 159 alertmanager_config: | 160 route: 161 receiver: 'default-receiver' 162 group_wait: 30s 163 group_interval: 5m 164 repeat_interval: 4h 165 group_by: [cluster, alertname] 166 receivers: 167 - name: default-receiver 168 templates: 169 - "/" 170 `, 171 err: fmt.Errorf(`error validating Alertmanager config: invalid template name "/": the template name cannot contain any path`), 172 }, 173 { 174 name: "Should return error if the referenced template contains the root with repeated separators ///", 175 cfg: ` 176 alertmanager_config: | 177 route: 178 receiver: 'default-receiver' 179 group_wait: 30s 180 group_interval: 5m 181 repeat_interval: 4h 182 group_by: [cluster, alertname] 183 receivers: 184 - name: default-receiver 185 templates: 186 - "///" 187 `, 188 err: fmt.Errorf(`error validating Alertmanager config: invalid template name "///": the template name cannot contain any path`), 189 }, 190 { 191 name: "Should return error if the referenced template contains an absolute path", 192 cfg: ` 193 alertmanager_config: | 194 route: 195 receiver: 'default-receiver' 196 group_wait: 30s 197 group_interval: 5m 198 repeat_interval: 4h 199 group_by: [cluster, alertname] 200 receivers: 201 - name: default-receiver 202 templates: 203 - "/absolute/filepath" 204 `, 205 err: fmt.Errorf(`error validating Alertmanager config: invalid template name "/absolute/filepath": the template name cannot contain any path`), 206 }, 207 { 208 name: "Should return error if the referenced template contains a relative path", 209 cfg: ` 210 alertmanager_config: | 211 route: 212 receiver: 'default-receiver' 213 group_wait: 30s 214 group_interval: 5m 215 repeat_interval: 4h 216 group_by: [cluster, alertname] 217 receivers: 218 - name: default-receiver 219 templates: 220 - "../filepath" 221 `, 222 err: fmt.Errorf(`error validating Alertmanager config: invalid template name "../filepath": the template name cannot contain any path`), 223 }, 224 { 225 name: "Should pass if the referenced template is valid filename", 226 cfg: ` 227 alertmanager_config: | 228 route: 229 receiver: 'default-receiver' 230 group_wait: 30s 231 group_interval: 5m 232 repeat_interval: 4h 233 group_by: [cluster, alertname] 234 receivers: 235 - name: default-receiver 236 templates: 237 - "something.tmpl" 238 `, 239 }, 240 { 241 name: "Should return error if global HTTP password_file is set", 242 cfg: ` 243 alertmanager_config: | 244 global: 245 http_config: 246 basic_auth: 247 password_file: /secrets 248 249 route: 250 receiver: 'default-receiver' 251 receivers: 252 - name: default-receiver 253 `, 254 err: errors.Wrap(errPasswordFileNotAllowed, "error validating Alertmanager config"), 255 }, 256 { 257 name: "Should return error if global HTTP bearer_token_file is set", 258 cfg: ` 259 alertmanager_config: | 260 global: 261 http_config: 262 bearer_token_file: /secrets 263 264 route: 265 receiver: 'default-receiver' 266 receivers: 267 - name: default-receiver 268 `, 269 err: errors.Wrap(errPasswordFileNotAllowed, "error validating Alertmanager config"), 270 }, 271 { 272 name: "Should return error if global HTTP credentials_file is set", 273 cfg: ` 274 alertmanager_config: | 275 global: 276 http_config: 277 authorization: 278 credentials_file: /secrets 279 280 route: 281 receiver: 'default-receiver' 282 receivers: 283 - name: default-receiver 284 `, 285 err: errors.Wrap(errPasswordFileNotAllowed, "error validating Alertmanager config"), 286 }, 287 { 288 name: "Should return error if global OAuth2 client_secret_file is set", 289 cfg: ` 290 alertmanager_config: | 291 global: 292 http_config: 293 oauth2: 294 client_id: test 295 token_url: http://example.com 296 client_secret_file: /secrets 297 298 route: 299 receiver: 'default-receiver' 300 receivers: 301 - name: default-receiver 302 `, 303 err: errors.Wrap(errOAuth2SecretFileNotAllowed, "error validating Alertmanager config"), 304 }, 305 { 306 name: "Should return error if receiver's HTTP password_file is set", 307 cfg: ` 308 alertmanager_config: | 309 receivers: 310 - name: default-receiver 311 webhook_configs: 312 - url: http://localhost 313 http_config: 314 basic_auth: 315 password_file: /secrets 316 317 route: 318 receiver: 'default-receiver' 319 `, 320 err: errors.Wrap(errPasswordFileNotAllowed, "error validating Alertmanager config"), 321 }, 322 { 323 name: "Should return error if receiver's HTTP bearer_token_file is set", 324 cfg: ` 325 alertmanager_config: | 326 receivers: 327 - name: default-receiver 328 webhook_configs: 329 - url: http://localhost 330 http_config: 331 bearer_token_file: /secrets 332 333 route: 334 receiver: 'default-receiver' 335 `, 336 err: errors.Wrap(errPasswordFileNotAllowed, "error validating Alertmanager config"), 337 }, 338 { 339 name: "Should return error if receiver's HTTP credentials_file is set", 340 cfg: ` 341 alertmanager_config: | 342 receivers: 343 - name: default-receiver 344 webhook_configs: 345 - url: http://localhost 346 http_config: 347 authorization: 348 credentials_file: /secrets 349 350 route: 351 receiver: 'default-receiver' 352 `, 353 err: errors.Wrap(errPasswordFileNotAllowed, "error validating Alertmanager config"), 354 }, 355 { 356 name: "Should return error if receiver's OAuth2 client_secret_file is set", 357 cfg: ` 358 alertmanager_config: | 359 receivers: 360 - name: default-receiver 361 webhook_configs: 362 - url: http://localhost 363 http_config: 364 oauth2: 365 client_id: test 366 token_url: http://example.com 367 client_secret_file: /secrets 368 369 route: 370 receiver: 'default-receiver' 371 `, 372 err: errors.Wrap(errOAuth2SecretFileNotAllowed, "error validating Alertmanager config"), 373 }, 374 { 375 name: "Should return error if receiver's HTTP proxy_url is set", 376 cfg: ` 377 alertmanager_config: | 378 receivers: 379 - name: default-receiver 380 webhook_configs: 381 - url: http://localhost 382 http_config: 383 proxy_url: http://localhost 384 385 route: 386 receiver: 'default-receiver' 387 `, 388 err: errors.Wrap(errProxyURLNotAllowed, "error validating Alertmanager config"), 389 }, 390 { 391 name: "Should return error if global slack_api_url_file is set", 392 cfg: ` 393 alertmanager_config: | 394 global: 395 slack_api_url_file: /secrets 396 397 receivers: 398 - name: default-receiver 399 webhook_configs: 400 - url: http://localhost 401 402 route: 403 receiver: 'default-receiver' 404 `, 405 err: errors.Wrap(errSlackAPIURLFileNotAllowed, "error validating Alertmanager config"), 406 }, 407 { 408 name: "Should return error if Slack api_url_file is set", 409 cfg: ` 410 alertmanager_config: | 411 receivers: 412 - name: default-receiver 413 slack_configs: 414 - api_url_file: /secrets 415 416 route: 417 receiver: 'default-receiver' 418 `, 419 err: errors.Wrap(errSlackAPIURLFileNotAllowed, "error validating Alertmanager config"), 420 }, 421 { 422 name: "Should return error if VictorOps api_key_file is set", 423 cfg: ` 424 alertmanager_config: | 425 receivers: 426 - name: default-receiver 427 victorops_configs: 428 - api_key_file: /secrets 429 api_key: my-key 430 routing_key: test 431 432 route: 433 receiver: 'default-receiver' 434 `, 435 err: errors.Wrap(errVictorOpsAPIKeyFileNotAllowed, "error validating Alertmanager config"), 436 }, 437 { 438 name: "should return error if template is wrong", 439 cfg: ` 440 alertmanager_config: | 441 route: 442 receiver: 'default-receiver' 443 group_wait: 30s 444 group_interval: 5m 445 repeat_interval: 4h 446 group_by: [cluster, alertname] 447 receivers: 448 - name: default-receiver 449 templates: 450 - "*.tmpl" 451 template_files: 452 "test.tmpl": "{{ invalid Go template }}" 453 `, 454 err: fmt.Errorf(`error validating Alertmanager config: template: test.tmpl:1: function "invalid" not defined`), 455 }, 456 { 457 name: "config too big", 458 cfg: ` 459 alertmanager_config: | 460 route: 461 receiver: 'default-receiver' 462 group_wait: 30s 463 group_interval: 5m 464 repeat_interval: 4h 465 group_by: [cluster, alertname] 466 receivers: 467 - name: default-receiver 468 `, 469 maxConfigSize: 10, 470 err: fmt.Errorf(errConfigurationTooBig, 10), 471 }, 472 { 473 name: "config size OK", 474 cfg: ` 475 alertmanager_config: | 476 route: 477 receiver: 'default-receiver' 478 group_wait: 30s 479 group_interval: 5m 480 repeat_interval: 4h 481 group_by: [cluster, alertname] 482 receivers: 483 - name: default-receiver 484 `, 485 maxConfigSize: 1000, 486 err: nil, 487 }, 488 { 489 name: "templates limit reached", 490 cfg: ` 491 alertmanager_config: | 492 route: 493 receiver: 'default-receiver' 494 receivers: 495 - name: default-receiver 496 template_files: 497 "t1.tmpl": "Some template" 498 "t2.tmpl": "Some template" 499 "t3.tmpl": "Some template" 500 "t4.tmpl": "Some template" 501 "t5.tmpl": "Some template" 502 `, 503 maxTemplates: 3, 504 err: errors.Wrap(fmt.Errorf(errTooManyTemplates, 5, 3), "error validating Alertmanager config"), 505 }, 506 { 507 name: "templates limit not reached", 508 cfg: ` 509 alertmanager_config: | 510 route: 511 receiver: 'default-receiver' 512 receivers: 513 - name: default-receiver 514 template_files: 515 "t1.tmpl": "Some template" 516 "t2.tmpl": "Some template" 517 "t3.tmpl": "Some template" 518 "t4.tmpl": "Some template" 519 "t5.tmpl": "Some template" 520 `, 521 maxTemplates: 10, 522 err: nil, 523 }, 524 { 525 name: "template size limit reached", 526 cfg: ` 527 alertmanager_config: | 528 route: 529 receiver: 'default-receiver' 530 receivers: 531 - name: default-receiver 532 template_files: 533 "t1.tmpl": "Very big template" 534 `, 535 maxTemplateSize: 5, 536 err: errors.Wrap(fmt.Errorf(errTemplateTooBig, "t1.tmpl", 17, 5), "error validating Alertmanager config"), 537 }, 538 { 539 name: "template size limit ok", 540 cfg: ` 541 alertmanager_config: | 542 route: 543 receiver: 'default-receiver' 544 receivers: 545 - name: default-receiver 546 template_files: 547 "t1.tmpl": "Very big template" 548 `, 549 maxTemplateSize: 20, 550 err: nil, 551 }, 552 } 553 554 limits := &mockAlertManagerLimits{} 555 am := &MultitenantAlertmanager{ 556 store: prepareInMemoryAlertStore(), 557 logger: util_log.Logger, 558 limits: limits, 559 } 560 for _, tc := range testCases { 561 t.Run(tc.name, func(t *testing.T) { 562 limits.maxConfigSize = tc.maxConfigSize 563 limits.maxTemplatesCount = tc.maxTemplates 564 limits.maxSizeOfTemplate = tc.maxTemplateSize 565 566 req := httptest.NewRequest(http.MethodPost, "http://alertmanager/api/v1/alerts", bytes.NewReader([]byte(tc.cfg))) 567 ctx := user.InjectOrgID(req.Context(), "testing") 568 w := httptest.NewRecorder() 569 am.SetUserConfig(w, req.WithContext(ctx)) 570 resp := w.Result() 571 572 body, err := ioutil.ReadAll(resp.Body) 573 require.NoError(t, err) 574 575 if tc.err == nil { 576 require.Equal(t, http.StatusCreated, resp.StatusCode) 577 require.Equal(t, "", string(body)) 578 } else { 579 require.Equal(t, http.StatusBadRequest, resp.StatusCode) 580 require.Equal(t, tc.err.Error()+"\n", string(body)) 581 } 582 }) 583 } 584 } 585 586 func TestMultitenantAlertmanager_DeleteUserConfig(t *testing.T) { 587 storage := objstore.NewInMemBucket() 588 alertStore := bucketclient.NewBucketAlertStore(storage, nil, log.NewNopLogger()) 589 590 am := &MultitenantAlertmanager{ 591 store: alertStore, 592 logger: util_log.Logger, 593 } 594 595 require.NoError(t, alertStore.SetAlertConfig(context.Background(), alertspb.AlertConfigDesc{ 596 User: "test_user", 597 RawConfig: "config", 598 })) 599 600 require.Equal(t, 1, len(storage.Objects())) 601 602 req := httptest.NewRequest("POST", "/multitenant_alertmanager/delete_tenant_config", nil) 603 // Missing user returns error 401. (DeleteUserConfig does this, but in practice, authentication middleware will do it first) 604 { 605 rec := httptest.NewRecorder() 606 am.DeleteUserConfig(rec, req) 607 require.Equal(t, http.StatusUnauthorized, rec.Code) 608 require.Equal(t, 1, len(storage.Objects())) 609 } 610 611 // With user in the context. 612 ctx := user.InjectOrgID(context.Background(), "test_user") 613 req = req.WithContext(ctx) 614 { 615 rec := httptest.NewRecorder() 616 am.DeleteUserConfig(rec, req) 617 require.Equal(t, http.StatusOK, rec.Code) 618 require.Equal(t, 0, len(storage.Objects())) 619 } 620 621 // Repeating the request still reports 200 622 { 623 rec := httptest.NewRecorder() 624 am.DeleteUserConfig(rec, req) 625 626 require.Equal(t, http.StatusOK, rec.Code) 627 require.Equal(t, 0, len(storage.Objects())) 628 } 629 } 630 631 func TestAMConfigListUserConfig(t *testing.T) { 632 testCases := map[string]*UserConfig{ 633 "user1": { 634 AlertmanagerConfig: ` 635 global: 636 resolve_timeout: 5m 637 route: 638 receiver: route1 639 group_by: 640 - '...' 641 continue: false 642 receivers: 643 - name: route1 644 webhook_configs: 645 - send_resolved: true 646 http_config: {} 647 url: http://alertmanager/api/notifications?orgId=1&rrid=7 648 max_alerts: 0 649 `, 650 }, 651 "user2": { 652 AlertmanagerConfig: ` 653 global: 654 resolve_timeout: 5m 655 route: 656 receiver: route1 657 group_by: 658 - '...' 659 continue: false 660 receivers: 661 - name: route1 662 webhook_configs: 663 - send_resolved: true 664 http_config: {} 665 url: http://alertmanager/api/notifications?orgId=2&rrid=7 666 max_alerts: 0 667 `, 668 }, 669 } 670 671 storage := objstore.NewInMemBucket() 672 alertStore := bucketclient.NewBucketAlertStore(storage, nil, log.NewNopLogger()) 673 674 for u, cfg := range testCases { 675 err := alertStore.SetAlertConfig(context.Background(), alertspb.AlertConfigDesc{ 676 User: u, 677 RawConfig: cfg.AlertmanagerConfig, 678 }) 679 require.NoError(t, err) 680 } 681 682 externalURL := flagext.URLValue{} 683 err := externalURL.Set("http://localhost:8080/alertmanager") 684 require.NoError(t, err) 685 686 // Create the Multitenant Alertmanager. 687 reg := prometheus.NewPedanticRegistry() 688 cfg := mockAlertmanagerConfig(t) 689 am, err := createMultitenantAlertmanager(cfg, nil, nil, alertStore, nil, nil, log.NewNopLogger(), reg) 690 require.NoError(t, err) 691 require.NoError(t, services.StartAndAwaitRunning(context.Background(), am)) 692 defer services.StopAndAwaitTerminated(context.Background(), am) //nolint:errcheck 693 694 err = am.loadAndSyncConfigs(context.Background(), reasonPeriodic) 695 require.NoError(t, err) 696 require.Len(t, am.alertmanagers, 2) 697 698 router := mux.NewRouter() 699 router.Path("/multitenant_alertmanager/configs").Methods(http.MethodGet).HandlerFunc(am.ListAllConfigs) 700 req := httptest.NewRequest("GET", "https://localhost:8080/multitenant_alertmanager/configs", nil) 701 w := httptest.NewRecorder() 702 router.ServeHTTP(w, req) 703 704 resp := w.Result() 705 require.Equal(t, http.StatusOK, resp.StatusCode) 706 require.Equal(t, "application/yaml", resp.Header.Get("Content-Type")) 707 body, err := ioutil.ReadAll(resp.Body) 708 require.NoError(t, err) 709 old, err := yaml.Marshal(testCases) 710 require.NoError(t, err) 711 require.YAMLEq(t, string(old), string(body)) 712 } 713 714 func TestValidateAlertmanagerConfig(t *testing.T) { 715 tests := map[string]struct { 716 input interface{} 717 expected error 718 }{ 719 "*HTTPClientConfig": { 720 input: &commoncfg.HTTPClientConfig{ 721 BasicAuth: &commoncfg.BasicAuth{ 722 PasswordFile: "/secrets", 723 }, 724 }, 725 expected: errPasswordFileNotAllowed, 726 }, 727 "HTTPClientConfig": { 728 input: commoncfg.HTTPClientConfig{ 729 BasicAuth: &commoncfg.BasicAuth{ 730 PasswordFile: "/secrets", 731 }, 732 }, 733 expected: errPasswordFileNotAllowed, 734 }, 735 "*TLSConfig": { 736 input: &commoncfg.TLSConfig{ 737 CertFile: "/cert", 738 }, 739 expected: errTLSFileNotAllowed, 740 }, 741 "TLSConfig": { 742 input: commoncfg.TLSConfig{ 743 CertFile: "/cert", 744 }, 745 expected: errTLSFileNotAllowed, 746 }, 747 "struct containing *HTTPClientConfig as direct child": { 748 input: config.GlobalConfig{ 749 HTTPConfig: &commoncfg.HTTPClientConfig{ 750 BasicAuth: &commoncfg.BasicAuth{ 751 PasswordFile: "/secrets", 752 }, 753 }, 754 }, 755 expected: errPasswordFileNotAllowed, 756 }, 757 "struct containing *HTTPClientConfig as nested child": { 758 input: config.Config{ 759 Global: &config.GlobalConfig{ 760 HTTPConfig: &commoncfg.HTTPClientConfig{ 761 BasicAuth: &commoncfg.BasicAuth{ 762 PasswordFile: "/secrets", 763 }, 764 }, 765 }, 766 }, 767 expected: errPasswordFileNotAllowed, 768 }, 769 "struct containing *HTTPClientConfig as nested child within a slice": { 770 input: config.Config{ 771 Receivers: []*config.Receiver{{ 772 Name: "test", 773 WebhookConfigs: []*config.WebhookConfig{{ 774 HTTPConfig: &commoncfg.HTTPClientConfig{ 775 BasicAuth: &commoncfg.BasicAuth{ 776 PasswordFile: "/secrets", 777 }, 778 }, 779 }}}, 780 }, 781 }, 782 expected: errPasswordFileNotAllowed, 783 }, 784 "map containing *HTTPClientConfig": { 785 input: map[string]*commoncfg.HTTPClientConfig{ 786 "test": { 787 BasicAuth: &commoncfg.BasicAuth{ 788 PasswordFile: "/secrets", 789 }, 790 }, 791 }, 792 expected: errPasswordFileNotAllowed, 793 }, 794 "map containing TLSConfig as nested child": { 795 input: map[string][]config.EmailConfig{ 796 "test": {{ 797 TLSConfig: commoncfg.TLSConfig{ 798 CAFile: "/file", 799 }, 800 }}, 801 }, 802 expected: errTLSFileNotAllowed, 803 }, 804 } 805 806 for testName, testData := range tests { 807 t.Run(testName, func(t *testing.T) { 808 err := validateAlertmanagerConfig(testData.input) 809 assert.ErrorIs(t, err, testData.expected) 810 }) 811 } 812 }