-
Notifications
You must be signed in to change notification settings - Fork 1
/
gocontrol.go
213 lines (192 loc) · 4.87 KB
/
gocontrol.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"net/mail"
"net/smtp"
"os"
"os/exec"
"strconv"
"sync"
"time"
"github.com/pedromg/goEncoderBase64"
)
// RequestInfo contains all the required config information
type RequestInfo struct {
Name string
URL string
Secure bool
Interval int
StatusCode []int
MaxAlerts int
Script string
DelayedBy int
Email bool
SMTPHost string
SMTPPort int
SMTPEmail string
SMTPUsername string
SMTPPassword string
EmailAddress string
Log bool
LogFile string
}
var reqinfo []RequestInfo
func jsonToRequestinfo(j []byte) (r []RequestInfo, err error) {
err = json.Unmarshal(j, &r)
return r, err
}
func getRequestInfo(f string) (r []RequestInfo, err error) {
var theJSON []byte
theJSON, err = ioutil.ReadFile(f)
if err == nil {
log.Print("JSON file loaded. OK")
r, err = jsonToRequestinfo(theJSON)
}
return r, err
}
func messageLine(elem RequestInfo, r *http.Response, err error) (m string) {
var theErr string
var theStatus string
if err != nil {
theErr = err.Error()
}
if r != nil {
theStatus = r.Status
}
m = time.Now().String() + " " + elem.Name + " - " + theStatus + " - " + theErr + "\n"
return m
}
func senderEmail(elem RequestInfo, from, to, msg string) {
f := mail.Address{from, from}
t := mail.Address{to, to}
// auth
auth := smtp.PlainAuth("", elem.SMTPUsername, elem.SMTPPassword, elem.SMTPHost)
err := smtp.SendMail(elem.SMTPHost+":"+strconv.Itoa(elem.SMTPPort), auth, f.Address, []string{t.Address}, []byte(msg))
if err != nil {
log.Print("SEND MAIL Error, ", err)
}
}
func sendEmail(elem RequestInfo, r *http.Response, err error) (e error) {
startTime := time.Now()
header := make(map[string]string)
header["From"] = elem.SMTPEmail
header["To"] = elem.EmailAddress
theMesgID := "<" + strconv.Itoa(rand.Intn(999999999)) + "__" +
startTime.Format("2006-01-02T15:04:05.999999999Z07:00") +
"==@" + elem.SMTPHost + ">"
header["Message-id"] = theMesgID
header["Date"] = startTime.Format("Mon, 02 Jan 2006 15:04:05 +0000")
header["Subject"] = "goControl Alert for " + elem.Name
header["MIME-Version"] = "1.0"
header["Content-Type"] = "text/plain; charset=\"utf-8\""
header["Content-Transfer-Encoding"] = "base64"
body := "++ goControl ALERT ++ \n\n "
body += messageLine(elem, r, err)
msg := ""
for k, v := range header {
msg += fmt.Sprintf("%s: %s\r\n", k, v)
}
msg += "\r\n"
msg += goEncoderBase64.Base64MimeEncoder(body)
go senderEmail(elem, elem.SMTPEmail, elem.EmailAddress, msg)
return e
}
func sendLog(elem RequestInfo, r *http.Response, err error) (e error) {
f, e := os.OpenFile(elem.LogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if e == nil {
defer f.Close()
_, e = f.WriteString(messageLine(elem, r, err))
}
return e
}
func execScript(elem RequestInfo) (err error) {
cmd := exec.Command("sh", elem.Script)
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
return err
}
func startWorker(elem RequestInfo, wg *sync.WaitGroup) {
defer wg.Done()
var url string
alert := false
delayCounter := 0
maxAlertCounter := 0
if elem.Secure {
url = "https://" + elem.URL
} else {
url = "http://" + elem.URL
}
log.Print("Started monitoring >> ", elem.URL, " at a ", elem.Interval, "s interval")
for {
// request is sent
r, err := http.Head(url)
// check for errors and response. If no errors, check if response status code is in the codes slice.
if err != nil {
alert = true
} else {
for _, sc := range elem.StatusCode {
if sc == r.StatusCode {
alert = true
}
}
}
// delay script execution counter.
// alerts will be sent if the maxalert is not reached.
// maxAlertCounter is incremented or reset.
if alert {
delayCounter++
maxAlertCounter++
} else {
maxAlertCounter = 0
delayCounter = 0
}
// script execution
if alert && delayCounter > elem.DelayedBy {
execScript(elem)
}
// no alerts if maxAlertCounter is reached
if maxAlertCounter > elem.MaxAlerts {
alert = false
}
// alert triggers
if alert && elem.Email {
sendEmail(elem, r, err)
}
if alert && elem.Log {
e := sendLog(elem, r, err)
if e != nil {
log.Print("ERROR Creating LogFile - ", e)
}
}
// reset the alert and sleep
alert = false
time.Sleep(time.Duration(elem.Interval) * time.Second)
}
}
func main() {
// TODO: json file name via argument
// TODO: list of json files, via argument
jsonFile := "./files/gocontrol.json"
// get the requests to monitor
res, err := getRequestInfo(jsonFile)
if err != nil {
log.Fatal("Fatal error loading the JSON data,", err)
}
for _, elem := range res {
fmt.Printf("URL: %s Script:%s (Secure:%v) \n", elem.URL, elem.Script, elem.Secure)
}
// create go routines for each request
var wg sync.WaitGroup
for _, elem := range res {
wg.Add(1)
go startWorker(elem, &wg)
}
wg.Wait()
}