-
-
Notifications
You must be signed in to change notification settings - Fork 32
/
notifier.go
131 lines (105 loc) · 3.21 KB
/
notifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright (c) Liam Stanley <[email protected]>. All rights reserved. Use of
// this source code is governed by the MIT license that can be found in
// the LICENSE file.
package main
import (
"context"
"crypto/tls"
"fmt"
"os"
"strings"
"sync"
"time"
"github.com/apex/log"
mail "gopkg.in/mail.v2"
)
var (
notifyCh = make(chan error, 20)
notifyQueue = []notifyError{}
)
type notifyError struct {
timestamp time.Time
err error
}
func notify(err error) {
err = fmt.Errorf("error: %w", err)
logger.WithError(err).Error("notify-error")
if !conf.Email.Enabled {
return
}
notifyCh <- err
}
func notifier(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
logger.Info("starting notifier")
// Wait for events, and sort of act like a debouncer. As a new event comes in,
// it prevents the time.After statement from returning, thus resetting it's
// timer.
//
// One catch though, if a certain amount of time passes since the first
// received event, send it anyway, rather than just resetting. This ensures
// that if a continual stream of events comes in, time.After() will *never*
// be called, and events will continue to build up forever.
for {
select {
case <-ctx.Done():
sendQueue()
return
case err := <-notifyCh:
notifyQueue = append(notifyQueue, notifyError{timestamp: time.Now(), err: err})
if time.Since(notifyQueue[0].timestamp) >= conf.NotifyMaxElapsed {
sendQueue()
}
case <-time.After(conf.NotifyQueueDelay):
sendQueue()
}
}
}
func sendQueue() {
if len(notifyQueue) == 0 {
return
}
logger.Infof("attempting to send notifications for %d alerts", len(notifyQueue))
text := `vault-unseal ran into errors when attempting to check seal status/unseal. here are the errors:
`
for i := range len(notifyQueue) {
text += fmt.Sprintf("\n%s :: %v", notifyQueue[i].timestamp.Format(time.RFC822), notifyQueue[i].err)
}
var err error
smtp := mail.NewDialer(conf.Email.Hostname, conf.Email.Port, conf.Email.Username, conf.Email.Password)
smtp.TLSConfig = &tls.Config{
InsecureSkipVerify: conf.Email.TLSSkipVerify, //nolint:gosec
ServerName: conf.Email.Hostname,
}
if conf.Email.MandatoryTLS {
smtp.StartTLSPolicy = mail.MandatoryStartTLS
}
smtp.LocalName, err = os.Hostname()
if err != nil {
smtp.LocalName = "localhost"
}
s, err := smtp.Dial()
if err != nil {
logger.WithError(err).WithFields(log.Fields{
"hostname": conf.Email.Hostname,
"port": conf.Email.Port,
}).Error("unable to make smtp connection")
return
}
text += fmt.Sprintf("\n\nsent from vault-unseal. version: %s, compile date: %s, hostname: %s", version, date, smtp.LocalName)
msg := mail.NewMessage()
msg.SetHeader("From", conf.Email.FromAddr)
msg.SetHeader("Subject", fmt.Sprintf("vault-unseal: %s: %d errors occurred", conf.Environment, len(notifyQueue)))
msg.SetBody("text/plain", text)
msg.SetHeader("To", conf.Email.SendAddrs[0])
if len(conf.Email.SendAddrs) > 1 {
msg.SetHeader("CC", conf.Email.SendAddrs[1:]...)
}
err = mail.Send(s, msg)
if err != nil {
logger.WithError(err).Error("unable to send notification")
return
}
logger.WithField("to", strings.Join(conf.Email.SendAddrs, ",")).Info("successfully sent notifications")
notifyQueue = nil
}