-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
227 lines (197 loc) · 5.36 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
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"github.com/sensu/sensu-go/types"
"github.com/spf13/cobra"
)
var (
webhook string
annotations string
debug bool
stdin *os.File
)
// Properties struct includes the two fields (subject and message) which Zenduty webhook expects to be POSTed to it in JSON
type Properties struct {
Subject string `json:"subject"`
Message string `json:"message"`
Status string `json:"status"`
Id string `json:"id"`
}
// Payload struct includes Properties struct for post to Zenduty webhook URL
type Payload struct {
Properties `json:"properties"`
}
func main() {
rootCmd := configureRootCommand()
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
func configureRootCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "sensu-go-zenduty-handler",
Short: "The Sensu Go Zenduty handler for incident alerting",
RunE: run,
}
cmd.Flags().StringVarP(&webhook,
"webhook",
"w",
os.Getenv("ZENDUTY_WEBHOOK"),
"The Webhook URL, use default from ZENDUTY_WEBHOOK env var")
cmd.Flags().StringVarP(&annotations,
"withAnnotations",
"a",
os.Getenv("ZENDUTY_ANNOTATIONS"),
"The Zenduty handler will parse check and entity annotations with these values. Use ZENDUTY_ANNOTATIONS env var with commas, like: documentation,playbook")
cmd.Flags().BoolVarP(&debug,
"debug",
"d",
false,
"Enable debug mode, which prints JSON object which would be POSTed to the Zenduty webhook instead of actually POSTing it")
_ = cmd.MarkFlagRequired("webhook")
return cmd
}
// formattedEventAction func
func formattedEventAction(event *types.Event) string {
switch event.Check.Status {
case 0:
return "RESOLVED"
case 1:
return "WARNING"
case 2:
return "CRITICAL"
default:
return "ALERT"
}
}
// stringInSlice checks if a slice contains a specific string
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// parseAnnotations func try to find a predeterminated keys
func parseAnnotations(event *types.Event) string {
var output string
// localannotations := make(map[string]string)
tags := strings.Split(annotations, ",")
if event.Check.Annotations != nil {
for key, value := range event.Check.Annotations {
if stringInSlice(key, tags) {
output += fmt.Sprintf(" %s: %s ,\n", key, value)
}
}
}
if event.Entity.Annotations != nil {
for key, value := range event.Check.Annotations {
if stringInSlice(key, tags) {
output += fmt.Sprintf(" %s: %s ,\n", key, value)
}
}
}
return output
}
// Gets ID of the event to set as Zenduty Entity Id, if ID doesn't exist(?) check name is used.
func getID(event *types.Event) string {
var output string
// if len(event.ID)>0{
// output = string(event.ID)
// } else{
output = event.Check.Name
// }
return output
}
// eventSubject func returns a one-line short summary
func eventSubject(event *types.Event) string {
return fmt.Sprintf("The %s check has changed to %s on host %s", event.Check.Name, formattedEventAction(event), event.Entity.Name)
}
// eventDescription func returns a formatted message
func eventDescription(event *types.Event) string {
return fmt.Sprintf("Server: %s, \nCheck: %s, \nStatus: %s, \nCheck Output: %s, \nAnnotation Information: %s\n", event.Entity.Name, event.Check.Name, formattedEventAction(event), event.Check.Output, parseAnnotations(event))
}
func run(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
_ = cmd.Help()
return fmt.Errorf("invalid argument(s) received")
}
if webhook == "" {
_ = cmd.Help()
return fmt.Errorf("webhook is empty")
}
if annotations == "" {
annotations = "documentation"
}
if stdin == nil {
stdin = os.Stdin
}
eventJSON, err := ioutil.ReadAll(stdin)
if err != nil {
return fmt.Errorf("failed to read stdin: %s", err)
}
event := &types.Event{}
err = json.Unmarshal(eventJSON, event)
if err != nil {
return fmt.Errorf("failed to unmarshal stdin data: %s", err)
}
if err = event.Validate(); err != nil {
return fmt.Errorf("failed to validate event: %s", err)
}
if !event.HasCheck() {
return fmt.Errorf("event does not contain check")
}
formPost := &Payload{
Properties: Properties{
Subject: eventSubject(event),
Message: eventDescription(event),
Status: formattedEventAction(event),
Id : getID(event),
},
}
bodymarshal, err := json.Marshal(formPost)
if err != nil {
fmt.Printf("[ERROR] %s", err)
}
if debug == true {
fmt.Printf("[DEBUG] JSON output: %s\n", bodymarshal)
fmt.Println("[DEBUG] Not posting JSON object to Zenduty webhook since we're in debug mode ")
os.Exit(1)
}
Post(webhook, bodymarshal)
return nil
}
//Post func to send the json to zenduty "generic webhook" integration
func Post(url string, body []byte) error {
client := &http.Client{
Timeout: time.Second * 10,
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
fmt.Printf("[ERROR] %s", err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("[ERROR] %s", err)
}
if resp.StatusCode != 200 {
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("[ERROR] %s", err)
}
s := string(bodyText)
fmt.Printf("[LOG]: %s ; %s", resp.Status, s)
}
defer resp.Body.Close()
return nil
}