1
2
3
4
5
6
7
8
9
10
11
12
13
14 package config
15
16 import (
17 "fmt"
18 "net/textproto"
19 "regexp"
20 "strings"
21 "text/template"
22 "time"
23
24 "github.com/pkg/errors"
25 commoncfg "github.com/prometheus/common/config"
26 "github.com/prometheus/common/sigv4"
27 )
28
29 var (
30
31 DefaultWebhookConfig = WebhookConfig{
32 NotifierConfig: NotifierConfig{
33 VSendResolved: true,
34 },
35 }
36
37
38 DefaultWebexConfig = WebexConfig{
39 NotifierConfig: NotifierConfig{
40 VSendResolved: true,
41 },
42 Message: `{{ template "webex.default.message" . }}`,
43 }
44
45
46 DefaultDiscordConfig = DiscordConfig{
47 NotifierConfig: NotifierConfig{
48 VSendResolved: true,
49 },
50 Title: `{{ template "discord.default.title" . }}`,
51 Message: `{{ template "discord.default.message" . }}`,
52 }
53
54
55 DefaultEmailConfig = EmailConfig{
56 NotifierConfig: NotifierConfig{
57 VSendResolved: false,
58 },
59 HTML: `{{ template "email.default.html" . }}`,
60 Text: ``,
61 }
62
63
64 DefaultEmailSubject = `{{ template "email.default.subject" . }}`
65
66
67 DefaultPagerdutyDetails = map[string]string{
68 "firing": `{{ template "pagerduty.default.instances" .Alerts.Firing }}`,
69 "resolved": `{{ template "pagerduty.default.instances" .Alerts.Resolved }}`,
70 "num_firing": `{{ .Alerts.Firing | len }}`,
71 "num_resolved": `{{ .Alerts.Resolved | len }}`,
72 }
73
74
75 DefaultPagerdutyConfig = PagerdutyConfig{
76 NotifierConfig: NotifierConfig{
77 VSendResolved: true,
78 },
79 Description: `{{ template "pagerduty.default.description" .}}`,
80 Client: `{{ template "pagerduty.default.client" . }}`,
81 ClientURL: `{{ template "pagerduty.default.clientURL" . }}`,
82 }
83
84
85 DefaultSlackConfig = SlackConfig{
86 NotifierConfig: NotifierConfig{
87 VSendResolved: false,
88 },
89 Color: `{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}`,
90 Username: `{{ template "slack.default.username" . }}`,
91 Title: `{{ template "slack.default.title" . }}`,
92 TitleLink: `{{ template "slack.default.titlelink" . }}`,
93 IconEmoji: `{{ template "slack.default.iconemoji" . }}`,
94 IconURL: `{{ template "slack.default.iconurl" . }}`,
95 Pretext: `{{ template "slack.default.pretext" . }}`,
96 Text: `{{ template "slack.default.text" . }}`,
97 Fallback: `{{ template "slack.default.fallback" . }}`,
98 CallbackID: `{{ template "slack.default.callbackid" . }}`,
99 Footer: `{{ template "slack.default.footer" . }}`,
100 }
101
102
103 DefaultOpsGenieConfig = OpsGenieConfig{
104 NotifierConfig: NotifierConfig{
105 VSendResolved: true,
106 },
107 Message: `{{ template "opsgenie.default.message" . }}`,
108 Description: `{{ template "opsgenie.default.description" . }}`,
109 Source: `{{ template "opsgenie.default.source" . }}`,
110
111 }
112
113
114 DefaultWechatConfig = WechatConfig{
115 NotifierConfig: NotifierConfig{
116 VSendResolved: false,
117 },
118 Message: `{{ template "wechat.default.message" . }}`,
119 ToUser: `{{ template "wechat.default.to_user" . }}`,
120 ToParty: `{{ template "wechat.default.to_party" . }}`,
121 ToTag: `{{ template "wechat.default.to_tag" . }}`,
122 AgentID: `{{ template "wechat.default.agent_id" . }}`,
123 }
124
125
126 DefaultVictorOpsConfig = VictorOpsConfig{
127 NotifierConfig: NotifierConfig{
128 VSendResolved: true,
129 },
130 MessageType: `CRITICAL`,
131 StateMessage: `{{ template "victorops.default.state_message" . }}`,
132 EntityDisplayName: `{{ template "victorops.default.entity_display_name" . }}`,
133 MonitoringTool: `{{ template "victorops.default.monitoring_tool" . }}`,
134 }
135
136
137 DefaultPushoverConfig = PushoverConfig{
138 NotifierConfig: NotifierConfig{
139 VSendResolved: true,
140 },
141 Title: `{{ template "pushover.default.title" . }}`,
142 Message: `{{ template "pushover.default.message" . }}`,
143 URL: `{{ template "pushover.default.url" . }}`,
144 Priority: `{{ if eq .Status "firing" }}2{{ else }}0{{ end }}`,
145 Retry: duration(1 * time.Minute),
146 Expire: duration(1 * time.Hour),
147 HTML: false,
148 }
149
150
151 DefaultSNSConfig = SNSConfig{
152 NotifierConfig: NotifierConfig{
153 VSendResolved: true,
154 },
155 Subject: `{{ template "sns.default.subject" . }}`,
156 Message: `{{ template "sns.default.message" . }}`,
157 }
158
159 DefaultTelegramConfig = TelegramConfig{
160 NotifierConfig: NotifierConfig{
161 VSendResolved: true,
162 },
163 DisableNotifications: false,
164 Message: `{{ template "telegram.default.message" . }}`,
165 ParseMode: "HTML",
166 }
167 )
168
169
170 type NotifierConfig struct {
171 VSendResolved bool `yaml:"send_resolved" json:"send_resolved"`
172 }
173
174 func (nc *NotifierConfig) SendResolved() bool {
175 return nc.VSendResolved
176 }
177
178
179 type WebexConfig struct {
180 NotifierConfig `yaml:",inline" json:",inline"`
181 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
182 APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
183
184 Message string `yaml:"message,omitempty" json:"message,omitempty"`
185 RoomID string `yaml:"room_id" json:"room_id"`
186 }
187
188
189 func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
190 *c = DefaultWebexConfig
191 type plain WebexConfig
192 if err := unmarshal((*plain)(c)); err != nil {
193 return err
194 }
195
196 if c.RoomID == "" {
197 return fmt.Errorf("missing room_id on webex_config")
198 }
199
200 if c.HTTPConfig == nil || c.HTTPConfig.Authorization == nil {
201 return fmt.Errorf("missing webex_configs.http_config.authorization")
202 }
203
204 return nil
205 }
206
207
208 type DiscordConfig struct {
209 NotifierConfig `yaml:",inline" json:",inline"`
210
211 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
212 WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"`
213
214 Title string `yaml:"title,omitempty" json:"title,omitempty"`
215 Message string `yaml:"message,omitempty" json:"message,omitempty"`
216 }
217
218
219 func (c *DiscordConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
220 *c = DefaultDiscordConfig
221 type plain DiscordConfig
222 return unmarshal((*plain)(c))
223 }
224
225
226 type EmailConfig struct {
227 NotifierConfig `yaml:",inline" json:",inline"`
228
229
230 To string `yaml:"to,omitempty" json:"to,omitempty"`
231 From string `yaml:"from,omitempty" json:"from,omitempty"`
232 Hello string `yaml:"hello,omitempty" json:"hello,omitempty"`
233 Smarthost HostPort `yaml:"smarthost,omitempty" json:"smarthost,omitempty"`
234 AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"`
235 AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"`
236 AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"`
237 AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"`
238 AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"`
239 Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
240 HTML string `yaml:"html,omitempty" json:"html,omitempty"`
241 Text string `yaml:"text,omitempty" json:"text,omitempty"`
242 RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"`
243 TLSConfig commoncfg.TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"`
244 }
245
246
247 func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
248 *c = DefaultEmailConfig
249 type plain EmailConfig
250 if err := unmarshal((*plain)(c)); err != nil {
251 return err
252 }
253 if c.To == "" {
254 return fmt.Errorf("missing to address in email config")
255 }
256
257 normalizedHeaders := map[string]string{}
258 for h, v := range c.Headers {
259 normalized := textproto.CanonicalMIMEHeaderKey(h)
260 if _, ok := normalizedHeaders[normalized]; ok {
261 return fmt.Errorf("duplicate header %q in email config", normalized)
262 }
263 normalizedHeaders[normalized] = v
264 }
265 c.Headers = normalizedHeaders
266
267 return nil
268 }
269
270
271 type PagerdutyConfig struct {
272 NotifierConfig `yaml:",inline" json:",inline"`
273
274 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
275
276 ServiceKey Secret `yaml:"service_key,omitempty" json:"service_key,omitempty"`
277 ServiceKeyFile string `yaml:"service_key_file,omitempty" json:"service_key_file,omitempty"`
278 RoutingKey Secret `yaml:"routing_key,omitempty" json:"routing_key,omitempty"`
279 RoutingKeyFile string `yaml:"routing_key_file,omitempty" json:"routing_key_file,omitempty"`
280 URL *URL `yaml:"url,omitempty" json:"url,omitempty"`
281 Client string `yaml:"client,omitempty" json:"client,omitempty"`
282 ClientURL string `yaml:"client_url,omitempty" json:"client_url,omitempty"`
283 Description string `yaml:"description,omitempty" json:"description,omitempty"`
284 Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
285 Images []PagerdutyImage `yaml:"images,omitempty" json:"images,omitempty"`
286 Links []PagerdutyLink `yaml:"links,omitempty" json:"links,omitempty"`
287 Source string `yaml:"source,omitempty" json:"source,omitempty"`
288 Severity string `yaml:"severity,omitempty" json:"severity,omitempty"`
289 Class string `yaml:"class,omitempty" json:"class,omitempty"`
290 Component string `yaml:"component,omitempty" json:"component,omitempty"`
291 Group string `yaml:"group,omitempty" json:"group,omitempty"`
292 }
293
294
295 type PagerdutyLink struct {
296 Href string `yaml:"href,omitempty" json:"href,omitempty"`
297 Text string `yaml:"text,omitempty" json:"text,omitempty"`
298 }
299
300
301 type PagerdutyImage struct {
302 Src string `yaml:"src,omitempty" json:"src,omitempty"`
303 Alt string `yaml:"alt,omitempty" json:"alt,omitempty"`
304 Href string `yaml:"href,omitempty" json:"href,omitempty"`
305 }
306
307
308 func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
309 *c = DefaultPagerdutyConfig
310 type plain PagerdutyConfig
311 if err := unmarshal((*plain)(c)); err != nil {
312 return err
313 }
314 if c.RoutingKey == "" && c.ServiceKey == "" && c.RoutingKeyFile == "" && c.ServiceKeyFile == "" {
315 return fmt.Errorf("missing service or routing key in PagerDuty config")
316 }
317 if len(c.RoutingKey) > 0 && len(c.RoutingKeyFile) > 0 {
318 return fmt.Errorf("at most one of routing_key & routing_key_file must be configured")
319 }
320 if len(c.ServiceKey) > 0 && len(c.ServiceKeyFile) > 0 {
321 return fmt.Errorf("at most one of service_key & service_key_file must be configured")
322 }
323 if c.Details == nil {
324 c.Details = make(map[string]string)
325 }
326 if c.Source == "" {
327 c.Source = c.Client
328 }
329 for k, v := range DefaultPagerdutyDetails {
330 if _, ok := c.Details[k]; !ok {
331 c.Details[k] = v
332 }
333 }
334 return nil
335 }
336
337
338
339
340 type SlackAction struct {
341 Type string `yaml:"type,omitempty" json:"type,omitempty"`
342 Text string `yaml:"text,omitempty" json:"text,omitempty"`
343 URL string `yaml:"url,omitempty" json:"url,omitempty"`
344 Style string `yaml:"style,omitempty" json:"style,omitempty"`
345 Name string `yaml:"name,omitempty" json:"name,omitempty"`
346 Value string `yaml:"value,omitempty" json:"value,omitempty"`
347 ConfirmField *SlackConfirmationField `yaml:"confirm,omitempty" json:"confirm,omitempty"`
348 }
349
350
351 func (c *SlackAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
352 type plain SlackAction
353 if err := unmarshal((*plain)(c)); err != nil {
354 return err
355 }
356 if c.Type == "" {
357 return fmt.Errorf("missing type in Slack action configuration")
358 }
359 if c.Text == "" {
360 return fmt.Errorf("missing text in Slack action configuration")
361 }
362 if c.URL != "" {
363
364 c.Name = ""
365 c.Value = ""
366 c.ConfirmField = nil
367 } else if c.Name != "" {
368 c.URL = ""
369 } else {
370 return fmt.Errorf("missing name or url in Slack action configuration")
371 }
372 return nil
373 }
374
375
376
377
378 type SlackConfirmationField struct {
379 Text string `yaml:"text,omitempty" json:"text,omitempty"`
380 Title string `yaml:"title,omitempty" json:"title,omitempty"`
381 OkText string `yaml:"ok_text,omitempty" json:"ok_text,omitempty"`
382 DismissText string `yaml:"dismiss_text,omitempty" json:"dismiss_text,omitempty"`
383 }
384
385
386 func (c *SlackConfirmationField) UnmarshalYAML(unmarshal func(interface{}) error) error {
387 type plain SlackConfirmationField
388 if err := unmarshal((*plain)(c)); err != nil {
389 return err
390 }
391 if c.Text == "" {
392 return fmt.Errorf("missing text in Slack confirmation configuration")
393 }
394 return nil
395 }
396
397
398
399
400
401 type SlackField struct {
402 Title string `yaml:"title,omitempty" json:"title,omitempty"`
403 Value string `yaml:"value,omitempty" json:"value,omitempty"`
404 Short *bool `yaml:"short,omitempty" json:"short,omitempty"`
405 }
406
407
408 func (c *SlackField) UnmarshalYAML(unmarshal func(interface{}) error) error {
409 type plain SlackField
410 if err := unmarshal((*plain)(c)); err != nil {
411 return err
412 }
413 if c.Title == "" {
414 return fmt.Errorf("missing title in Slack field configuration")
415 }
416 if c.Value == "" {
417 return fmt.Errorf("missing value in Slack field configuration")
418 }
419 return nil
420 }
421
422
423 type SlackConfig struct {
424 NotifierConfig `yaml:",inline" json:",inline"`
425
426 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
427
428 APIURL *SecretURL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
429 APIURLFile string `yaml:"api_url_file,omitempty" json:"api_url_file,omitempty"`
430
431
432 Channel string `yaml:"channel,omitempty" json:"channel,omitempty"`
433 Username string `yaml:"username,omitempty" json:"username,omitempty"`
434 Color string `yaml:"color,omitempty" json:"color,omitempty"`
435
436 Title string `yaml:"title,omitempty" json:"title,omitempty"`
437 TitleLink string `yaml:"title_link,omitempty" json:"title_link,omitempty"`
438 Pretext string `yaml:"pretext,omitempty" json:"pretext,omitempty"`
439 Text string `yaml:"text,omitempty" json:"text,omitempty"`
440 Fields []*SlackField `yaml:"fields,omitempty" json:"fields,omitempty"`
441 ShortFields bool `yaml:"short_fields" json:"short_fields,omitempty"`
442 Footer string `yaml:"footer,omitempty" json:"footer,omitempty"`
443 Fallback string `yaml:"fallback,omitempty" json:"fallback,omitempty"`
444 CallbackID string `yaml:"callback_id,omitempty" json:"callback_id,omitempty"`
445 IconEmoji string `yaml:"icon_emoji,omitempty" json:"icon_emoji,omitempty"`
446 IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"`
447 ImageURL string `yaml:"image_url,omitempty" json:"image_url,omitempty"`
448 ThumbURL string `yaml:"thumb_url,omitempty" json:"thumb_url,omitempty"`
449 LinkNames bool `yaml:"link_names" json:"link_names,omitempty"`
450 MrkdwnIn []string `yaml:"mrkdwn_in,omitempty" json:"mrkdwn_in,omitempty"`
451 Actions []*SlackAction `yaml:"actions,omitempty" json:"actions,omitempty"`
452 }
453
454
455 func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
456 *c = DefaultSlackConfig
457 type plain SlackConfig
458 if err := unmarshal((*plain)(c)); err != nil {
459 return err
460 }
461
462 if c.APIURL != nil && len(c.APIURLFile) > 0 {
463 return fmt.Errorf("at most one of api_url & api_url_file must be configured")
464 }
465
466 return nil
467 }
468
469
470 type WebhookConfig struct {
471 NotifierConfig `yaml:",inline" json:",inline"`
472
473 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
474
475
476 URL *URL `yaml:"url" json:"url"`
477
478
479
480 MaxAlerts uint64 `yaml:"max_alerts" json:"max_alerts"`
481 }
482
483
484 func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
485 *c = DefaultWebhookConfig
486 type plain WebhookConfig
487 if err := unmarshal((*plain)(c)); err != nil {
488 return err
489 }
490 if c.URL == nil {
491 return fmt.Errorf("missing URL in webhook config")
492 }
493 if c.URL.Scheme != "https" && c.URL.Scheme != "http" {
494 return fmt.Errorf("scheme required for webhook url")
495 }
496 return nil
497 }
498
499
500 type WechatConfig struct {
501 NotifierConfig `yaml:",inline" json:",inline"`
502
503 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
504
505 APISecret Secret `yaml:"api_secret,omitempty" json:"api_secret,omitempty"`
506 CorpID string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"`
507 Message string `yaml:"message,omitempty" json:"message,omitempty"`
508 APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
509 ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"`
510 ToParty string `yaml:"to_party,omitempty" json:"to_party,omitempty"`
511 ToTag string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"`
512 AgentID string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"`
513 MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"`
514 }
515
516 const wechatValidTypesRe = `^(text|markdown)$`
517
518 var wechatTypeMatcher = regexp.MustCompile(wechatValidTypesRe)
519
520
521 func (c *WechatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
522 *c = DefaultWechatConfig
523 type plain WechatConfig
524 if err := unmarshal((*plain)(c)); err != nil {
525 return err
526 }
527
528 if c.MessageType == "" {
529 c.MessageType = "text"
530 }
531
532 if !wechatTypeMatcher.MatchString(c.MessageType) {
533 return errors.Errorf("weChat message type %q does not match valid options %s", c.MessageType, wechatValidTypesRe)
534 }
535
536 return nil
537 }
538
539
540 type OpsGenieConfig struct {
541 NotifierConfig `yaml:",inline" json:",inline"`
542
543 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
544
545 APIKey Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"`
546 APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"`
547 APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
548 Message string `yaml:"message,omitempty" json:"message,omitempty"`
549 Description string `yaml:"description,omitempty" json:"description,omitempty"`
550 Source string `yaml:"source,omitempty" json:"source,omitempty"`
551 Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
552 Entity string `yaml:"entity,omitempty" json:"entity,omitempty"`
553 Responders []OpsGenieConfigResponder `yaml:"responders,omitempty" json:"responders,omitempty"`
554 Actions string `yaml:"actions,omitempty" json:"actions,omitempty"`
555 Tags string `yaml:"tags,omitempty" json:"tags,omitempty"`
556 Note string `yaml:"note,omitempty" json:"note,omitempty"`
557 Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
558 UpdateAlerts bool `yaml:"update_alerts,omitempty" json:"update_alerts,omitempty"`
559 }
560
561 const opsgenieValidTypesRe = `^(team|teams|user|escalation|schedule)$`
562
563 var opsgenieTypeMatcher = regexp.MustCompile(opsgenieValidTypesRe)
564
565
566 func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
567 *c = DefaultOpsGenieConfig
568 type plain OpsGenieConfig
569 if err := unmarshal((*plain)(c)); err != nil {
570 return err
571 }
572
573 if c.APIKey != "" && len(c.APIKeyFile) > 0 {
574 return fmt.Errorf("at most one of api_key & api_key_file must be configured")
575 }
576
577 for _, r := range c.Responders {
578 if r.ID == "" && r.Username == "" && r.Name == "" {
579 return errors.Errorf("opsGenieConfig responder %v has to have at least one of id, username or name specified", r)
580 }
581
582 if strings.Contains(r.Type, "{{") {
583 _, err := template.New("").Parse(r.Type)
584 if err != nil {
585 return errors.Errorf("opsGenieConfig responder %v type is not a valid template: %v", r, err)
586 }
587 } else {
588 r.Type = strings.ToLower(r.Type)
589 if !opsgenieTypeMatcher.MatchString(r.Type) {
590 return errors.Errorf("opsGenieConfig responder %v type does not match valid options %s", r, opsgenieValidTypesRe)
591 }
592 }
593 }
594
595 return nil
596 }
597
598 type OpsGenieConfigResponder struct {
599
600 ID string `yaml:"id,omitempty" json:"id,omitempty"`
601 Name string `yaml:"name,omitempty" json:"name,omitempty"`
602 Username string `yaml:"username,omitempty" json:"username,omitempty"`
603
604
605 Type string `yaml:"type,omitempty" json:"type,omitempty"`
606 }
607
608
609 type VictorOpsConfig struct {
610 NotifierConfig `yaml:",inline" json:",inline"`
611
612 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
613
614 APIKey Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"`
615 APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"`
616 APIURL *URL `yaml:"api_url" json:"api_url"`
617 RoutingKey string `yaml:"routing_key" json:"routing_key"`
618 MessageType string `yaml:"message_type" json:"message_type"`
619 StateMessage string `yaml:"state_message" json:"state_message"`
620 EntityDisplayName string `yaml:"entity_display_name" json:"entity_display_name"`
621 MonitoringTool string `yaml:"monitoring_tool" json:"monitoring_tool"`
622 CustomFields map[string]string `yaml:"custom_fields,omitempty" json:"custom_fields,omitempty"`
623 }
624
625
626 func (c *VictorOpsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
627 *c = DefaultVictorOpsConfig
628 type plain VictorOpsConfig
629 if err := unmarshal((*plain)(c)); err != nil {
630 return err
631 }
632 if c.RoutingKey == "" {
633 return fmt.Errorf("missing Routing key in VictorOps config")
634 }
635 if c.APIKey != "" && len(c.APIKeyFile) > 0 {
636 return fmt.Errorf("at most one of api_key & api_key_file must be configured")
637 }
638
639 reservedFields := []string{"routing_key", "message_type", "state_message", "entity_display_name", "monitoring_tool", "entity_id", "entity_state"}
640
641 for _, v := range reservedFields {
642 if _, ok := c.CustomFields[v]; ok {
643 return fmt.Errorf("victorOps config contains custom field %s which cannot be used as it conflicts with the fixed/static fields", v)
644 }
645 }
646
647 return nil
648 }
649
650 type duration time.Duration
651
652 func (d *duration) UnmarshalText(text []byte) error {
653 parsed, err := time.ParseDuration(string(text))
654 if err == nil {
655 *d = duration(parsed)
656 }
657 return err
658 }
659
660 func (d duration) MarshalText() ([]byte, error) {
661 return []byte(time.Duration(d).String()), nil
662 }
663
664 type PushoverConfig struct {
665 NotifierConfig `yaml:",inline" json:",inline"`
666
667 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
668
669 UserKey Secret `yaml:"user_key,omitempty" json:"user_key,omitempty"`
670 Token Secret `yaml:"token,omitempty" json:"token,omitempty"`
671 Title string `yaml:"title,omitempty" json:"title,omitempty"`
672 Message string `yaml:"message,omitempty" json:"message,omitempty"`
673 URL string `yaml:"url,omitempty" json:"url,omitempty"`
674 URLTitle string `yaml:"url_title,omitempty" json:"url_title,omitempty"`
675 Sound string `yaml:"sound,omitempty" json:"sound,omitempty"`
676 Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
677 Retry duration `yaml:"retry,omitempty" json:"retry,omitempty"`
678 Expire duration `yaml:"expire,omitempty" json:"expire,omitempty"`
679 HTML bool `yaml:"html" json:"html,omitempty"`
680 }
681
682
683 func (c *PushoverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
684 *c = DefaultPushoverConfig
685 type plain PushoverConfig
686 if err := unmarshal((*plain)(c)); err != nil {
687 return err
688 }
689 if c.UserKey == "" {
690 return fmt.Errorf("missing user key in Pushover config")
691 }
692 if c.Token == "" {
693 return fmt.Errorf("missing token in Pushover config")
694 }
695 return nil
696 }
697
698 type SNSConfig struct {
699 NotifierConfig `yaml:",inline" json:",inline"`
700
701 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
702
703 APIUrl string `yaml:"api_url,omitempty" json:"api_url,omitempty"`
704 Sigv4 sigv4.SigV4Config `yaml:"sigv4" json:"sigv4"`
705 TopicARN string `yaml:"topic_arn,omitempty" json:"topic_arn,omitempty"`
706 PhoneNumber string `yaml:"phone_number,omitempty" json:"phone_number,omitempty"`
707 TargetARN string `yaml:"target_arn,omitempty" json:"target_arn,omitempty"`
708 Subject string `yaml:"subject,omitempty" json:"subject,omitempty"`
709 Message string `yaml:"message,omitempty" json:"message,omitempty"`
710 Attributes map[string]string `yaml:"attributes,omitempty" json:"attributes,omitempty"`
711 }
712
713
714 func (c *SNSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
715 *c = DefaultSNSConfig
716 type plain SNSConfig
717 if err := unmarshal((*plain)(c)); err != nil {
718 return err
719 }
720 if (c.TargetARN == "") != (c.TopicARN == "") != (c.PhoneNumber == "") {
721 return fmt.Errorf("must provide either a Target ARN, Topic ARN, or Phone Number for SNS config")
722 }
723 return nil
724 }
725
726
727 type TelegramConfig struct {
728 NotifierConfig `yaml:",inline" json:",inline"`
729
730 HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
731
732 APIUrl *URL `yaml:"api_url" json:"api_url,omitempty"`
733 BotToken Secret `yaml:"bot_token,omitempty" json:"token,omitempty"`
734 ChatID int64 `yaml:"chat_id,omitempty" json:"chat,omitempty"`
735 Message string `yaml:"message,omitempty" json:"message,omitempty"`
736 DisableNotifications bool `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"`
737 ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"`
738 }
739
740
741 func (c *TelegramConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
742 *c = DefaultTelegramConfig
743 type plain TelegramConfig
744 if err := unmarshal((*plain)(c)); err != nil {
745 return err
746 }
747 if c.BotToken == "" {
748 return fmt.Errorf("missing bot_token on telegram_config")
749 }
750 if c.ChatID == 0 {
751 return fmt.Errorf("missing chat_id on telegram_config")
752 }
753 if c.ParseMode != "" &&
754 c.ParseMode != "Markdown" &&
755 c.ParseMode != "MarkdownV2" &&
756 c.ParseMode != "HTML" {
757 return fmt.Errorf("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string")
758 }
759 return nil
760 }
761
View as plain text