-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
376 lines (314 loc) · 14.3 KB
/
main.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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
var relversion = "1.3"
// ====================================================================================================
//
// Auth: G. Johnson
// Date: 14-NOV-2020
// Desc: Lite version of the master PD util that will work on lower Windows versions like Win2008
//
// ====================================================================================================
//
// BUILDING - Assumes PowerShell command line in use on Windows for building Win and Linux Versions
//
// Windows build:
//
// $env:GOOS="windows"
// go build pagerdutylite.go
//
// LINUX build
//
// $env:GOOS="linux"
// go build pagerdutylite.go
//
// If bulding Win/Lin on Linux use "export" command to set the same params for each platform
//
// ====================================================================================================
//
// original flags from main PD util
//
// usage: PagerDuty events [-h] --routing_key ROUTING_KEY [--msg MSG]
// [--source SOURCE] --keyname KEYNAME
// [--event {TRIGGER,RESOLVE,ACKNOWLEDGE}]
// [--details DETAILS]
// [--jdetails JDETAILS]
// [--severity {info,critical,warning,error}]
// [--retries RETRIES] [--retry_interval {30,60,120,300}]
// [--proxy_server PROXY_SERVER]
//
// limited options implemented in this util
//
// usage: PagerDuty events [-h]
// --routing_key ROUTING_KEY
// --msg MSG
// --source SOURCE
// --keyname KEYNAME
// --event {TRIGGER,RESOLVE,ACKNOWLEDGE}
// --details DETAILS
// --jdetails JSON_LOG_DETAILS
// --severity {info,critical,warning,error}
// --proxy_server PROXY_SERVER
//
// ====================================================================================================
var showlog bool
var rtnJSONrslt bool
// type PyLdContext struct {
// CtxType string `json:"type"` // link or image
// CtxHref string `json:"href"` // URL
// CtxText string `json:"text"` // Alt text
// CtxSource string `json:"src"` // for image this would URI to the online image
// }
type PayLoad struct {
PyLdSummary string `json:"summary"` // main incident title
PyLdSource string `json:"source"` // source of the incident ( hostname, appname, etc)
PyLdSeverity string `json:"severity"` // info warning error critical
CustomDetails *json.RawMessage `json:"custom_details"` // takes raw JSON to be used to make a simple info table in the PD alert page. JSON is simply sets of K:V pairs in an array
// removed
// PyLdComponent string `json:"component"` // system sub-component type ( db, restsvr, etc )
// PyLdGroup string `json:"group"` // grouping if several incidents get raised ( pricingApp, warehouseheating, etc )
// PyLdClass string `json:"class"` // type of error in the incident ( highCPU, Lowtemp, diskSpace, etc )
// PyLdClient string `json:"client"` //
// PyLDTimestamp string `json:"timestamp"` // override PD timestamp, useful if there's a delay in delivering to PD
}
type PDEventTrigger struct {
RoutingKey string `json:"routing_key"` // the key used from within a service for an API call
EventAction string `json:"event_action"` // trigger acknowledge resolve
DeDupeKey string `json:"dedup_key"` // unique key you use to log and update on, you set it
PyLd PayLoad `json:"payload"` // payload struct
}
type PDEventResolve struct {
RoutingKey string `json:"routing_key"` // the key used from within a service for an API call
EventAction string `json:"event_action"` // trigger acknowledge resolve
DeDupeKey string `json:"dedup_key"` // unique key you use to log and update on, you set it
}
type PDEventAcknowledge struct {
RoutingKey string `json:"routing_key"` // the key used from within a service for an API call
EventAction string `json:"event_action"` // trigger acknowledge resolve
DeDupeKey string `json:"dedup_key"` // unique key you use to log and update on, you set it
}
//
func FlagUsage() {
fmt.Print("\n")
fmt.Printf("PagerDuty Util Lite - %s", relversion)
fmt.Print("\n\n")
fmt.Println("--routing_key ", "<string> - (reqd) - The primary routing key for the PD event rule or service (REQD)")
fmt.Println("--keyname ", "<string> - (reqd) - Unique user defined key.")
fmt.Println("--event ", "<string> - (reqd) - Must be one of { trigger | acknowledge | resolve}")
fmt.Println("--severity ", "<string> - (reqd) - {info | critical | warning | error} ")
fmt.Println("--msg ", "<string> - (reqd) - Primary message alert title.")
fmt.Println("--source ", "<string> - (reqd) - Source of the alert, advise use of hostname.")
fmt.Println("--details ", "<string> - (opt) - Simple logging details for the alert.")
fmt.Println("--jsondetailsfile ", "<string> - (opt) - JSON formatted text file with sets of key/value pairs holding extra alerting info.")
fmt.Println("--proxy_server ", "<string> - (opt) - Force specific proxy server ( default use HTTP_PROXY/HTTPS_PROXY from environment).")
fmt.Print("\n")
fmt.Println("--jsonresult ", "(opt) - Return result to STDOUT in JSON format. Useful for other apps that need to capture the result.")
fmt.Println("--savejsonresponse ", "(opt) - Save the JSON result to a file.")
fmt.Print("\n")
fmt.Println("Note : ")
fmt.Println(" - If you need to use a proxy then HTTP_PROXY or HTTPS_PROXY are drawn from the environment by default.")
fmt.Print("\n")
fmt.Println("Examples : ")
fmt.Println(" .\\pagerdutylite.exe --routing_key aaabbbccccdddeeefff1112223334445 --keyname \"TestEvent12345\" --event trigger --severity error --msg \"TestEvent Title\" --source \"testsys\" --jsonresult")
fmt.Println(" .\\pagerdutylite.exe --routing_key aaabbbccccdddeeefff1112223334445 --keyname \"TestEvent12345\" --event acknowledge --jsonresult")
fmt.Println(" .\\pagerdutylite.exe --routing_key aaabbbccccdddeeefff1112223334445 --keyname \"TestEvent12345\" --event resolve --jsonresult")
fmt.Print("\n\n")
}
// primary outtput but only shows if the "--showlog" param is invoked
func FuncOutputMsg(outMsg string) {
if showlog {
log.Println(outMsg)
}
}
func PDRequest(jsonReq []byte) (retVal int, bodyResult string) {
FuncOutputMsg("Proceeding : Submitting request to PagerDuty")
// new HTTP request
req, err := http.NewRequest(http.MethodPost, "https://events.pagerduty.com/v2/enqueue", bytes.NewBuffer(jsonReq))
// basic headers
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("accept", "application/vnd.pagerduty+json;version=2")
// client object up
client := &http.Client{}
// send the request and check it for errors
// note errors during the call, data issues will not be known unless scanned
resp, err := client.Do(req)
if err != nil {
// this will cause an exit
log.Fatalln(err)
}
// leave the connection open until we're done in this func
defer resp.Body.Close()
// collect the result from the call
bodyBytes, _ := ioutil.ReadAll(resp.Body)
// Convert response body to string
bodyString := string(bodyBytes)
FuncOutputMsg("Result : " + bodyString)
if rtnJSONrslt {
fmt.Println(bodyString)
}
if strings.Contains(bodyString, "\"status\":\"success\"") {
return 0, bodyString
} else {
return 1, bodyString
}
}
func DumpJSONResultToFile(PDCallJSONDumPFile string, PDCallJSONResponseBody string) {
FuncOutputMsg("Write response : " + PDCallJSONDumPFile + ".json")
datOut := []byte(PDCallJSONResponseBody)
err := ioutil.WriteFile(PDCallJSONDumPFile+".json", datOut, 0644)
if err != nil {
panic(err)
}
}
func ReadInCustomJSONFile(fileNameLocation string) (tmpJSONStr []byte) {
file, err := os.Open(fileNameLocation)
if err != nil {
log.Fatalln("Failed to read the JSON file: ")
log.Fatal(err)
}
defer func() {
if err := file.Close(); err != nil {
log.Fatalln("Failed to close the JSON file reader:")
log.Fatal(err)
}
}()
tmpJSONStr, err = ioutil.ReadAll(file)
if err != nil {
log.Fatalln("Failed to read the JSON file reader: ")
log.Fatal(err)
}
return tmpJSONStr
}
func main() {
clfRoutingKey := flag.String("routing_key", "", "<string> - The primary routing key for the PD event rule or service")
clfMessageSummary := flag.String("msg", "", "<string> - Primary message alert title.")
clfAlertSource := flag.String("source", "", "<string> - Source of the alert, advise use of hostname.")
clfUniqueKey := flag.String("keyname", "", "<string> - Unique user defined key.")
clfEventAction := flag.String("event", "trigger", "<string> - Must be one of { trigger | acknowledge | resolve}")
clfAlertDetails := flag.String("details", "", "<string> - JSON formatted details for the alert")
clfCustomJSONDetailsFile := flag.String("jsondetailsfile", "", "<string> - JSON formatted text file with key/value pairs of alerting info")
clfEventSeverity := flag.String("severity", "", "<string> - {info | critical | warning | error} ")
clfRtnJSONRslt := flag.Bool("jsonresult", false, "Return result in JSON format.")
clfShowOpsLog := flag.Bool("showlog", false, "Display the output of the operations.")
clfDumpJSONResultToFile := flag.Bool("savejsonresponse", false, "Save the JSON result to a file names <keyname>.json ")
// clfJSONLogDetails := flag.String("jdetails", "", "JSON formatted log details.")
clfProxyServer := flag.String("proxy_server", "", "Force use of specific proxy server.")
// despite the above defaults, there is a custom help message defined in a func
flag.Usage = FlagUsage
// parse the inbound flags
flag.Parse()
// optional flag to output the log
showlog = *clfShowOpsLog
rtnJSONrslt = *clfRtnJSONRslt
FuncOutputMsg("Routing Key : " + *clfRoutingKey)
FuncOutputMsg("Message Key : " + *clfUniqueKey)
// start building the struct for JSON delivery
jsonReq := []byte{}
// some basic input validation
if len(*clfRoutingKey) < 20 {
fmt.Println("RoutingKey must be one a 20 character string. Exiting.")
os.Exit(1)
}
var takeAction string
if strings.ToLower(*clfEventAction) == "trigger" ||
strings.ToLower(*clfEventAction) == "acknowledge" ||
strings.ToLower(*clfEventAction) == "resolve" {
takeAction = strings.ToLower(*clfEventAction)
} else {
fmt.Println("EventAction must be one of \"trigger\", \"acknowledge\" or \"resolve\". Exiting.")
os.Exit(1)
}
FuncOutputMsg("Action : " + takeAction)
// =====================================================================
if takeAction == "trigger" {
if len(*clfMessageSummary) < 1 {
fmt.Println("MessageSummary must be at least 1 character!. Exiting.")
os.Exit(1)
}
if len(*clfAlertSource) < 1 {
fmt.Println("AlertSource must be at least 1 character!. Exiting.")
os.Exit(1)
}
if strings.ToLower(*clfEventSeverity) == "info" ||
strings.ToLower(*clfEventSeverity) == "critical" ||
strings.ToLower(*clfEventSeverity) == "warning" ||
strings.ToLower(*clfEventSeverity) == "error" {
} else {
fmt.Println("EventSeverity must be one of \"info\", \"critical\", \"warning\" or \"error\". Exiting.")
os.Exit(1)
}
FuncOutputMsg("Severity : " + *clfEventSeverity)
FuncOutputMsg("Message Source : " + *clfAlertSource)
FuncOutputMsg("Message Summary : " + *clfMessageSummary)
if len(*clfProxyServer) > 0 {
FuncOutputMsg("Proxy Server : " + *clfProxyServer)
}
// define type, start with nothing
customJSONDetails := json.RawMessage("{}")
// if it needs to be updated with a real value off the cmd line then swap that in
if len(*clfCustomJSONDetailsFile) > 0 && len(*clfAlertDetails) > 0 {
FuncOutputMsg("Custom JSON File : " + *clfCustomJSONDetailsFile)
FuncOutputMsg("Alert Details : " + *clfAlertDetails)
tmpJSONStr := ReadInCustomJSONFile(*clfCustomJSONDetailsFile)
if strings.HasPrefix(string(tmpJSONStr), "{") {
// tmpJSONStr = tmpJSONStr[:len(tmpJSONStr)-len("}")]
tmpJSONStr = tmpJSONStr[1:]
newJSONStr := "{\"extra details\":\"" + *clfAlertDetails + "\"," + string(tmpJSONStr)
customJSONDetails = json.RawMessage(newJSONStr)
} else {
FuncOutputMsg("Unable to append 'details' into 'jdetails'. 'jdetails' param must start with valid '{' character.")
customJSONDetails = json.RawMessage(tmpJSONStr)
}
// customJSONDetails = json.RawMessage(tmpJSONStr)
} else if len(*clfAlertDetails) > 0 {
FuncOutputMsg("Alert Details : " + *clfAlertDetails)
customJSONDetails = json.RawMessage("{\"extra details\":\"" + *clfAlertDetails + "\"}")
} else if len(*clfCustomJSONDetailsFile) > 0 {
FuncOutputMsg("Custom JSON File : " + *clfCustomJSONDetailsFile)
customJSONDetails = json.RawMessage(ReadInCustomJSONFile(*clfCustomJSONDetailsFile))
}
// now fill in the structs that will be used to draw up the JSON to send off
pyld := PayLoad{*clfMessageSummary,
*clfAlertSource,
*clfEventSeverity,
&customJSONDetails}
mainPyLd := PDEventTrigger{
*clfRoutingKey,
takeAction,
*clfUniqueKey,
pyld}
jsonReq, _ = json.Marshal(mainPyLd)
}
if takeAction == "acknowledge" {
mainPyLd := PDEventAcknowledge{
*clfRoutingKey,
takeAction,
*clfUniqueKey}
// marshal up the struct into a JSON formatted string
jsonReq, _ = json.Marshal(mainPyLd)
}
if takeAction == "resolve" {
mainPyLd := PDEventResolve{
*clfRoutingKey,
takeAction,
*clfUniqueKey}
// marshal up the struct into a JSON formatted string
jsonReq, _ = json.Marshal(mainPyLd)
}
FuncOutputMsg("JSON request : " + string(jsonReq))
// post it to PD and see if they accept it
PDCallResponseStatus, PDCallJSONResponseBody := PDRequest(jsonReq)
if *clfDumpJSONResultToFile {
DumpJSONResultToFile(*clfUniqueKey, PDCallJSONResponseBody)
}
os.Exit(PDCallResponseStatus)
}