PK���ȼRY��������€��� �v3.phpUT �øŽg‰gñ“gux �õ��õ��½T]kÛ0}߯pEhìâÙM7X‰çv%”v0֐µ{)Aå:6S$!ÉMJèߕ?R÷!>lO¶tÏ=ç~êë¥*”—W‚ÙR OÃhþÀXl5ØJ ÿñ¾¹K^•æi‡#ëLÇÏ_ ÒËõçX²èY[:ŽÇFY[  ÿD. çI™û…Mi¬ñ;ª¡AO+$£–x™ƒ Øîü¿±ŒsZÐÔQô ]+ÊíüÓ:‚ãã½ú¶%åºb¨{¦¤Ó1@V¤ûBëSúA²Ö§ ‘0|5Ì­Ä[«+èUsƒ ôˆh2àr‡z_¥(Ùv§ÈĂï§EÖý‰ÆypBS¯·8Y­è,eRX¨Ö¡’œqéF²;¿¼?Ø?Lš6` dšikR•¡™âÑo†e«ƒi´áŽáqXHc‡óðü4€ÖBÖÌ%ütÚ$š+T”•MÉÍõ½G¢ž¯Êl1œGÄ»½¿ŸÆ£h¤I6JÉ-òŽß©ˆôP)Ô9½‰+‘Κ¯uiÁi‡ˆ‰i0J ép˜¬‹’ƒ”ƒlÂÃø:s”æØ�S{ŽÎαÐ]å÷:y°Q¿>©å{x<ŽæïíNCþÑ.Mf?¨«2ý}=ûõýî'=£§ÿu•Ü(—¾IIa­"éþ@¶�¿ä9?^-qìÇÞôvŠeÈc ðlacã®xèÄ'®âd¶ çˆSEæódP/ÍÆv{Ô)Ó ?>…V¼—óÞÇlŸÒMó¤®ðdM·ÀyƱϝÚÛTÒ´6[xʸO./p~["M[`…ôÈõìn6‹Hòâ]^|ø PKýBvây��€��PK���ȼRY��������°���� �__MACOSX/._v3.phpUT �øŽg‰gþ“gux �õ��õ��c`cg`b`ðMLVðVˆP€'qƒøˆŽ!!AP&HÇ %PDF-1.7 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >> endobj 2 0 obj << /Type /Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Kids [6 0 R ] /Count 1 /Resources << /ProcSet 4 0 R /Font << /F1 8 0 R /F2 9 0 R >> >> /MediaBox [0.000 0.000 595.280 841.890] >> endobj 4 0 obj [/PDF /Text ] endobj 5 0 obj << /Producer (���d�o�m�p�d�f� �2�.�0�.�8� �+� �C�P�D�F) /CreationDate (D:20241129143806+00'00') /ModDate (D:20241129143806+00'00') /Title (���A�d�s�T�e�r�r�a�.�c�o�m� �i�n�v�o�i�c�e) >> endobj 6 0 obj << /Type /Page /MediaBox [0.000 0.000 595.280 841.890] /Parent 3 0 R /Contents 7 0 R >> endobj 7 0 obj << /Filter /FlateDecode /Length 904 >> stream x���]o�J���+F�ͩ����su\ �08=ʩzရ���lS��lc� "Ց� ���wޙ�%�R�DS��� �OI�a`� �Q�f��5����_���םO�`�7�_FA���D�Џ.j�a=�j����>��n���R+�P��l�rH�{0��w��0��=W�2D ����G���I�>�_B3ed�H�yJ�G>/��ywy�fk��%�$�2.��d_�h����&)b0��"[\B��*_.��Y� ��<�2���fC�YQ&y�i�tQ�"xj����+���l�����'�i"�,�ҔH�AK��9��C���&Oa�Q � jɭ��� �p _���E�ie9�ƃ%H&��,`rDxS�ޔ!�(�X!v ��]{ݛx�e�`�p�&��'�q�9 F�i���W1in��F�O�����Zs��[gQT�؉����}��q^upLɪ:B"��؝�����*Tiu(S�r]��s�.��s9n�N!K!L�M�?�*[��N�8��c��ۯ�b�� ��� �YZ���SR3�n�����lPN��P�;��^�]�!'�z-���ӊ���/��껣��4�l(M�E�QL��X ��~���G��M|�����*��~�;/=N4�-|y�`�i�\�e�T�<���L��G}�"В�J^���q��"X�?(V�ߣXۆ{��H[����P�� �c���kc�Z�9v�����? �a��R�h|��^�k�D4W���?Iӊ�]<��4�)$wdat���~�����������|�L��x�p|N�*��E� �/4�Qpi�x.>��d����,M�y|4^�Ż��8S/޾���uQe���D�y� ��ͧH�����j�wX � �&z� endstream endobj 8 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >> endobj xref 0 10 0000000000 65535 f 0000000009 00000 n 0000000074 00000 n 0000000120 00000 n 0000000284 00000 n 0000000313 00000 n 0000000514 00000 n 0000000617 00000 n 0000001593 00000 n 0000001700 00000 n trailer << /Size 10 /Root 1 0 R /Info 5 0 R /ID[] >> startxref 1812 %%EOF
Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 128

Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 129

Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 130

Warning: Cannot modify header information - headers already sent by (output started at /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php:1) in /home/u866776246/domains/wisatalogung.com/public_html/uploads/produk/1775157541_x.php on line 131
// Copyright 2016 Prometheus Team // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package config import ( "encoding/json" "net/url" "os" "reflect" "regexp" "strings" "testing" "time" commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" ) func TestLoadEmptyString(t *testing.T) { var in string _, err := Load(in) expected := "no route provided in config" if err == nil { t.Fatalf("no error returned, expected:\n%v", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) } } func TestDefaultReceiverExists(t *testing.T) { in := ` route: group_wait: 30s ` _, err := Load(in) expected := "root route must specify a default receiver" if err == nil { t.Fatalf("no error returned, expected:\n%v", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) } } func TestReceiverNameIsUnique(t *testing.T) { in := ` route: receiver: team-X receivers: - name: 'team-X' - name: 'team-X' ` _, err := Load(in) expected := "notification config name \"team-X\" is not unique" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestReceiverExists(t *testing.T) { in := ` route: receiver: team-X receivers: - name: 'team-Y' ` _, err := Load(in) expected := "undefined receiver \"team-X\" used in route" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestReceiverExistsForDeepSubRoute(t *testing.T) { in := ` route: receiver: team-X routes: - match: foo: bar routes: - match: foo: bar receiver: nonexistent receivers: - name: 'team-X' ` _, err := Load(in) expected := "undefined receiver \"nonexistent\" used in route" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestReceiverHasName(t *testing.T) { in := ` route: receivers: - name: '' ` _, err := Load(in) expected := "missing name in receiver" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestMuteTimeExists(t *testing.T) { in := ` route: receiver: team-Y routes: - match: severity: critical mute_time_intervals: - business_hours receivers: - name: 'team-Y' ` _, err := Load(in) expected := "undefined time interval \"business_hours\" used in route" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestActiveTimeExists(t *testing.T) { in := ` route: receiver: team-Y routes: - match: severity: critical active_time_intervals: - business_hours receivers: - name: 'team-Y' ` _, err := Load(in) expected := "undefined time interval \"business_hours\" used in route" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestTimeIntervalHasName(t *testing.T) { in := ` time_intervals: - name: time_intervals: - times: - start_time: '09:00' end_time: '17:00' receivers: - name: 'team-X-mails' route: receiver: 'team-X-mails' routes: - match: severity: critical mute_time_intervals: - business_hours ` _, err := Load(in) expected := "missing name in time interval" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestMuteTimeNoDuplicates(t *testing.T) { in := ` mute_time_intervals: - name: duplicate time_intervals: - times: - start_time: '09:00' end_time: '17:00' - name: duplicate time_intervals: - times: - start_time: '10:00' end_time: '14:00' receivers: - name: 'team-X-mails' route: receiver: 'team-X-mails' routes: - match: severity: critical mute_time_intervals: - business_hours ` _, err := Load(in) expected := "mute time interval \"duplicate\" is not unique" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestGroupByHasNoDuplicatedLabels(t *testing.T) { in := ` route: group_by: ['alertname', 'cluster', 'service', 'cluster'] receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "duplicated label \"cluster\" in group_by" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestWildcardGroupByWithOtherGroupByLabels(t *testing.T) { in := ` route: group_by: ['alertname', 'cluster', '...'] receiver: team-X-mails receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "cannot have wildcard group_by (`...`) and other other labels at the same time" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestGroupByInvalidLabel(t *testing.T) { in := ` route: group_by: ['-invalid-'] receiver: team-X-mails receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "invalid label name \"-invalid-\" in group_by list" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestRootRouteExists(t *testing.T) { in := ` receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "no routes provided" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestRootRouteNoMuteTimes(t *testing.T) { in := ` mute_time_intervals: - name: my_mute_time time_intervals: - times: - start_time: '09:00' end_time: '17:00' receivers: - name: 'team-X-mails' route: receiver: 'team-X-mails' mute_time_intervals: - my_mute_time ` _, err := Load(in) expected := "root route must not have any mute time intervals" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestRootRouteNoActiveTimes(t *testing.T) { in := ` time_intervals: - name: my_active_time time_intervals: - times: - start_time: '09:00' end_time: '17:00' receivers: - name: 'team-X-mails' route: receiver: 'team-X-mails' active_time_intervals: - my_active_time ` _, err := Load(in) expected := "root route must not have any active time intervals" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestRootRouteHasNoMatcher(t *testing.T) { testCases := []struct { name string in string }{ { name: "Test deprecated matchers on root route not allowed", in: ` route: receiver: 'team-X' match: severity: critical receivers: - name: 'team-X' `, }, { name: "Test matchers not allowed on root route", in: ` route: receiver: 'team-X' matchers: - severity=critical receivers: - name: 'team-X' `, }, } expected := "root route must not have any matchers" for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { _, err := Load(tc.in) if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } }) } } func TestContinueErrorInRouteRoot(t *testing.T) { in := ` route: receiver: team-X-mails continue: true receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "cannot have continue in root route" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestGroupIntervalIsGreaterThanZero(t *testing.T) { in := ` route: receiver: team-X-mails group_interval: 0s receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "group_interval cannot be zero" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestRepeatIntervalIsGreaterThanZero(t *testing.T) { in := ` route: receiver: team-X-mails repeat_interval: 0s receivers: - name: 'team-X-mails' ` _, err := Load(in) expected := "repeat_interval cannot be zero" if err == nil { t.Fatalf("no error returned, expected:\n%q", expected) } if err.Error() != expected { t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) } } func TestHideConfigSecrets(t *testing.T) { c, err := LoadFile("testdata/conf.good.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.good.yml", err) } // String method must not reveal authentication credentials. s := c.String() if strings.Count(s, "") != 13 || strings.Contains(s, "mysecret") { t.Fatal("config's String method reveals authentication credentials.") } } func TestJSONMarshal(t *testing.T) { c, err := LoadFile("testdata/conf.good.yml") if err != nil { t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err) } _, err = json.Marshal(c) if err != nil { t.Fatal("JSON Marshaling failed:", err) } } func TestJSONMarshalSecret(t *testing.T) { test := struct { S Secret }{ S: Secret("test"), } c, err := json.Marshal(test) if err != nil { t.Fatal(err) } // u003c -> "<" // u003e -> ">" require.Equal(t, "{\"S\":\"\\u003csecret\\u003e\"}", string(c), "Secret not properly elided.") } func TestMarshalSecretURL(t *testing.T) { urlp, err := url.Parse("http://example.com/") if err != nil { t.Fatal(err) } u := &SecretURL{urlp} c, err := json.Marshal(u) if err != nil { t.Fatal(err) } // u003c -> "<" // u003e -> ">" require.Equal(t, "\"\\u003csecret\\u003e\"", string(c), "SecretURL not properly elided in JSON.") // Check that the marshaled data can be unmarshaled again. out := &SecretURL{} err = json.Unmarshal(c, out) if err != nil { t.Fatal(err) } c, err = yaml.Marshal(u) if err != nil { t.Fatal(err) } require.Equal(t, "\n", string(c), "SecretURL not properly elided in YAML.") // Check that the marshaled data can be unmarshaled again. out = &SecretURL{} err = yaml.Unmarshal(c, &out) if err != nil { t.Fatal(err) } } func TestUnmarshalSecretURL(t *testing.T) { b := []byte(`"http://example.com/se cret"`) var u SecretURL err := json.Unmarshal(b, &u) if err != nil { t.Fatal(err) } require.Equal(t, "http://example.com/se%20cret", u.String(), "SecretURL not properly unmarshaled in JSON.") err = yaml.Unmarshal(b, &u) if err != nil { t.Fatal(err) } require.Equal(t, "http://example.com/se%20cret", u.String(), "SecretURL not properly unmarshaled in YAML.") } func TestMarshalURL(t *testing.T) { for name, tc := range map[string]struct { input *URL expectedJSON string expectedYAML string }{ "url": { input: mustParseURL("http://example.com/"), expectedJSON: "\"http://example.com/\"", expectedYAML: "http://example.com/\n", }, "wrapped nil value": { input: &URL{}, expectedJSON: "null", expectedYAML: "null\n", }, "wrapped empty URL": { input: &URL{&url.URL{}}, expectedJSON: "\"\"", expectedYAML: "\"\"\n", }, } { t.Run(name, func(t *testing.T) { j, err := json.Marshal(tc.input) require.NoError(t, err) require.Equal(t, tc.expectedJSON, string(j), "URL not properly marshaled into JSON.") y, err := yaml.Marshal(tc.input) require.NoError(t, err) require.Equal(t, tc.expectedYAML, string(y), "URL not properly marshaled into YAML.") }) } } func TestUnmarshalNilURL(t *testing.T) { b := []byte(`null`) { var u URL err := json.Unmarshal(b, &u) require.Error(t, err, "unsupported scheme \"\" for URL") require.Nil(t, nil, u.URL) } { var u URL err := yaml.Unmarshal(b, &u) require.NoError(t, err) require.Nil(t, nil, u.URL) // UnmarshalYAML is not even called when unmarshalling "null". } } func TestUnmarshalEmptyURL(t *testing.T) { b := []byte(`""`) { var u URL err := json.Unmarshal(b, &u) require.Error(t, err, "unsupported scheme \"\" for URL") require.Equal(t, (*url.URL)(nil), u.URL) } { var u URL err := yaml.Unmarshal(b, &u) require.Error(t, err, "unsupported scheme \"\" for URL") require.Equal(t, (*url.URL)(nil), u.URL) } } func TestUnmarshalURL(t *testing.T) { b := []byte(`"http://example.com/a b"`) var u URL err := json.Unmarshal(b, &u) if err != nil { t.Fatal(err) } require.Equal(t, "http://example.com/a%20b", u.String(), "URL not properly unmarshaled in JSON.") err = yaml.Unmarshal(b, &u) if err != nil { t.Fatal(err) } require.Equal(t, "http://example.com/a%20b", u.String(), "URL not properly unmarshaled in YAML.") } func TestUnmarshalInvalidURL(t *testing.T) { for _, b := range [][]byte{ []byte(`"://example.com"`), []byte(`"http:example.com"`), []byte(`"telnet://example.com"`), } { var u URL err := json.Unmarshal(b, &u) if err == nil { t.Errorf("Expected an error unmarshaling %q from JSON", string(b)) } err = yaml.Unmarshal(b, &u) if err == nil { t.Errorf("Expected an error unmarshaling %q from YAML", string(b)) } t.Logf("%s", err) } } func TestUnmarshalRelativeURL(t *testing.T) { b := []byte(`"/home"`) var u URL err := json.Unmarshal(b, &u) if err == nil { t.Errorf("Expected an error parsing URL") } err = yaml.Unmarshal(b, &u) if err == nil { t.Errorf("Expected an error parsing URL") } } func TestMarshalRegexpWithNilValue(t *testing.T) { r := &Regexp{} out, err := json.Marshal(r) require.NoError(t, err) require.Equal(t, "null", string(out)) out, err = yaml.Marshal(r) require.NoError(t, err) require.Equal(t, "null\n", string(out)) } func TestUnmarshalEmptyRegexp(t *testing.T) { b := []byte(`""`) { var re Regexp err := json.Unmarshal(b, &re) require.NoError(t, err) require.Equal(t, regexp.MustCompile("^(?:)$"), re.Regexp) require.Equal(t, "", re.original) } { var re Regexp err := yaml.Unmarshal(b, &re) require.NoError(t, err) require.Equal(t, regexp.MustCompile("^(?:)$"), re.Regexp) require.Equal(t, "", re.original) } } func TestUnmarshalNullRegexp(t *testing.T) { input := []byte(`null`) { var re Regexp err := json.Unmarshal(input, &re) require.NoError(t, err) require.Nil(t, nil, re.Regexp) require.Equal(t, "", re.original) } { var re Regexp err := yaml.Unmarshal(input, &re) // Interestingly enough, unmarshalling `null` in YAML doesn't even call UnmarshalYAML. require.NoError(t, err) require.Nil(t, re.Regexp) require.Equal(t, "", re.original) } } func TestMarshalEmptyMatchers(t *testing.T) { r := Matchers{} out, err := json.Marshal(r) require.NoError(t, err) require.Equal(t, "[]", string(out)) out, err = yaml.Marshal(r) require.NoError(t, err) require.Equal(t, "[]\n", string(out)) } func TestJSONUnmarshal(t *testing.T) { c, err := LoadFile("testdata/conf.good.yml") if err != nil { t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err) } _, err = json.Marshal(c) if err != nil { t.Fatal("JSON Marshaling failed:", err) } } func TestMarshalIdempotency(t *testing.T) { c, err := LoadFile("testdata/conf.good.yml") if err != nil { t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err) } marshaled, err := yaml.Marshal(c) if err != nil { t.Fatal("YAML Marshaling failed:", err) } c = new(Config) if err := yaml.Unmarshal(marshaled, c); err != nil { t.Fatal("YAML Unmarshaling failed:", err) } } func TestGroupByAllNotMarshaled(t *testing.T) { in := ` route: receiver: team-X-mails group_by: [...] receivers: - name: 'team-X-mails' ` c, err := Load(in) if err != nil { t.Fatal("load failed:", err) } dat, err := yaml.Marshal(c) if err != nil { t.Fatal("YAML Marshaling failed:", err) } if strings.Contains(string(dat), "groupbyall") { t.Fatal("groupbyall found in config file") } } func TestEmptyFieldsAndRegex(t *testing.T) { boolFoo := true regexpFoo := Regexp{ Regexp: regexp.MustCompile("^(?:^(foo1|foo2|baz)$)$"), original: "^(foo1|foo2|baz)$", } expectedConf := Config{ Global: &GlobalConfig{ HTTPConfig: &commoncfg.HTTPClientConfig{ FollowRedirects: true, EnableHTTP2: true, }, ResolveTimeout: model.Duration(5 * time.Minute), SMTPSmarthost: HostPort{Host: "localhost", Port: "25"}, SMTPFrom: "alertmanager@example.org", SlackAPIURL: (*SecretURL)(mustParseURL("http://slack.example.com/")), SMTPRequireTLS: true, PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"), WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), TelegramAPIUrl: mustParseURL("https://api.telegram.org"), WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), }, Templates: []string{ "/etc/alertmanager/template/*.tmpl", }, Route: &Route{ Receiver: "team-X-mails", GroupBy: []model.LabelName{ "alertname", "cluster", "service", }, GroupByStr: []string{ "alertname", "cluster", "service", }, GroupByAll: false, Routes: []*Route{ { Receiver: "team-X-mails", MatchRE: map[string]Regexp{ "service": regexpFoo, }, }, }, }, Receivers: []Receiver{ { Name: "team-X-mails", EmailConfigs: []*EmailConfig{ { To: "team-X+alerts@example.org", From: "alertmanager@example.org", Smarthost: HostPort{Host: "localhost", Port: "25"}, HTML: "{{ template \"email.default.html\" . }}", RequireTLS: &boolFoo, }, }, }, }, } // Load a non-empty configuration to ensure that all fields are overwritten. // See https://github.com/prometheus/alertmanager/issues/1649. _, err := LoadFile("testdata/conf.good.yml") if err != nil { t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err) } config, err := LoadFile("testdata/conf.empty-fields.yml") if err != nil { t.Errorf("Error parsing %s: %s", "testdata/conf.empty-fields.yml", err) } configGot, err := yaml.Marshal(config) if err != nil { t.Fatal("YAML Marshaling failed:", err) } configExp, err := yaml.Marshal(expectedConf) if err != nil { t.Fatalf("%s", err) } if !reflect.DeepEqual(configGot, configExp) { t.Fatalf("%s: unexpected config result: \n\n%s\n expected\n\n%s", "testdata/conf.empty-fields.yml", configGot, configExp) } } func TestGlobalAndLocalHTTPConfig(t *testing.T) { config, err := LoadFile("testdata/conf.http-config.good.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf-http-config.good.yml", err) } if config.Global.HTTPConfig.FollowRedirects { t.Fatalf("global HTTP config should not follow redirects") } if !config.Receivers[0].SlackConfigs[0].HTTPConfig.FollowRedirects { t.Fatalf("global HTTP config should follow redirects") } } func TestSMTPHello(t *testing.T) { c, err := LoadFile("testdata/conf.good.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.good.yml", err) } const refValue = "host.example.org" hostName := c.Global.SMTPHello if hostName != refValue { t.Errorf("Invalid SMTP Hello hostname: %s\nExpected: %s", hostName, refValue) } } func TestSMTPBothPasswordAndFile(t *testing.T) { _, err := LoadFile("testdata/conf.smtp-both-password-and-file.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.smtp-both-password-and-file.yml", err) } if err.Error() != "at most one of smtp_auth_password & smtp_auth_password_file must be configured" { t.Errorf("Expected: %s\nGot: %s", "at most one of auth_password & auth_password_file must be configured", err.Error()) } } func TestSMTPNoUsernameOrPassword(t *testing.T) { _, err := LoadFile("testdata/conf.smtp-no-username-or-password.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.smtp-no-username-or-password.yml", err) } } func TestGlobalAndLocalSMTPPassword(t *testing.T) { config, err := LoadFile("testdata/conf.smtp-password-global-and-local.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.smtp-password-global-and-local.yml", err) } require.Equal(t, "/tmp/globaluserpassword", config.Receivers[0].EmailConfigs[0].AuthPasswordFile, "first email should use password file /tmp/globaluserpassword") require.Emptyf(t, config.Receivers[0].EmailConfigs[0].AuthPassword, "password field should be empty when file provided") require.Equal(t, "/tmp/localuser1password", config.Receivers[0].EmailConfigs[1].AuthPasswordFile, "second email should use password file /tmp/localuser1password") require.Emptyf(t, config.Receivers[0].EmailConfigs[1].AuthPassword, "password field should be empty when file provided") require.Equal(t, Secret("mysecret"), config.Receivers[0].EmailConfigs[2].AuthPassword, "third email should use password mysecret") require.Emptyf(t, config.Receivers[0].EmailConfigs[2].AuthPasswordFile, "file field should be empty when password provided") } func TestGroupByAll(t *testing.T) { c, err := LoadFile("testdata/conf.group-by-all.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.group-by-all.yml", err) } if !c.Route.GroupByAll { t.Errorf("Invalid group by all param: expected to by true") } } func TestVictorOpsDefaultAPIKey(t *testing.T) { conf, err := LoadFile("testdata/conf.victorops-default-apikey.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.victorops-default-apikey.yml", err) } defaultKey := conf.Global.VictorOpsAPIKey overrideKey := Secret("qwe456") if defaultKey != conf.Receivers[0].VictorOpsConfigs[0].APIKey { t.Fatalf("Invalid victorops key: %s\nExpected: %s", conf.Receivers[0].VictorOpsConfigs[0].APIKey, defaultKey) } if overrideKey != conf.Receivers[1].VictorOpsConfigs[0].APIKey { t.Errorf("Invalid victorops key: %s\nExpected: %s", conf.Receivers[0].VictorOpsConfigs[0].APIKey, string(overrideKey)) } } func TestVictorOpsDefaultAPIKeyFile(t *testing.T) { conf, err := LoadFile("testdata/conf.victorops-default-apikey-file.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.victorops-default-apikey-file.yml", err) } defaultKey := conf.Global.VictorOpsAPIKeyFile overrideKey := "/override_file" if defaultKey != conf.Receivers[0].VictorOpsConfigs[0].APIKeyFile { t.Fatalf("Invalid VictorOps key_file: %s\nExpected: %s", conf.Receivers[0].VictorOpsConfigs[0].APIKeyFile, defaultKey) } if overrideKey != conf.Receivers[1].VictorOpsConfigs[0].APIKeyFile { t.Errorf("Invalid VictorOps key_file: %s\nExpected: %s", conf.Receivers[0].VictorOpsConfigs[0].APIKeyFile, overrideKey) } } func TestVictorOpsBothAPIKeyAndFile(t *testing.T) { _, err := LoadFile("testdata/conf.victorops-both-file-and-apikey.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.victorops-both-file-and-apikey.yml", err) } if err.Error() != "at most one of victorops_api_key & victorops_api_key_file must be configured" { t.Errorf("Expected: %s\nGot: %s", "at most one of victorops_api_key & victorops_api_key_file must be configured", err.Error()) } } func TestVictorOpsNoAPIKey(t *testing.T) { _, err := LoadFile("testdata/conf.victorops-no-apikey.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.victorops-no-apikey.yml", err) } if err.Error() != "no global VictorOps API Key set" { t.Errorf("Expected: %s\nGot: %s", "no global VictorOps API Key set", err.Error()) } } func TestOpsGenieDefaultAPIKey(t *testing.T) { conf, err := LoadFile("testdata/conf.opsgenie-default-apikey.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.opsgenie-default-apikey.yml", err) } defaultKey := conf.Global.OpsGenieAPIKey if defaultKey != conf.Receivers[0].OpsGenieConfigs[0].APIKey { t.Fatalf("Invalid OpsGenie key: %s\nExpected: %s", conf.Receivers[0].OpsGenieConfigs[0].APIKey, defaultKey) } if defaultKey == conf.Receivers[1].OpsGenieConfigs[0].APIKey { t.Errorf("Invalid OpsGenie key: %s\nExpected: %s", conf.Receivers[0].OpsGenieConfigs[0].APIKey, "qwe456") } } func TestOpsGenieDefaultAPIKeyFile(t *testing.T) { conf, err := LoadFile("testdata/conf.opsgenie-default-apikey-file.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.opsgenie-default-apikey-file.yml", err) } defaultKey := conf.Global.OpsGenieAPIKeyFile if defaultKey != conf.Receivers[0].OpsGenieConfigs[0].APIKeyFile { t.Fatalf("Invalid OpsGenie key_file: %s\nExpected: %s", conf.Receivers[0].OpsGenieConfigs[0].APIKeyFile, defaultKey) } if defaultKey == conf.Receivers[1].OpsGenieConfigs[0].APIKeyFile { t.Errorf("Invalid OpsGenie key_file: %s\nExpected: %s", conf.Receivers[0].OpsGenieConfigs[0].APIKeyFile, "/override_file") } } func TestOpsGenieBothAPIKeyAndFile(t *testing.T) { _, err := LoadFile("testdata/conf.opsgenie-both-file-and-apikey.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.opsgenie-both-file-and-apikey.yml", err) } if err.Error() != "at most one of opsgenie_api_key & opsgenie_api_key_file must be configured" { t.Errorf("Expected: %s\nGot: %s", "at most one of opsgenie_api_key & opsgenie_api_key_file must be configured", err.Error()) } } func TestOpsGenieNoAPIKey(t *testing.T) { _, err := LoadFile("testdata/conf.opsgenie-no-apikey.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.opsgenie-no-apikey.yml", err) } if err.Error() != "no global OpsGenie API Key set either inline or in a file" { t.Errorf("Expected: %s\nGot: %s", "no global OpsGenie API Key set either inline or in a file", err.Error()) } } func TestOpsGenieDeprecatedTeamSpecified(t *testing.T) { _, err := LoadFile("testdata/conf.opsgenie-default-apikey-old-team.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.opsgenie-default-apikey-old-team.yml", err) } const expectedErr = `yaml: unmarshal errors: line 16: field teams not found in type config.plain` if err.Error() != expectedErr { t.Errorf("Expected: %s\nGot: %s", expectedErr, err.Error()) } } func TestSlackBothAPIURLAndFile(t *testing.T) { _, err := LoadFile("testdata/conf.slack-both-file-and-url.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.slack-both-file-and-url.yml", err) } if err.Error() != "at most one of slack_api_url & slack_api_url_file must be configured" { t.Errorf("Expected: %s\nGot: %s", "at most one of slack_api_url & slack_api_url_file must be configured", err.Error()) } } func TestSlackNoAPIURL(t *testing.T) { _, err := LoadFile("testdata/conf.slack-no-api-url.yml") if err == nil { t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.slack-no-api-url.yml", err) } if err.Error() != "no global Slack API URL set either inline or in a file" { t.Errorf("Expected: %s\nGot: %s", "no global Slack API URL set either inline or in a file", err.Error()) } } func TestSlackGlobalAPIURLFile(t *testing.T) { conf, err := LoadFile("testdata/conf.slack-default-api-url-file.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.slack-default-api-url-file.yml", err) } // no override firstConfig := conf.Receivers[0].SlackConfigs[0] if firstConfig.APIURLFile != "/global_file" || firstConfig.APIURL != nil { t.Fatalf("Invalid Slack URL file: %s\nExpected: %s", firstConfig.APIURLFile, "/global_file") } // override the file secondConfig := conf.Receivers[0].SlackConfigs[1] if secondConfig.APIURLFile != "/override_file" || secondConfig.APIURL != nil { t.Fatalf("Invalid Slack URL file: %s\nExpected: %s", secondConfig.APIURLFile, "/override_file") } // override the global file with an inline URL thirdConfig := conf.Receivers[0].SlackConfigs[2] if thirdConfig.APIURL.String() != "http://mysecret.example.com/" || thirdConfig.APIURLFile != "" { t.Fatalf("Invalid Slack URL: %s\nExpected: %s", thirdConfig.APIURL.String(), "http://mysecret.example.com/") } } func TestValidSNSConfig(t *testing.T) { _, err := LoadFile("testdata/conf.sns-topic-arn.yml") if err != nil { t.Fatalf("Error parsing %s: %s", "testdata/conf.sns-topic-arn.yml\"", err) } } func TestInvalidSNSConfig(t *testing.T) { _, err := LoadFile("testdata/conf.sns-invalid.yml") if err == nil { t.Fatalf("expected error with missing fields on SNS config") } const expectedErr = `must provide either a Target ARN, Topic ARN, or Phone Number for SNS config` if err.Error() != expectedErr { t.Errorf("Expected: %s\nGot: %s", expectedErr, err.Error()) } } func TestUnmarshalHostPort(t *testing.T) { for _, tc := range []struct { in string exp HostPort jsonOut string yamlOut string err bool }{ { in: `""`, exp: HostPort{}, yamlOut: `"" `, jsonOut: `""`, }, { in: `"localhost:25"`, exp: HostPort{Host: "localhost", Port: "25"}, yamlOut: `localhost:25 `, jsonOut: `"localhost:25"`, }, { in: `":25"`, exp: HostPort{Host: "", Port: "25"}, yamlOut: `:25 `, jsonOut: `":25"`, }, { in: `"localhost"`, err: true, }, { in: `"localhost:"`, err: true, }, } { tc := tc t.Run(tc.in, func(t *testing.T) { hp := HostPort{} err := yaml.Unmarshal([]byte(tc.in), &hp) if tc.err { require.Error(t, err) return } require.NoError(t, err) require.Equal(t, tc.exp, hp) b, err := yaml.Marshal(&hp) require.NoError(t, err) require.Equal(t, tc.yamlOut, string(b)) b, err = json.Marshal(&hp) require.NoError(t, err) require.Equal(t, tc.jsonOut, string(b)) }) } } func TestNilRegexp(t *testing.T) { for _, tc := range []struct { file string errMsg string }{ { file: "testdata/conf.nil-match_re-route.yml", errMsg: "invalid_label", }, { file: "testdata/conf.nil-source_match_re-inhibition.yml", errMsg: "invalid_source_label", }, { file: "testdata/conf.nil-target_match_re-inhibition.yml", errMsg: "invalid_target_label", }, } { t.Run(tc.file, func(t *testing.T) { _, err := os.Stat(tc.file) require.NoError(t, err) _, err = LoadFile(tc.file) require.Error(t, err) require.Contains(t, err.Error(), tc.errMsg) }) } }