generated from keptn-sandbox/keptn-service-template-go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
eventhandlers.go
366 lines (301 loc) · 12.5 KB
/
eventhandlers.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
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/cloudevents/sdk-go/pkg/cloudevents"
keptn "github.com/keptn/go-utils/pkg/lib"
"github.com/cloudevents/sdk-go/pkg/cloudevents/types"
"github.com/google/uuid"
)
type PACResultFile struct {
Results []PACResult `json:"results"`
}
type PACResult struct {
ID string `json:"id"`
URL string `json:"url"`
Date string `json:"date"`
Data map[string]float64 `json:"data"`
}
/**
* Here are all the handler functions for the individual event
See https://github.com/keptn/spec/blob/0.1.3/cloudevents.md for details on the payload
-> "sh.keptn.event.configuration.change"
-> "sh.keptn.events.deployment-finished"
-> "sh.keptn.events.tests-finished"
-> "sh.keptn.event.start-evaluation"
-> "sh.keptn.events.evaluation-done"
-> "sh.keptn.event.problem.open"
-> "sh.keptn.events.problem"
-> "sh.keptn.event.action.triggered"
*/
// Handles ConfigureMonitoringEventType = "sh.keptn.event.monitoring.configure"
func HandleConfigureMonitoringEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.ConfigureMonitoringEventData) error {
log.Printf("Handling Configure Monitoring Event: %s", incomingEvent.Context.GetID())
return nil
}
//
// Handles ConfigurationChangeEventType = "sh.keptn.event.configuration.change"
// TODO: add in your handler code
//
func HandleConfigurationChangeEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.ConfigurationChangeEventData) error {
log.Printf("Handling Configuration Changed Event: %s", incomingEvent.Context.GetID())
return nil
}
//
// Handles DeploymentFinishedEventType = "sh.keptn.events.deployment-finished"
// TODO: add in your handler code
//
func HandleDeploymentFinishedEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.DeploymentFinishedEventData) error {
log.Printf("Handling Deployment Finished Event: %s", incomingEvent.Context.GetID())
// capture start time for tests
// startTime := time.Now()
// run tests
// ToDo: Implement your tests here
// Send Test Finished Event
// return myKeptn.SendTestsFinishedEvent(&incomingEvent, "", "", startTime, "pass", nil, "pac-sliprovider")
return nil
}
//
// Handles TestsFinishedEventType = "sh.keptn.events.tests-finished"
// TODO: add in your handler code
//
func HandleTestsFinishedEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.TestsFinishedEventData) error {
log.Printf("Handling Tests Finished Event: %s", incomingEvent.Context.GetID())
return nil
}
//
// Handles EvaluationDoneEventType = "sh.keptn.events.evaluation-done"
// TODO: add in your handler code
//
func HandleStartEvaluationEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.StartEvaluationEventData) error {
log.Printf("Handling Start Evaluation Event: %s", incomingEvent.Context.GetID())
return nil
}
//
// Handles DeploymentFinishedEventType = "sh.keptn.events.deployment-finished"
// TODO: add in your handler code
//
func HandleEvaluationDoneEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.EvaluationDoneEventData) error {
log.Printf("Handling Evaluation Done Event: %s", incomingEvent.Context.GetID())
return nil
}
//
// Handles InternalGetSLIEventType = "sh.keptn.internal.event.get-sli"
// TODO: add in your handler code
//
func HandleInternalGetSLIEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.InternalGetSLIEventData) error {
log.Printf("Handling Internal Get SLI Event: %s", incomingEvent.Context.GetID())
incomingGetSLIEventData := data
// Step 1 - Do we need to do something?
// Lets make sure we are only processing an event that really belongs to our SLI Provider
if incomingGetSLIEventData.SLIProvider != "pac-sliprovider" {
return fmt.Errorf("Not handling event because its not for pac-sliprovider. Its for: " + incomingGetSLIEventData.SLIProvider)
}
// Step 2 - prep-work
// Get the incoming GetSLIEvent for accessing incoming data
// Labels: get the incoming labels for potential config data and use it to pass more labels on result, e.g: links
// Indicators: this is the list of indicators as requested in the SLO.yaml
// SLIResult: this is the array that will receive the results
labels := incomingGetSLIEventData.Labels
if labels == nil {
labels = make(map[string]string)
}
// Step 3 - query the data entry that matches what the user wants
indicators := incomingGetSLIEventData.Indicators
sliResults := []*keptn.SLIResult{}
pacResult, resultFile, err := loadPACData(myKeptn, incomingGetSLIEventData, labels)
// if we dont have any data return the error
if err != nil || pacResult == nil {
SendInternalGetSLIDoneEvent(myKeptn, incomingGetSLIEventData, indicators, sliResults, labels, err, "pac-sliprovider")
return err
}
// Step 4 - now lets return those result properties
for _, indicatorName := range indicators {
pacValue, pacValueExists := pacResult.Data[indicatorName]
valueMessage := ""
if !pacValueExists {
valueMessage = "Couldnt find data for SLI " + indicatorName
}
sliResult := &keptn.SLIResult{
Metric: indicatorName,
Value: pacValue,
Success: pacValueExists,
Message: valueMessage,
}
sliResults = append(sliResults, sliResult)
}
// Step 5 - add the result file link to the labels so it shows up in the bridage
labels["PAC Data Source"] = resultFile
labels["Link to "+pacResult.ID] = pacResult.URL
sliResultAsText, err := json.Marshal(sliResults)
log.Printf(string(sliResultAsText))
return SendInternalGetSLIDoneEvent(myKeptn, incomingGetSLIEventData, indicators, sliResults, labels, nil, "pac-sliprovider")
}
//
// Handles ProblemOpenEventType = "sh.keptn.event.problem.open"
// Handles ProblemEventType = "sh.keptn.events.problem"
// TODO: add in your handler code
//
func HandleProblemEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.ProblemEventData) error {
log.Printf("Handling Problem Event: %s", incomingEvent.Context.GetID())
// Deprecated since Keptn 0.7.0 - use the HandleActionTriggeredEvent instead
return nil
}
//
// Handles ActionTriggeredEventType = "sh.keptn.event.action.triggered"
// TODO: add in your handler code
//
func HandleActionTriggeredEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.ActionTriggeredEventData) error {
log.Printf("Handling Action Triggered Event: %s", incomingEvent.Context.GetID())
// check if action is supported
if data.Action.Action == "action-xyz" {
//myKeptn.SendActionStartedEvent()
// Implement your remediation action here
//myKeptn.SendActionFinishedEvent()
}
return nil
}
//
// This actually loads the data from the results file
//
func loadPACData(myKeptn *keptn.Keptn, incomingGetSLIEventData *keptn.InternalGetSLIEventData, labels map[string]string) (*PACResult, string, error) {
// Step 1 - figure out where to load our data from
resultFile := ""
// Step 1a - option 1 could be e.g: from customFilters which is data from the SLO.yaml
if incomingGetSLIEventData.CustomFilters != nil {
for _, customFilter := range incomingGetSLIEventData.CustomFilters {
if customFilter.Key == "resultfile" {
resultFile = customFilter.Value
}
}
}
// Step 1b - just to give you a different example - loading some configuration from an environment variable on the containers if it wasnt set through filters
if resultFile == "" {
resultFile = os.Getenv("RESULTFILE")
}
// Step 1c - another option is loading it from a sli-provider specific configuration file stored in the Keptn Repo
// This could be a sli.yaml, myconfig.conf, ... - whatever makes sense for your SLI provider
// In this case we load it from a file in a sli-provider specific subfolder that is stored for this keptn project, stage & service
sliConfigFile, err := myKeptn.GetKeptnResource("pac-sliprovider/sli.yaml")
if err != nil {
log.Printf("TODO: Downloaded SLI.yaml but not doing anything with it yet in this sample SLI Provider: " + sliConfigFile)
// resultFile = keptnResourceContent
}
// Step 1d - if no configuration was set through customFilters, Envs or Config File lets go with a default!
if resultFile == "" {
log.Printf("No result file passed on slo.yaml so we default to our results.json")
resultFile = "https://raw.githubusercontent.com/grabnerandi/pac-sliprovider/master/resultfiles/results.json"
}
// Validateion - if a resultfile was specified but it doesnt start with https we assume we just got passed the filename itself - so - we prepend our sample repo URL
if strings.Index(resultFile, "http") != 0 {
log.Printf(resultFile + " not a full qualified URL. So we prepend our sample URL")
resultFile = "https://raw.githubusercontent.com/grabnerandi/pac-sliprovider/master/resultfiles/" + resultFile
}
// Step 2 - Lets actually download the result file from the URL we have:-)
resp, err := http.Get(resultFile)
if err != nil {
return nil, resultFile, err
}
defer resp.Body.Close()
resultFileContent, _ := ioutil.ReadAll(resp.Body)
resultJSON := &PACResultFile{}
err = json.Unmarshal(resultFileContent, &resultJSON)
if err != nil {
return nil, resultFile, err
}
if resultJSON == nil || len(resultJSON.Results) == 0 {
return nil, resultFile, fmt.Errorf("No results in pacresult.json")
}
// Step 3 - query the data the user actually wants
// Depending on the SLI service we could use the timeframe (start/end) to e.g: query timeranges, we could also use information passed via labels, e.g: a test run id
// In our example we simply look for a label with the name "pacId" - if that is present we return the data we have for that pacId. Otherwise we just return the first result
pacID := labels["pacId"]
var matchingPacResult *PACResult
if pacID == "" {
log.Printf("No pacId passed via labels - so - just picking first result in " + resultFile)
if len(resultJSON.Results) > 0 {
matchingPacResult = &resultJSON.Results[0]
}
} else {
log.Printf("Searching for entry pacId=" + pacID)
for ix, pacResult := range resultJSON.Results {
if strings.Compare(pacResult.ID, pacID) == 0 {
matchingPacResult = &resultJSON.Results[ix]
break
}
}
}
if matchingPacResult == nil {
return nil, resultFile, fmt.Errorf("Couldn't find matching results for " + pacID)
} else {
log.Printf("Found match: " + matchingPacResult.ID + ": " + matchingPacResult.URL)
}
return matchingPacResult, resultFile, nil
}
/**
* Sends the SLI Done Event. If err != nil it will send an error message
* THIS SHOULD MAKE IT INTO go-utils!
*/
func SendInternalGetSLIDoneEvent(myKeptn *keptn.Keptn, incomingGetSLIEventData *keptn.InternalGetSLIEventData, indicators []string, indicatorValues []*keptn.SLIResult, labels map[string]string, err error, eventSource string) error {
source, _ := url.Parse(eventSource)
contentType := "application/json"
// if an error was set - the indicators will be set to failed and error message is set to each
if err != nil {
errMessage := err.Error()
if (indicatorValues == nil) || (len(indicatorValues) == 0) {
if indicators == nil || len(indicators) == 0 {
indicators = []string{"no metric"}
}
for _, indicatorName := range indicators {
indicatorValues = []*keptn.SLIResult{
{
Metric: indicatorName,
Value: 0.0,
},
}
}
}
for _, indicator := range indicatorValues {
indicator.Success = false
indicator.Message = errMessage
}
}
sliDoneEvent := keptn.InternalGetSLIDoneEventData{}
// reuse data from the incoming GetSLIEventData
if incomingGetSLIEventData != nil {
sliDoneEvent.Project = incomingGetSLIEventData.Project
sliDoneEvent.Stage = incomingGetSLIEventData.Stage
sliDoneEvent.Service = incomingGetSLIEventData.Service
sliDoneEvent.Start = incomingGetSLIEventData.Start
sliDoneEvent.End = incomingGetSLIEventData.End
sliDoneEvent.TestStrategy = incomingGetSLIEventData.TestStrategy
sliDoneEvent.DeploymentStrategy = incomingGetSLIEventData.DeploymentStrategy
sliDoneEvent.Deployment = incomingGetSLIEventData.Deployment
}
if labels != nil {
sliDoneEvent.Labels = labels
}
if indicatorValues != nil {
sliDoneEvent.IndicatorValues = indicatorValues
}
event := cloudevents.Event{
Context: cloudevents.EventContextV02{
ID: uuid.New().String(),
Time: &types.Timestamp{Time: time.Now()},
Type: keptn.InternalGetSLIDoneEventType,
Source: types.URLRef{URL: *source},
ContentType: &contentType,
Extensions: map[string]interface{}{"shkeptncontext": myKeptn.KeptnContext},
}.AsV02(),
Data: sliDoneEvent,
}
log.Println(fmt.Printf("%s", event))
return myKeptn.SendCloudEvent(event)
}