Skip to content

Commit

Permalink
#20 Added new cloud-events, triggered events, better logging
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Kreuzberger <[email protected]>
  • Loading branch information
christian-kreuzberger-dtx committed Feb 9, 2021
1 parent 676ea10 commit ef804f6
Show file tree
Hide file tree
Showing 7 changed files with 727 additions and 210 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ When writing code, it is recommended to follow the coding style suggested by the
If you don't care about the details, your first entrypoint is [eventhandlers.go](eventhandlers.go). Within this file
you can add implementation for pre-defined Keptn Cloud events.

To better understand Keptn CloudEvents, please look at the [Keptn Spec](https://github.com/keptn/spec).
To better understand all variants of Keptn CloudEvents, please look at the [Keptn Spec](https://github.com/keptn/spec).

If you want to get more insights, please look into [main.go](main.go), [deploy/service.yaml](deploy/service.yaml),
If you want to get more insights into processing those CloudEvents or even defining your own CloudEvents in code, please
look into [main.go](main.go) (specifically `processKeptnCloudEvent`), [deploy/service.yaml](deploy/service.yaml),
consult the [Keptn docs](https://keptn.sh/docs/) as well as existing [Keptn Core](https://github.com/keptn/keptn) and
[Keptn Contrib](https://github.com/keptn-contrib/) services.

Expand Down
2 changes: 0 additions & 2 deletions deploy/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ spec:
ports:
- containerPort: 8080
env:
- name: EVENTBROKER
value: 'http://event-broker/keptn'
- name: CONFIGURATION_SERVICE
value: 'http://configuration-service:8080'
- name: distributor
Expand Down
2 changes: 1 addition & 1 deletion eventhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
keptnlib "github.com/keptn/go-utils/pkg/lib"
keptn "github.com/keptn/go-utils/pkg/lib/keptn"

"github.com/cloudevents/sdk-go/pkg/cloudevents"
cloudevents "github.com/cloudevents/sdk-go/v2" // make sure to use v2 cloudevents here
)

/**
Expand Down
264 changes: 175 additions & 89 deletions eventhandlers.go
Original file line number Diff line number Diff line change
@@ -1,169 +1,255 @@
package main

import (
"fmt"
"log"
"time"

"github.com/cloudevents/sdk-go/pkg/cloudevents"
cloudevents "github.com/cloudevents/sdk-go/v2" // make sure to use v2 cloudevents here
keptn "github.com/keptn/go-utils/pkg/lib"
keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0"
)

/**
* 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())
* See https://github.com/keptn/spec/blob/0.8.0-alpha/cloudevents.md for details on the payload
**/

// GenericLogKeptnCloudEventHandler is a generic handler for Keptn Cloud Events that logs the CloudEvent
func GenericLogKeptnCloudEventHandler(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data interface{}) error {
log.Printf("Handling %s Event: %s", incomingEvent.Type(), incomingEvent.Context.GetID())
log.Printf("CloudEvent %T: %v", data, data)

return nil
}

//
// Handles ConfigurationChangeEventType = "sh.keptn.event.configuration.change"
// OldHandleConfigureMonitoringTriggeredEvent handles old configure-monitoring events
// 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())
func OldHandleConfigureMonitoringTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptn.ConfigureMonitoringEventData) error {
log.Printf("Handling old configure-monitoring Event: %s", incomingEvent.Context.GetID())

return nil
}

//
// Handles DeploymentFinishedEventType = "sh.keptn.events.deployment-finished"
// HandleConfigureMonitoringTriggeredEvent handles configure-monitoring.triggered events
// 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()
func HandleConfigureMonitoringTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.ConfigureMonitoringTriggeredEventData) error {
log.Printf("Handling configure-monitoring.triggered Event: %s", incomingEvent.Context.GetID())

// run tests
// ToDo: Implement your tests here

// Send Test Finished Event
// return myKeptn.SendTestsFinishedEvent(&incomingEvent, "", "", startTime, "pass", nil, "keptn-service-template-go")
return nil
}

//
// Handles TestsFinishedEventType = "sh.keptn.events.tests-finished"
// HandleDeploymentTriggeredEvent handles deployment.triggered events
// 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())
func HandleDeploymentTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.DeploymentTriggeredEventData) error {
log.Printf("Handling deployment.triggered Event: %s", incomingEvent.Context.GetID())

return nil
}

//
// Handles EvaluationDoneEventType = "sh.keptn.events.evaluation-done"
// HandleTestTriggeredEvent handles test.triggered events
// 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())
func HandleTestTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.TestTriggeredEventData) error {
log.Printf("Handling test.triggered Event: %s", incomingEvent.Context.GetID())

return nil
}

//
// Handles DeploymentFinishedEventType = "sh.keptn.events.deployment-finished"
// HandleEvaluationTriggeredEvent handles evaluation.triggered events
// 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())
func HandleEvaluationTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.EvaluationTriggeredEventData) error {
log.Printf("Handling evaluation.triggered 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())
// sendGetSliFinishedCloudEvent is a helper function to send a get-sli.finished event
func sendGetSliFinishedCloudEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.GetSLITriggeredEventData,
status keptnv2.StatusType, result keptnv2.ResultType, message string) error {
log.Printf("Sending getSli Finished Cloud Event with status=%s and result=%s back to Keptn (%s)", status, result, message)
getSliFinishedEventData := keptnv2.GetSLIFinishedEventData{}

getSliFinishedEventData.EventData = data.EventData
getSliFinishedEventData.Status = status
getSliFinishedEventData.Result = result
getSliFinishedEventData.Message = message

// Convert To CloudEvent
finishedEvent := cloudevents.NewEvent()
finishedEvent.SetType(keptnv2.GetFinishedEventType(keptnv2.GetSLITaskName))
finishedEvent.SetData(cloudevents.ApplicationJSON, getSliFinishedEventData)

return SendEvent(myKeptn, finishedEvent, incomingEvent)
}

incomingGetSLIEventData := &keptn.InternalGetSLIEventData{}
incomingEvent.DataAs(incomingGetSLIEventData)
// HandleGetSliEvent handles get-sli events
// TODO: adapt handler code to your needs
func HandleGetSliEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.GetSLITriggeredEventData) error {
log.Printf("Handling get-sli.triggered Event: %s", incomingEvent.Context.GetID())

// 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 != "keptn-service-template-go" {
if data.GetSLI.SLIProvider != "keptn-service-template-go" {
log.Printf("Not handling get-sli event as it is meant for %s", data.GetSLI.SLIProvider)
return nil
}*/
}

// Step 2 - Send out a get-sli.started CloudEvent
// The get-sli.started cloud-event is new since Keptn 0.8.0 and is required for the task to start
getSliStartedData := keptnv2.GetSLIStartedEventData{}

getSliStartedData.EventData = data.EventData
getSliStartedData.Status = keptnv2.StatusSucceeded // alternative: keptnv2.StatusErrored
getSliStartedData.Result = keptnv2.ResultPass // alternative: keptnv2.ResultFailed

// Step 2 - prep-work
// Get any additional input / configuration data, e.g
// Labels: get the incoming labels for potential config data and use it to pass more labels on result, e.g: links
// SLI.yaml: if your service uses SLI.yaml to store query definitions for SLIs get that file from Keptn
/* labels := incomingGetSLIEventData.Labels
// Convert To CloudEvent
startedEvent := cloudevents.NewEvent()
startedEvent.SetType(keptnv2.GetStartedEventType(keptnv2.GetSLITaskName))
startedEvent.SetData(cloudevents.ApplicationJSON, getSliStartedData)

// send action.started event
SendEvent(myKeptn, startedEvent, incomingEvent)

// Step 4 - prep-work
// Get any additional input / configuration data
// - Labels: get the incoming labels for potential config data and use it to pass more labels on result, e.g: links
// - SLI.yaml: if your service uses SLI.yaml to store query definitions for SLIs get that file from Keptn
labels := data.Labels
if labels == nil {
labels = make(map[string]string)
}
testRunID := labels["testRunId"]*/
testRunID := labels["testRunId"]

// Step 5 - get SLI Config File
// Get SLI File from keptn-service-template-go subdirectory of the config repo - to add the file use:
// keptn add-resource --project=PROJECT --stage=STAGE --service=SERVICE --resource=my-sli-config.yaml --resourceUri=keptn-service-template-go/sli.yaml
sliFile := "keptn-service-template-go/sli.yaml"
sliConfigFileContent, err := myKeptn.GetKeptnResource(sliFile)

if err != nil {
// failed to fetch sli config file
errMsg := fmt.Sprintf("Failed to fetch SLI file %s from config repo: %s", sliFile, err.Error())
log.Println(errMsg)
// send a get-sli.finished event with status=error and result=failed back to Keptn
return sendGetSliFinishedCloudEvent(myKeptn, incomingEvent, data, keptnv2.StatusErrored, keptnv2.ResultFailed, errMsg)
}

// sliConfigFileContent, err := myKeptn.GetKeptnResource("keptn-service-template-go/sli.yaml")
fmt.Println(sliConfigFileContent)

// Step 3 - do your work - iterate through the list of requested indicators and return their values
// Step 6 - do your work - iterate through the list of requested indicators and return their values
// Indicators: this is the list of indicators as requested in the SLO.yaml
// SLIResult: this is the array that will receive the results
/* indicators := incomingGetSLIEventData.Indicators
sliResults := []*keptn.SLIResult{}
indicators := data.GetSLI.Indicators
sliResults := []*keptnv2.SLIResult{}

for _, indicatorName := range indicators {
sliResult := &keptn.SLIResult{
sliResult := &keptnv2.SLIResult{
Metric: indicatorName,
Value: 123.4,
Value: 123.4, // ToDo: Fetch the values from your monitoring tool here
}
sliResults = append(sliResults, sliResult)
}*/
}

// Step 4 - add additional context via labels
// labels["Link to Data Source"] = "https://mydatasource/myquery?testRun=" + testRunID
// Step 7 - add additional context via labels (e.g., a backlink to the monitoring or CI tool)
labels["Link to Data Source"] = "https://mydatasource/myquery?testRun=" + testRunID

// Step 8 - Build get-sli.finished event data
getSliFinishedEventData := keptnv2.GetSLIFinishedEventData{
EventData: keptnv2.EventData{
Project: data.Project,
Stage: data.Stage,
Service: data.Service,
Labels: labels,
Status: keptnv2.StatusSucceeded,
Result: keptnv2.ResultPass,
},
GetSLI: struct {
Start string `json:"start"`
End string `json:"end"`
IndicatorValues []*keptnv2.SLIResult `json:"indicatorValues"`
}{
IndicatorValues: sliResults,
Start: data.GetSLI.Start,
End: data.GetSLI.End,
},
}

// Step 4 - send results back to Keptn
// return myKeptn.SendInternalGetSLIDoneEvent(incomingGetSLIEventData, sliResults, labels, err, "keptn-service-template-go")
// Step 8 - Convert To CloudEvent
finishedEvent := cloudevents.NewEvent()
finishedEvent.SetType(keptnv2.GetFinishedEventType(keptnv2.GetSLITaskName))
finishedEvent.SetData(cloudevents.ApplicationJSON, getSliFinishedEventData)

return nil
// Step 9 - send action.finished CloudEvent back to Keptn
return SendEvent(myKeptn, finishedEvent, incomingEvent)
}

//
// Handles ProblemOpenEventType = "sh.keptn.event.problem.open"
// Handles ProblemEventType = "sh.keptn.events.problem"
// HandleProblemEvent handles two problem events:
// - ProblemOpenEventType = "sh.keptn.event.problem.open"
// - ProblemEventType = "sh.keptn.events.problem"
// TODO: add in your handler code
//
func HandleProblemEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.ProblemEventData) error {
func HandleProblemEvent(myKeptn *keptnv2.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"
// HandleActionTriggeredEvent handles action.triggered events
// TODO: add in your handler code
//
func HandleActionTriggeredEvent(myKeptn *keptn.Keptn, incomingEvent cloudevents.Event, data *keptn.ActionTriggeredEventData) error {
func HandleActionTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.ActionTriggeredEventData) error {
log.Printf("Handling Action Triggered Event: %s", incomingEvent.Context.GetID())
log.Printf("Action=%s\n", data.Action.Action)

// check if action is supported
if data.Action.Action == "action-xyz" {
//myKeptn.SendActionStartedEvent()
// -----------------------------------------------------
// 1. Send Action.Started Cloud-Event
// -----------------------------------------------------

// generate an action.started event
actionStartedData := keptnv2.ActionStartedEventData{}

actionStartedData.EventData = data.EventData
actionStartedData.Status = keptnv2.StatusSucceeded // alternative: keptnv2.StatusErrored
actionStartedData.Result = keptnv2.ResultPass // alternative: keptnv2.ResultFailed

// Convert To CloudEvent
startedEvent := cloudevents.NewEvent()
startedEvent.SetType(keptnv2.GetStartedEventType(keptnv2.ActionTaskName))
startedEvent.SetData(cloudevents.ApplicationJSON, actionStartedData)

// send action.started event
SendEvent(myKeptn, startedEvent, incomingEvent)

// Implement your remediation action here
// -----------------------------------------------------
// 2. Implement your remediation action here
// -----------------------------------------------------
time.Sleep(5 * time.Second) // Example: Wait 5 seconds. Maybe the problem fixes itself.

//myKeptn.SendActionFinishedEvent()
// -----------------------------------------------------
// 3. Send Action.Finished Cloud-Event
// -----------------------------------------------------

// generate an action.finished event
actionFinishedData := keptnv2.ActionFinishedEventData{}

actionFinishedData.EventData = data.EventData
actionFinishedData.Status = keptnv2.StatusSucceeded // alternative: keptnv2.StatusErrored
actionFinishedData.Result = keptnv2.ResultPass // alternative: keptnv2.ResultFailed
actionFinishedData.Message = "Successfully sleeped!"

// Convert To CloudEvent
finishedEvent := cloudevents.NewEvent()
finishedEvent.SetType(keptnv2.GetFinishedEventType(keptnv2.ActionTaskName))
finishedEvent.SetData(cloudevents.ApplicationJSON, actionFinishedData)

// send action.finished event
SendEvent(myKeptn, finishedEvent, incomingEvent)

} else {
log.Printf("Retrieved unknown action %s, skipping...", data.Action.Action)
return nil
}
return nil
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module example.com/keptn-service-template-go
go 1.13

require (
github.com/cloudevents/sdk-go v1.1.2
github.com/cloudevents/sdk-go v1.1.2 // indirect
github.com/cloudevents/sdk-go/v2 v2.2.0
github.com/google/uuid v1.1.1
github.com/kelseyhightower/envconfig v1.4.0
github.com/keptn/go-utils v0.8.0-alpha
github.com/keptn/go-utils v0.8.0-alpha.0.20210113154602-3a846009dff4
github.com/onsi/ginkgo v1.12.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
)
Loading

0 comments on commit ef804f6

Please sign in to comment.