diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index a256306..661703b 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -117,7 +117,7 @@ jobs: - name: Install Keptn id: install_keptn - uses: keptn-sandbox/action-install-keptn@v2.0.0 + uses: keptn-sandbox/action-install-keptn@v3.0.0 timeout-minutes: 5 with: KEPTN_VERSION: ${{ matrix.keptn-version }} @@ -169,7 +169,12 @@ jobs: kubectl describe nodes > k8s_debug/k8s_describe_nodes.txt kubectl cluster-info dump > k8s_debug/k8s_cluster_info_dump.txt kubectl get all -n keptn -o json > k8s_debug/k8s_keptn_objects.json + kubectl get events -n keptn --sort-by='.metadata.creationTimestamp' -A > k8s_debug/keptn_events.txt kubectl logs -n keptn -l app.kubernetes.io/instance=keptn --prefix=true --previous=false --all-containers > k8s_debug/k8s_keptn_logs.txt || true + kubectl logs -n keptn -l app.kubernetes.io/instance=keptn-service-template-go --prefix=true --previous=false --all-containers > k8s_debug/service_template_logs.txt || true + kubectl logs -n keptn -l app.kubernetes.io/instance=keptn-service-template-go --prefix=true --previous=true --all-containers > k8s_debug/service_template_logs_previous.txt || true + kubectl describe pod -n keptn -l app.kubernetes.io/instance=keptn-service-template-go > k8s_debug/keptn_service_template_describe.txt || true + kubectl logs deployment/keptn-service-template-go -n keptn > k8s_debug/keptn_service_template_deployment_logs.txt || true kubectl logs deployment/keptn-gitea-provisioner-service --prefix=true --previous=false --all-containers > k8s_debug/k8s_gitea_provisioner_logs.txt || true kubectl get statefulsets,configmaps,pods,networkpolicy,serviceaccounts,role,rolebindings,events,services -n ${GITEA_NAMESPACE} -o json > k8s_debug/k8s_objects_gitea.json kubectl logs statefulsets/gitea --prefix=true --previous=false --all-containers -n ${GITEA_NAMESPACE} > k8s_debug/k8s_logs_gitea.txt || true diff --git a/README.md b/README.md index c623b32..6093efa 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Examples: -* Webhooks: https://keptn.sh/docs/0.14.x/integrations/webhooks/ +* Webhooks: https://keptn.sh/docs/0.17.x/integrations/webhooks/ * Job-Executor-Service: https://github.com/keptn-sandbox/job-executor-service --- @@ -33,9 +33,9 @@ This implements a keptn-service-template-go for Keptn. If you want to learn more | Keptn Version* | [Keptn-service-template-go version](https://github.com/keptn-sandbox/keptn-service-template-go/releases) | |:--------------:|:--------------------------------------------------------------------------------------------------------:| -| 0.13.x | 0.13.0 | -| 0.14.x | 0.14.0 | -| 0.17.x | 0.17.0 | +| 0.13.x | 0.13.0 | +| 0.14.x | 0.14.0 | +| 0.17.x | 0.17.0 | \* This is the Keptn version we aim to be compatible with. Other versions should work too, but there is no guarantee. @@ -56,7 +56,7 @@ for example: helm install -n keptn keptn-service-template-go chart/ ``` -This should install the `keptn-service-template-go` together with a Keptn `distributor` into the `keptn` namespace, which you can verify using +This should install the `keptn-service-template-go` into the `keptn` namespace, which you can verify using ```console kubectl -n keptn get deployment keptn-service-template-go -o wide diff --git a/chart/README.md b/chart/README.md index f6ed323..f38aa9d 100644 --- a/chart/README.md +++ b/chart/README.md @@ -9,37 +9,25 @@ Helm Chart for the keptn keptn-service-template-go The following table lists the configurable parameters of the keptn-service-template-go chart and their default values. -| Parameter | Description | Default | -| ------------------------ | ----------------------- | -------------- | -| `image.repository` | Container image name | `"ghcr.io/keptn-sandbox/keptn-service-template-go"` | -| `image.pullPolicy` | Kubernetes image pull policy | `"IfNotPresent"` | -| `image.tag` | Container tag | `""` | -| `service.enabled` | Creates a kubernetes service for the keptn-service-template-go | `true` | -| `distributor.stageFilter` | Sets the stage this helm service belongs to | `""` | -| `distributor.serviceFilter` | Sets the service this helm service belongs to | `""` | -| `distributor.projectFilter` | Sets the project this helm service belongs to | `""` | -| `distributor.pubsubTopic` | Sets the events the service subscribes to. *NOTE: in case of remote control plane wildcards like "sh.keptn.>" don't work, please provide a comma-separated list of explicit events* | `"sh.keptn.>"` | -| `distributor.image.repository` | Container image name | `"docker.io/keptn/distributor"` | -| `distributor.image.pullPolicy` | Kubernetes image pull policy | `"IfNotPresent"` | -| `distributor.image.tag` | Container tag | `""` | -| `remoteControlPlane.enabled` | Enables remote execution plane mode | `false` | -| `remoteControlPlane.api.protocol` | Used protocol (http, https | `"https"` | -| `remoteControlPlane.api.hostname` | Hostname of the control plane cluster (and port) | `""` | -| `remoteControlPlane.api.apiValidateTls` | Defines if the control plane certificate should be validated | `true` | -| `remoteControlPlane.api.token` | Keptn api token | `""` | -| `imagePullSecrets` | Secrets to use for container registry credentials | `[]` | -| `serviceAccount.create` | Enables the service account creation | `true` | -| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | -| `serviceAccount.name` | The name of the service account to use. | `""` | -| `podAnnotations` | Annotations to add to the created pods | `{}` | -| `podSecurityContext` | Set the pod security context (e.g. fsgroups) | `{}` | -| `securityContext` | Set the security context (e.g. runasuser) | `{}` | -| `resources` | Resource limits and requests | `{}` | -| `nodeSelector` | Node selector configuration | `{}` | -| `tolerations` | Tolerations for the pods | `[]` | -| `affinity` | Affinity rules | `{}` | - - - - - +| Parameter | Description | Default | +|-----------------------------------------|----------------------------------------------------------------|-----------------------------------------------------| +| `image.repository` | Container image name | `"ghcr.io/keptn-sandbox/keptn-service-template-go"` | +| `image.pullPolicy` | Kubernetes image pull policy | `"IfNotPresent"` | +| `image.tag` | Container tag | `""` | +| `service.enabled` | Creates a kubernetes service for the keptn-service-template-go | `true` | +| `remoteControlPlane.enabled` | Enables remote execution plane mode | `false` | +| `remoteControlPlane.api.protocol` | Used protocol (http, https | `"https"` | +| `remoteControlPlane.api.hostname` | Hostname of the control plane cluster (and port) | `""` | +| `remoteControlPlane.api.token` | Keptn api token | `""` | +| `imagePullSecrets` | Secrets to use for container registry credentials | `[]` | +| `serviceAccount.create` | Enables the service account creation | `true` | +| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | +| `serviceAccount.name` | The name of the service account to use. | `""` | +| `podAnnotations` | Annotations to add to the created pods | `{}` | +| `podSecurityContext` | Set the pod security context (e.g. fsgroups) | `{}` | +| `securityContext` | Set the security context (e.g. runasuser) | `{}` | +| `resources` | Resource limits and requests | `{}` | +| `nodeSelector` | Node selector configuration | `{}` | +| `tolerations` | Tolerations for the pods | `[]` | +| `affinity` | Affinity rules | `{}` | +| `subscription.pubsubTopic` | Sets the events the service subscribes to | `"sh.keptn.>"` | diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index a67bc6e..7c95775 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -4,7 +4,6 @@ metadata: name: {{ include "keptn-service.fullname" . }} labels: {{- include "keptn-service.labels" . | nindent 4 }} - spec: replicas: 1 selector: @@ -31,113 +30,53 @@ spec: securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - livenessProbe: - httpGet: - path: /health - port: 8080 - readinessProbe: - httpGet: - path: /ready - port: 8080 imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: 80 env: - - name: CONFIGURATION_SERVICE - value: "http://localhost:8081/configuration-service" - name: env value: 'production' + - name: LOG_LEVEL + value: 'debug' + - name: PUBSUB_TOPIC + value: {{ .Values.subscription.pubsubTopic }} + - name: K8S_DEPLOYMENT_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: 'metadata.labels[''app.kubernetes.io/name'']' + - name: K8S_DEPLOYMENT_VERSION + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: 'metadata.labels[''app.kubernetes.io/version'']' + - name: K8S_DEPLOYMENT_COMPONENT + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: 'metadata.labels[''app.kubernetes.io/component'']' + - name: K8S_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: K8S_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + {{- if .Values.remoteControlPlane.enabled }} + - name: KEPTN_API_ENDPOINT + value: "{{ .Values.remoteControlPlane.api.protocol }}://{{ .Values.remoteControlPlane.api.hostname }}/api" + - name: KEPTN_API_TOKEN + value: "{{ .Values.remoteControlPlane.api.token }}" + - name: HTTP_SSL_VERIFY + value: "{{ .Values.remoteControlPlane.api.apiValidateTls | default "true" }}" + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} - - name: distributor - image: "{{ .Values.distributor.image.repository }}:{{ .Values.distributor.image.tag | default .Chart.AppVersion }}" - livenessProbe: - httpGet: - path: /health - port: 8080 - readinessProbe: - httpGet: - path: /ready - port: 8080 - lifecycle: - preStop: - exec: - command: [ "/bin/sleep","60" ] - imagePullPolicy: Always - ports: - - containerPort: 8080 - resources: - requests: - memory: "32Mi" - cpu: "50m" - limits: - memory: "128Mi" - cpu: "500m" - env: - - name: PUBSUB_URL - value: 'nats://keptn-nats' - - name: PUBSUB_TOPIC - value: {{ .Values.distributor.pubsubTopic }} - - name: PUBSUB_RECIPIENT - value: '127.0.0.1' - - name: STAGE_FILTER - value: "{{ .Values.distributor.stageFilter }}" - - name: PROJECT_FILTER - value: "{{ .Values.distributor.projectFilter }}" - - name: SERVICE_FILTER - value: "{{ .Values.distributor.serviceFilter }}" - - name: VERSION - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: 'metadata.labels[''app.kubernetes.io/version'']' - - name: DISTRIBUTOR_VERSION - value: {{ .Values.distributor.image.tag | default .Chart.AppVersion }} - - name: K8S_DEPLOYMENT_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: 'metadata.labels[''app.kubernetes.io/name'']' - - name: K8S_POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: K8S_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: K8S_NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - {{- if .Values.remoteControlPlane.enabled }} - - name: KEPTN_API_ENDPOINT - value: "{{ .Values.remoteControlPlane.api.protocol }}://{{ .Values.remoteControlPlane.api.hostname }}/api" - - name: KEPTN_API_TOKEN - value: "{{ .Values.remoteControlPlane.api.token }}" - - name: HTTP_SSL_VERIFY - value: "{{ .Values.remoteControlPlane.api.apiValidateTls | default "true" }}" - {{- end }} - {{- if (((.Values.distributor).config).queueGroup).enabled | default true }} - - name: PUBSUB_GROUP - valueFrom: - fieldRef: - fieldPath: metadata.labels['app.kubernetes.io/name'] - {{- end }} - - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - terminationGracePeriodSeconds: 60 diff --git a/chart/values.schema.json b/chart/values.schema.json index 9189c9c..ad7e1d9 100644 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -64,36 +64,6 @@ } } } - }, - "distributor": { - "type": "object", - "required": [ - "image" - ], - "properties": { - "image": { - "properties": { - "repository": { - "pattern": "[a-z][a-z0-9-./]{2,63}$" - }, - "pullPolicy": { - "enum": [ - "IfNotPresent", - "Always" - ] - } - } - }, - "stageFilter": { - "pattern": "^$|[A-Za-z0-9-.]{2,63}$" - }, - "serviceFilter": { - "pattern": "^$|[A-Za-z0-9-.]{2,63}$" - }, - "projectFilter": { - "pattern": "^$|[A-Za-z0-9-.]{2,63}$" - } - } } } } diff --git a/chart/values.yaml b/chart/values.yaml index 3790359..16b6777 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -5,26 +5,16 @@ image: service: enabled: true # Creates a Kubernetes Service for the keptn-service-template-go -distributor: - stageFilter: "" # Sets the stage this helm service belongs to - serviceFilter: "" # Sets the service this helm service belongs to - projectFilter: "" # Sets the project this helm service belongs to +subscription: pubsubTopic: "sh.keptn.>" # Sets the events the service subscribes to - image: - repository: docker.io/keptn/distributor # Container Image Name - pullPolicy: IfNotPresent # Kubernetes Image Pull Policy - tag: "0.17.0" # Container Tag - config: - queueGroup: - enabled: true # Enable connection via Nats queue group to support exactly-once message processing remoteControlPlane: enabled: false # Enables remote execution plane mode api: protocol: "http" # Used Protocol (http, https) - hostname: "" # Hostname of the control plane cluster (and Port) - apiValidateTls: true # Defines if the control plane certificate should be validated + hostname: "api-gateway-nginx.keptn" # Hostname of the control plane cluster (and Port) token: "" # Keptn API Token + apiValidateTls: true # Defines if the control plane certificate should be validated imagePullSecrets: [] # Secrets to use for container registry credentials diff --git a/eventhandler_test.go b/eventhandler_test.go deleted file mode 100644 index e741e17..0000000 --- a/eventhandler_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "github.com/keptn/go-utils/pkg/lib/v0_2_0/fake" - "io/ioutil" - "testing" - - keptn "github.com/keptn/go-utils/pkg/lib/keptn" - keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" - - cloudevents "github.com/cloudevents/sdk-go/v2" // make sure to use v2 cloudevents here -) - -/** - * loads a cloud event from the passed test json file and initializes a keptn object with it - */ -func initializeTestObjects(eventFileName string) (*keptnv2.Keptn, *cloudevents.Event, error) { - // load sample event - eventFile, err := ioutil.ReadFile(eventFileName) - if err != nil { - return nil, nil, fmt.Errorf("Cant load %s: %s", eventFileName, err.Error()) - } - - incomingEvent := &cloudevents.Event{} - err = json.Unmarshal(eventFile, incomingEvent) - if err != nil { - return nil, nil, fmt.Errorf("Error parsing: %s", err.Error()) - } - - // Add a Fake EventSender to KeptnOptions - var keptnOptions = keptn.KeptnOpts{ - EventSender: &fake.EventSender{}, - } - keptnOptions.UseLocalFileSystem = true - myKeptn, err := keptnv2.NewKeptn(incomingEvent, keptnOptions) - - return myKeptn, incomingEvent, err -} - -// Tests HandleActionTriggeredEvent -// TODO: Add your test-code -func TestHandleActionTriggeredEvent(t *testing.T) { - myKeptn, incomingEvent, err := initializeTestObjects("test-events/action.triggered.json") - if err != nil { - t.Error(err) - return - } - - specificEvent := &keptnv2.ActionTriggeredEventData{} - err = incomingEvent.DataAs(specificEvent) - if err != nil { - t.Errorf("Error getting keptn event data") - } - - err = HandleActionTriggeredEvent(myKeptn, *incomingEvent, specificEvent) - if err != nil { - t.Errorf("Error: " + err.Error()) - } - - gotEvents := len(myKeptn.EventSender.(*fake.EventSender).SentEvents) - - // Verify that HandleGetSliTriggeredEvent has sent 2 cloudevents - if gotEvents != 2 { - t.Errorf("Expected two events to be sent, but got %v", gotEvents) - } - - // Verify that the first CE sent is a .started event - if keptnv2.GetStartedEventType(keptnv2.ActionTaskName) != myKeptn.EventSender.(*fake.EventSender).SentEvents[0].Type() { - t.Errorf("Expected a action.started event type") - } - - // Verify that the second CE sent is a .finished event - if keptnv2.GetFinishedEventType(keptnv2.ActionTaskName) != myKeptn.EventSender.(*fake.EventSender).SentEvents[1].Type() { - t.Errorf("Expected a action.finished event type") - } -} - -// Tests HandleDeploymentTriggeredEvent -// TODO: Add your test-code -func TestHandleDeploymentTriggeredEvent(t *testing.T) { - myKeptn, incomingEvent, err := initializeTestObjects("test-events/evaluation.triggered.json") - if err != nil { - t.Error(err) - return - } - - specificEvent := &keptnv2.DeploymentTriggeredEventData{} - err = incomingEvent.DataAs(specificEvent) - if err != nil { - t.Errorf("Error getting keptn event data") - } - - err = HandleDeploymentTriggeredEvent(myKeptn, *incomingEvent, specificEvent) - if err != nil { - t.Errorf("Error: " + err.Error()) - } -} - -// Tests HandleEvaluationTriggeredEvent -// TODO: Add your test-code -func TestHandleEvaluationTriggeredEvent(t *testing.T) { - myKeptn, incomingEvent, err := initializeTestObjects("test-events/evaluation.triggered.json") - if err != nil { - t.Error(err) - return - } - - specificEvent := &keptnv2.EvaluationTriggeredEventData{} - err = incomingEvent.DataAs(specificEvent) - if err != nil { - t.Errorf("Error getting keptn event data") - } - - err = HandleEvaluationTriggeredEvent(myKeptn, *incomingEvent, specificEvent) - if err != nil { - t.Errorf("Error: " + err.Error()) - } -} - -// Tests the HandleGetSliTriggeredEvent Handler -// TODO: Add your test-code -func TestHandleGetSliTriggered(t *testing.T) { - myKeptn, incomingEvent, err := initializeTestObjects("test-events/get-sli.triggered.json") - if err != nil { - t.Error(err) - return - } - - specificEvent := &keptnv2.GetSLITriggeredEventData{} - err = incomingEvent.DataAs(specificEvent) - if err != nil { - t.Errorf("Error getting keptn event data") - } - - err = HandleGetSliTriggeredEvent(myKeptn, *incomingEvent, specificEvent) - if err != nil { - t.Errorf("Error: " + err.Error()) - } - - gotEvents := len(myKeptn.EventSender.(*fake.EventSender).SentEvents) - - // Verify that HandleGetSliTriggeredEvent has sent 2 cloudevents - if gotEvents != 2 { - t.Errorf("Expected two events to be sent, but got %v", gotEvents) - } - - // Verify that the first CE sent is a .started event - if keptnv2.GetStartedEventType(keptnv2.GetSLITaskName) != myKeptn.EventSender.(*fake.EventSender).SentEvents[0].Type() { - t.Errorf("Expected a get-sli.started event type") - } - - // Verify that the second CE sent is a .finished event - if keptnv2.GetFinishedEventType(keptnv2.GetSLITaskName) != myKeptn.EventSender.(*fake.EventSender).SentEvents[1].Type() { - t.Errorf("Expected a get-sli.finished event type") - } -} - -// Tests the HandleReleaseTriggeredEvent Handler -// TODO: Add your test-code -func TestHandleReleaseTriggeredEvent(t *testing.T) { - myKeptn, incomingEvent, err := initializeTestObjects("test-events/release.triggered.json") - if err != nil { - t.Error(err) - return - } - - specificEvent := &keptnv2.ReleaseTriggeredEventData{} - err = incomingEvent.DataAs(specificEvent) - if err != nil { - t.Errorf("Error getting keptn event data") - } - - err = HandleReleaseTriggeredEvent(myKeptn, *incomingEvent, specificEvent) - if err != nil { - t.Errorf("Error: " + err.Error()) - } -} diff --git a/eventhandlers.go b/eventhandlers.go deleted file mode 100644 index 8cb5419..0000000 --- a/eventhandlers.go +++ /dev/null @@ -1,224 +0,0 @@ -package main - -import ( - "fmt" - "log" - "time" - - 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.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 -} - -// OldHandleConfigureMonitoringEvent handles old configure-monitoring events -// TODO: add in your handler code -func OldHandleConfigureMonitoringEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptn.ConfigureMonitoringEventData) error { - log.Printf("Handling old configure-monitoring Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleConfigureMonitoringTriggeredEvent handles configure-monitoring.triggered events -// TODO: add in your handler code -func HandleConfigureMonitoringTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.ConfigureMonitoringTriggeredEventData) error { - log.Printf("Handling configure-monitoring.triggered Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleDeploymentTriggeredEvent handles deployment.triggered events -// TODO: add in your handler code -func HandleDeploymentTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.DeploymentTriggeredEventData) error { - log.Printf("Handling deployment.triggered Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleTestTriggeredEvent handles test.triggered events -// TODO: add in your handler code -func HandleTestTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.TestTriggeredEventData) error { - log.Printf("Handling test.triggered Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleApprovalTriggeredEvent handles approval.triggered events -// TODO: add in your handler code -func HandleApprovalTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.ApprovalTriggeredEventData) error { - log.Printf("Handling approval.triggered Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleEvaluationTriggeredEvent handles evaluation.triggered events -// TODO: add in your handler code -func HandleEvaluationTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.EvaluationTriggeredEventData) error { - log.Printf("Handling evaluation.triggered Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleReleaseTriggeredEvent handles release.triggered events -// TODO: add in your handler code -func HandleReleaseTriggeredEvent(myKeptn *keptnv2.Keptn, incomingEvent cloudevents.Event, data *keptnv2.ReleaseTriggeredEventData) error { - log.Printf("Handling release.triggered Event: %s", incomingEvent.Context.GetID()) - - return nil -} - -// HandleGetSliTriggeredEvent handles get-sli.triggered events if SLIProvider == keptn-service-template-go -// This function acts as an example showing how to handle get-sli events by sending .started and .finished events -// TODO: adapt handler code to your needs -func HandleGetSliTriggeredEvent(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 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 to be send when the task is started - _, err := myKeptn.SendTaskStartedEvent(data, ServiceName) - - if err != nil { - errMsg := fmt.Sprintf("Failed to send task started CloudEvent (%s), aborting...", err.Error()) - log.Println(errMsg) - return err - } - - // 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"] - - // 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) - - // FYI you do not need to "fail" if sli.yaml is missing, you can also assume smart defaults like we do - // in keptn-contrib/dynatrace-service and keptn-contrib/prometheus-service - 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 - - _, err = myKeptn.SendTaskFinishedEvent(&keptnv2.EventData{ - Status: keptnv2.StatusErrored, - Result: keptnv2.ResultFailed, - }, ServiceName) - - return err - } - - fmt.Println(sliConfigFileContent) - - // 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 := data.GetSLI.Indicators - sliResults := []*keptnv2.SLIResult{} - - for _, indicatorName := range indicators { - sliResult := &keptnv2.SLIResult{ - Metric: indicatorName, - Value: 123.4, // ToDo: Fetch the values from your monitoring tool here - } - sliResults = append(sliResults, sliResult) - } - - // 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{ - Status: keptnv2.StatusSucceeded, - Result: keptnv2.ResultPass, - }, - GetSLI: keptnv2.GetSLIFinished{ - IndicatorValues: sliResults, - Start: data.GetSLI.Start, - End: data.GetSLI.End, - }, - } - - _, err = myKeptn.SendTaskFinishedEvent(getSliFinishedEventData, ServiceName) - - if err != nil { - errMsg := fmt.Sprintf("Failed to send task finished CloudEvent (%s), aborting...", err.Error()) - log.Println(errMsg) - return err - } - - return nil -} - -// 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 *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 -} - -// HandleActionTriggeredEvent handles action.triggered events -// TODO: add in your handler code -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" { - // ----------------------------------------------------- - // 1. Send Action.Started Cloud-Event - // ----------------------------------------------------- - myKeptn.SendTaskStartedEvent(data, ServiceName) - - // ----------------------------------------------------- - // 2. Implement your remediation action here - // ----------------------------------------------------- - time.Sleep(5 * time.Second) // Example: Wait 5 seconds. Maybe the problem fixes itself. - - // ----------------------------------------------------- - // 3. Send Action.Finished Cloud-Event - // ----------------------------------------------------- - myKeptn.SendTaskFinishedEvent(&keptnv2.EventData{ - Status: keptnv2.StatusSucceeded, // alternative: keptnv2.StatusErrored - Result: keptnv2.ResultPass, // alternative: keptnv2.ResultFailed - Message: "Successfully sleeped!", - }, ServiceName) - - } else { - log.Printf("Retrieved unknown action %s, skipping...", data.Action.Action) - return nil - } - return nil -} diff --git a/go.mod b/go.mod index 6de916d..0f9675c 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ -module example.com/keptn-service-template-go +module github.com/keptn-service-template-go go 1.17 require ( github.com/cloudevents/sdk-go/v2 v2.10.0 - github.com/kelseyhightower/envconfig v1.4.0 - github.com/keptn/go-utils v0.17.0 + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/keptn/go-utils v0.17.1-0.20220718120931-866624f8ce42 github.com/mitchellh/mapstructure v1.5.0 github.com/stretchr/testify v1.8.0 gopkg.in/yaml.v3 v3.0.1 // indirect; pin v3.0.1 >= because of CVE-2022-28948 @@ -14,9 +14,12 @@ require ( k8s.io/client-go v0.24.1 ) +require github.com/sirupsen/logrus v1.8.1 + require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/avast/retry-go v3.0.0+incompatible // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/cloudevents/sdk-go/observability/opentelemetry/v2 v2.0.0-20211001212819-74757a691209 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -38,6 +41,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nats-io/nats.go v1.16.0 // indirect + github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0 // indirect go.opentelemetry.io/otel v1.7.0 // indirect @@ -46,9 +52,10 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.0 // indirect + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect diff --git a/go.sum b/go.sum index 03ba7b6..06a8785 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -209,10 +211,11 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/keptn/go-utils v0.17.0 h1:AfxqcL1OBIRHbPDmxRzaVhgAf7Wz/wWrwafdw9i1iP0= -github.com/keptn/go-utils v0.17.0/go.mod h1:EPhgzrqJYbHzK8qbWDDKihWeRJKIkVGR7vi+jaAPftw= +github.com/keptn/go-utils v0.17.1-0.20220718120931-866624f8ce42 h1:zo++qdVyq+hn6VDjo4SBFXUata+AXZl9WA+jpmi+2kM= +github.com/keptn/go-utils v0.17.1-0.20220718120931-866624f8ce42/go.mod h1:zb3sZlADFEhm4pdzOjLdNd1JfHHzqYnkmsMkdO2kYIg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -223,6 +226,7 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -238,6 +242,14 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I= +github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4= +github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g= +github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= @@ -259,12 +271,15 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -320,7 +335,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -434,6 +452,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -466,8 +485,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/handler/action_triggered_event_handler.go b/handler/action_triggered_event_handler.go new file mode 100644 index 0000000..6aee02f --- /dev/null +++ b/handler/action_triggered_event_handler.go @@ -0,0 +1,59 @@ +package handler + +import ( + keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" + "github.com/keptn/go-utils/pkg/sdk" + "time" +) + +type ActionTriggeredEventHandler struct { +} + +func NewActionTriggeredEventHandler() *ActionTriggeredEventHandler { + return &ActionTriggeredEventHandler{} +} + +// Execute handles action.triggered events +// TODO: Add in your handler code +func (g *ActionTriggeredEventHandler) Execute(k sdk.IKeptn, event sdk.KeptnEvent) (interface{}, *sdk.Error) { + k.Logger().Infof("Handling Action Triggered Event: %s", event.ID) + actionTriggeredEvent := &keptnv2.ActionTriggeredEventData{} + + if err := keptnv2.Decode(event.Data, actionTriggeredEvent); err != nil { + return nil, &sdk.Error{Err: err, StatusType: keptnv2.StatusErrored, ResultType: keptnv2.ResultFailed, Message: "failed to decode action.triggered event: " + err.Error()} + } + + k.Logger().Infof("Action=%s", actionTriggeredEvent.Action.Action) + + // check if action is supported + if actionTriggeredEvent.Action.Action == "action-xyz" { + k.Logger().Info("Action remediation triggered") + // ----------------------------------------------------- + // TODO: Implement your remediation action here + // ----------------------------------------------------- + time.Sleep(1 * time.Second) // Example: Wait 5 seconds. Maybe the problem fixes itself. + + // Return finished event + finishedEventData := getActionFinishedEvent(keptnv2.ResultPass, keptnv2.StatusSucceeded, *actionTriggeredEvent, "") + + return finishedEventData, nil + } + + k.Logger().Infof("Retrieved unknown action %s, skipping...", actionTriggeredEvent.Action.Action) + return nil, nil +} + +func getActionFinishedEvent(result keptnv2.ResultType, status keptnv2.StatusType, actionTriggeredEvent keptnv2.ActionTriggeredEventData, message string) keptnv2.ActionFinishedEventData { + + return keptnv2.ActionFinishedEventData{ + EventData: keptnv2.EventData{ + Project: actionTriggeredEvent.Project, + Stage: actionTriggeredEvent.Stage, + Service: actionTriggeredEvent.Service, + Labels: actionTriggeredEvent.Labels, + Status: status, + Result: result, + Message: message, + }, + } +} diff --git a/handler/action_triggered_event_handler_test.go b/handler/action_triggered_event_handler_test.go new file mode 100644 index 0000000..dc2e7f4 --- /dev/null +++ b/handler/action_triggered_event_handler_test.go @@ -0,0 +1,62 @@ +package handler + +import ( + "encoding/json" + keptnapi "github.com/keptn/go-utils/pkg/api/models" + keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" + "github.com/keptn/go-utils/pkg/sdk" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func newEvent(filename string) keptnapi.KeptnContextExtendedCE { + content, err := ioutil.ReadFile(filename) + if err != nil { + log.Fatal(err) + } + event := keptnapi.KeptnContextExtendedCE{} + err = json.Unmarshal(content, &event) + _ = err + return event +} + +func Test_Receiving_GetActionTriggeredEvent(t *testing.T) { + var returnedStatusCode = 200 + ts := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + if strings.Contains(r.URL.String(), "/admin/features/") { + w.WriteHeader(returnedStatusCode) + w.Write([]byte(`{}`)) + return + } + + defer r.Body.Close() + body, _ := ioutil.ReadAll(r.Body) + keptnCE := &keptnapi.KeptnContextExtendedCE{} + + _ = json.Unmarshal(body, keptnCE) + + w.WriteHeader(returnedStatusCode) + w.Write([]byte(`{}`)) + }), + ) + defer ts.Close() + + fakeKeptn := sdk.NewFakeKeptn("test-service-template-svc") + fakeKeptn.AddTaskHandler("sh.keptn.event.action.triggered", NewActionTriggeredEventHandler()) + + fakeKeptn.NewEvent(newEvent("../test/events/action_triggered.json")) + + fakeKeptn.AssertNumberOfEventSent(t, 2) + + fakeKeptn.AssertSentEventType(t, 0, keptnv2.GetStartedEventType("action")) + fakeKeptn.AssertSentEventType(t, 1, keptnv2.GetFinishedEventType("action")) + + fakeKeptn.AssertSentEventStatus(t, 1, keptnv2.StatusSucceeded) + fakeKeptn.AssertSentEventResult(t, 1, keptnv2.ResultPass) +} diff --git a/handler/get_sli_triggered_event_handler.go b/handler/get_sli_triggered_event_handler.go new file mode 100644 index 0000000..f34b888 --- /dev/null +++ b/handler/get_sli_triggered_event_handler.go @@ -0,0 +1,79 @@ +package handler + +import ( + api "github.com/keptn/go-utils/pkg/api/utils" + keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" + "github.com/keptn/go-utils/pkg/sdk" +) + +type GetSliEventHandler struct { +} + +func NewGetSliEventHandler() *GetSliEventHandler { + return &GetSliEventHandler{} +} + +// Execute handles get-sli.triggered events if SLIProvider == keptn-service-template-go +// This function acts as an example showing how to handle get-sli events +// TODO: Adapt handler code to your needs +func (g *GetSliEventHandler) Execute(k sdk.IKeptn, event sdk.KeptnEvent) (interface{}, *sdk.Error) { + k.Logger().Infof("Handling get-sli.triggered Event: %s", event.ID) + + sliTriggeredEvent := &keptnv2.GetSLITriggeredEventData{} + + if err := keptnv2.Decode(event.Data, sliTriggeredEvent); err != nil { + return nil, &sdk.Error{Err: err, StatusType: keptnv2.StatusErrored, ResultType: keptnv2.ResultFailed, Message: "failed to decode sli.triggered event: " + err.Error()} + } + + // Check if the event belongs to our SLI Provider + if sliTriggeredEvent.GetSLI.SLIProvider != "keptn-service-template-go" { + k.Logger().Infof("Not handling get-sli event as it is meant for %s", sliTriggeredEvent.GetSLI.SLIProvider) + return nil, nil + } + + // Get SLI File from keptn-service-template-go subdirectory of the config repo - to add the file use: + sliFile := "keptn-service-template-go/sli.yaml" + resourceScope := *api.NewResourceScope().Project(sliTriggeredEvent.Project).Stage(sliTriggeredEvent.Stage).Service(sliTriggeredEvent.Service).Resource(sliFile) + sliConfigFileContent, err := k.GetResourceHandler().GetResource(resourceScope) + + if err != nil { + k.Logger().Infof("Error while fetching SLI file: %e", err) + return nil, &sdk.Error{Err: err, StatusType: keptnv2.StatusErrored, ResultType: keptnv2.ResultFailed, Message: "error while fetching SLI file: " + err.Error()} + } + + k.Logger().Infof("SLI config content: %s", sliConfigFileContent) + + // TODO: Implement your functionality here + indicators := sliTriggeredEvent.GetSLI.Indicators + var sliResults []*keptnv2.SLIResult + + for _, indicatorName := range indicators { + sliResult := &keptnv2.SLIResult{ + Metric: indicatorName, + Value: 123.4, // TODO: Fetch the values from your monitoring tool here + } + sliResults = append(sliResults, sliResult) + } + + finishedEventData := getSliFinishedEvent(keptnv2.ResultPass, keptnv2.StatusSucceeded, *sliTriggeredEvent, "", sliResults) + + return finishedEventData, nil +} + +func getSliFinishedEvent(result keptnv2.ResultType, status keptnv2.StatusType, sliTriggeredEvent keptnv2.GetSLITriggeredEventData, message string, sliResult []*keptnv2.SLIResult) keptnv2.GetSLIFinishedEventData { + + return keptnv2.GetSLIFinishedEventData{ + EventData: keptnv2.EventData{ + Project: sliTriggeredEvent.Project, + Stage: sliTriggeredEvent.Stage, + Service: sliTriggeredEvent.Service, + Labels: sliTriggeredEvent.Labels, + Status: status, + Result: result, + Message: message, + }, + GetSLI: keptnv2.GetSLIFinished{ + IndicatorValues: sliResult, + }, + } +} diff --git a/handler/get_sli_triggered_event_handler_test.go b/handler/get_sli_triggered_event_handler_test.go new file mode 100644 index 0000000..e1123b5 --- /dev/null +++ b/handler/get_sli_triggered_event_handler_test.go @@ -0,0 +1,50 @@ +package handler + +import ( + "encoding/json" + keptnapi "github.com/keptn/go-utils/pkg/api/models" + keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" + "github.com/keptn/go-utils/pkg/sdk" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func Test_Receiving_GetSliTriggeredEvent(t *testing.T) { + var returnedStatusCode = 200 + ts := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + if strings.Contains(r.URL.String(), "/admin/features/") { + w.WriteHeader(returnedStatusCode) + w.Write([]byte(`{}`)) + return + } + + defer r.Body.Close() + body, _ := ioutil.ReadAll(r.Body) + keptnCE := &keptnapi.KeptnContextExtendedCE{} + + _ = json.Unmarshal(body, keptnCE) + + w.WriteHeader(returnedStatusCode) + w.Write([]byte(`{}`)) + }), + ) + defer ts.Close() + + fakeKeptn := sdk.NewFakeKeptn("test-service-template-svc") + fakeKeptn.AddTaskHandler("sh.keptn.event.get-sli.triggered", NewGetSliEventHandler()) + + fakeKeptn.NewEvent(newEvent("../test/events/get_sli_triggered.json")) + + fakeKeptn.AssertNumberOfEventSent(t, 2) + + fakeKeptn.AssertSentEventType(t, 0, keptnv2.GetStartedEventType("get-sli")) + fakeKeptn.AssertSentEventType(t, 1, keptnv2.GetFinishedEventType("get-sli")) + + fakeKeptn.AssertSentEventStatus(t, 1, keptnv2.StatusSucceeded) + fakeKeptn.AssertSentEventResult(t, 1, keptnv2.ResultPass) +} diff --git a/main.go b/main.go index 46c6011..c0ea88e 100644 --- a/main.go +++ b/main.go @@ -1,603 +1,39 @@ package main import ( - "context" - "encoding/json" - "errors" - "fmt" + "github.com/keptn-service-template-go/handler" + "github.com/keptn/go-utils/pkg/sdk" + "github.com/sirupsen/logrus" "log" - "net/http" "os" - "os/signal" - "syscall" - - cloudevents "github.com/cloudevents/sdk-go/v2" // make sure to use v2 cloudevents here - "github.com/kelseyhightower/envconfig" - keptnlib "github.com/keptn/go-utils/pkg/lib" - keptn "github.com/keptn/go-utils/pkg/lib/keptn" - keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" ) -var keptnOptions = keptn.KeptnOpts{} - -type gracefulShutdownKeyType struct{} - -var gracefulShutdownKey = gracefulShutdownKeyType{} - -// Opaque key type used for graceful shutdown context value -type keptnQuitType struct{} - -var serviceRunnerQuit = keptnQuitType{} - -type envConfig struct { - // Port on which to listen for cloudevents - Port int `envconfig:"RCV_PORT" default:"8080"` - // Path to which cloudevents are sent - Path string `envconfig:"RCV_PATH" default:"/"` - // Whether we are running locally (e.g., for testing) or on production - Env string `envconfig:"ENV" default:"local"` - // URL of the Keptn configuration service (this is where we can fetch files from the config repo) - ConfigurationServiceUrl string `envconfig:"CONFIGURATION_SERVICE" default:""` -} - -// ServiceName specifies the current services name (e.g., used as source when sending CloudEvents) -const ServiceName = "keptn-service-template-go" - -/** - * Parses a Keptn Cloud Event payload (data attribute) - */ -func parseKeptnCloudEventPayload(event cloudevents.Event, data interface{}) error { - err := event.DataAs(data) - if err != nil { - log.Fatalf("Got Data Error: %s", err.Error()) - return err - } - return nil -} - -/** - * This method gets called when a new event is received from the Keptn Event Distributor - * Depending on the Event Type will call the specific event handler functions, e.g: handleDeploymentFinishedEvent - * See https://github.com/keptn/spec/blob/0.2.0-alpha/cloudevents.md for details on the payload - */ -func processKeptnCloudEvent(ctx context.Context, event cloudevents.Event) error { - // create keptn handler - log.Printf("Initializing Keptn Handler") - myKeptn, err := keptnv2.NewKeptn(&event, keptnOptions) - if err != nil { - return errors.New("Could not create Keptn Handler: " + err.Error()) - } - - log.Printf("gotEvent(%s): %s - %s", event.Type(), myKeptn.KeptnContext, event.Context.GetID()) - - if err != nil { - log.Printf("failed to parse incoming cloudevent: %v", err) - return err - } - - /** - * CloudEvents types in Keptn 0.8.0 follow the following pattern: - * - sh.keptn.event.${EVENTNAME}.triggered - * - sh.keptn.event.${EVENTNAME}.started - * - sh.keptn.event.${EVENTNAME}.status.changed - * - sh.keptn.event.${EVENTNAME}.finished - * - * For convenience, types can be generated using the following methods: - * - triggered: keptnv2.GetTriggeredEventType(${EVENTNAME}) (e.g,. keptnv2.GetTriggeredEventType(keptnv2.DeploymentTaskName)) - * - started: keptnv2.GetStartedEventType(${EVENTNAME}) (e.g,. keptnv2.GetStartedEventType(keptnv2.DeploymentTaskName)) - * - status.changed: keptnv2.GetStatusChangedEventType(${EVENTNAME}) (e.g,. keptnv2.GetStatusChangedEventType(keptnv2.DeploymentTaskName)) - * - finished: keptnv2.GetFinishedEventType(${EVENTNAME}) (e.g,. keptnv2.GetFinishedEventType(keptnv2.DeploymentTaskName)) - * - * Keptn reserves some Cloud Event types, please read up on that here: https://keptn.sh/docs/0.8.x/manage/shipyard/ - * - * For those Cloud Events the keptn/go-utils library conveniently provides several data structures - * and strings in github.com/keptn/go-utils/pkg/lib/v0_2_0, e.g.: - * - deployment: DeploymentTaskName, DeploymentTriggeredEventData, DeploymentStartedEventData, DeploymentFinishedEventData - * - test: TestTaskName, TestTriggeredEventData, TestStartedEventData, TestFinishedEventData - * - ... (they all follow the same pattern) - * - * - * In most cases you will be interested in processing .triggered events (e.g., sh.keptn.event.deployment.triggered), - * which you an achieve as follows: - * if event.type() == keptnv2.GetTriggeredEventType(keptnv2.DeploymentTaskName) { ... } - * - * Processing the event payload can be achieved as follows: - * - * eventData := &keptnv2.DeploymentTriggeredEventData{} - * parseKeptnCloudEventPayload(event, eventData) - * - * See https://github.com/keptn/spec/blob/0.2.0-alpha/cloudevents.md for more details of Keptn Cloud Events and their payload - * Also, see https://github.com/keptn-sandbox/echo-service/blob/a90207bc119c0aca18368985c7bb80dea47309e9/pkg/events.go as an example how to create your own CloudEvents - **/ - - /** - * The following code presents a very generic implementation of processing almost all possible - * Cloud Events that are retrieved by this service. - * Please follow the documentation provided above for more guidance on the different types. - * Feel free to delete parts that you don't need. - **/ - switch event.Type() { - - // ------------------------------------------------------- - // sh.keptn.event.project.create - Note: This is due to change - case keptnv2.GetStartedEventType(keptnv2.ProjectCreateTaskName): // sh.keptn.event.project.create.started - log.Printf("Processing Project.Create.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ProjectCreateStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.ProjectCreateTaskName): // sh.keptn.event.project.create.finished - log.Printf("Processing Project.Create.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ProjectCreateFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - // ------------------------------------------------------- - // sh.keptn.event.service.create - Note: This is due to change - case keptnv2.GetStartedEventType(keptnv2.ServiceCreateTaskName): // sh.keptn.event.service.create.started - log.Printf("Processing Service.Create.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ServiceCreateStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.ServiceCreateTaskName): // sh.keptn.event.service.create.finished - log.Printf("Processing Service.Create.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ServiceCreateFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.approval - case keptnv2.GetTriggeredEventType(keptnv2.ApprovalTaskName): // sh.keptn.event.approval.triggered - log.Printf("Processing Approval.Triggered Event") - - eventData := &keptnv2.ApprovalTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleApprovalTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.ApprovalTaskName): // sh.keptn.event.approval.started - log.Printf("Processing Approval.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ApprovalStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.ApprovalTaskName): // sh.keptn.event.approval.finished - log.Printf("Processing Approval.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ApprovalFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.deployment - case keptnv2.GetTriggeredEventType(keptnv2.DeploymentTaskName): // sh.keptn.event.deployment.triggered - log.Printf("Processing Deployment.Triggered Event") - - eventData := &keptnv2.DeploymentTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleDeploymentTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.DeploymentTaskName): // sh.keptn.event.deployment.started - log.Printf("Processing Deployment.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.DeploymentStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.DeploymentTaskName): // sh.keptn.event.deployment.finished - log.Printf("Processing Deployment.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.DeploymentFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.test - case keptnv2.GetTriggeredEventType(keptnv2.TestTaskName): // sh.keptn.event.test.triggered - log.Printf("Processing Test.Triggered Event") - - eventData := &keptnv2.TestTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleTestTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.TestTaskName): // sh.keptn.event.test.started - log.Printf("Processing Test.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.TestStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.TestTaskName): // sh.keptn.event.test.finished - log.Printf("Processing Test.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.TestFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.evaluation - case keptnv2.GetTriggeredEventType(keptnv2.EvaluationTaskName): // sh.keptn.event.evaluation.triggered - log.Printf("Processing Evaluation.Triggered Event") - - eventData := &keptnv2.EvaluationTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleEvaluationTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.EvaluationTaskName): // sh.keptn.event.evaluation.started - log.Printf("Processing Evaluation.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.EvaluationStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.EvaluationTaskName): // sh.keptn.event.evaluation.finished - log.Printf("Processing Evaluation.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.EvaluationFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.release - case keptnv2.GetTriggeredEventType(keptnv2.ReleaseTaskName): // sh.keptn.event.release.triggered - log.Printf("Processing Release.Triggered Event") - - eventData := &keptnv2.ReleaseTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleReleaseTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.ReleaseTaskName): // sh.keptn.event.release.started - log.Printf("Processing Release.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ReleaseStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetStatusChangedEventType(keptnv2.ReleaseTaskName): // sh.keptn.event.release.status.changed - log.Printf("Processing Release.Status.Changed Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ReleaseStatusChangedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.ReleaseTaskName): // sh.keptn.event.release.finished - log.Printf("Processing Release.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ReleaseFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.get-action (sent by shipyard-controller to extract a remediation action from remediations.yaml) - case keptnv2.GetTriggeredEventType(keptnv2.GetActionTaskName): // sh.keptn.event.action.triggered - log.Printf("Processing Get-Action.Triggered Event") - - eventData := &keptnv2.GetActionTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.GetActionTaskName): // sh.keptn.event.action.started - log.Printf("Processing Get-Action.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.GetActionStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.GetActionTaskName): // sh.keptn.event.action.finished - log.Printf("Processing Get-Action.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.GetActionFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.action (sent by shipyard-controller to execute a remediation action) - case keptnv2.GetTriggeredEventType(keptnv2.ActionTaskName): // sh.keptn.event.action.triggered - log.Printf("Processing Action.Triggered Event") - - eventData := &keptnv2.ActionTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) +const getSliTriggeredEvent = "sh.keptn.event.get-sli.triggered" +const actionTriggeredEvent = "sh.keptn.event.action.triggered" +const serviceName = "keptn-service-template-go" +const envVarLogLevel = "LOG_LEVEL" - return HandleActionTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.ActionTaskName): // sh.keptn.event.action.started - log.Printf("Processing Action.Started Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ActionStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.ActionTaskName): // sh.keptn.event.action.finished - log.Printf("Processing Action.Finished Event") - // Please note: Processing .started, .status.changed and .finished events is only recommended when you want to - // notify an external service (e.g., for logging purposes). - - eventData := &keptnv2.ActionFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.problem / problem.open - // Please Note: This is deprecated; Use Action.Triggered instead - case keptnlib.ProblemEventType: // sh.keptn.event.problem - e.g., sent by Dynatrace to Keptn Api - log.Printf("Processing problem Event") - log.Printf("Subscribing to a problem.open or problem event is not recommended since Keptn 0.7. Please subscribe to event of type: sh.keptn.event.action.triggered") - - eventData := &keptnlib.ProblemEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleProblemEvent(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.get-sli (sent by lighthouse-service to fetch SLIs from the sli provider) - case keptnv2.GetTriggeredEventType(keptnv2.GetSLITaskName): // sh.keptn.event.get-sli.triggered - log.Printf("Processing Get-SLI.Triggered Event") - - eventData := &keptnv2.GetSLITriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleGetSliTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.GetSLITaskName): // sh.keptn.event.get-sli.started - log.Printf("Processing Get-SLI.Started Event") - - eventData := &keptnv2.GetSLIStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.GetSLITaskName): // sh.keptn.event.get-sli.finished - log.Printf("Processing Get-SLI.Finished Event") - - eventData := &keptnv2.GetSLIFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // sh.keptn.event.configure-monitoring - case keptnlib.ConfigureMonitoringEventType: // old configure-monitoring CE; compatibility with 0.8.0-alpha - log.Printf("Processing old configure-monitoring Event") - log.Printf("Note: This will be deprecated with Keptn 0.8.0") - - eventData := &keptnlib.ConfigureMonitoringEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Handle old configure-monitoring event - return OldHandleConfigureMonitoringEvent(myKeptn, event, eventData) - - case keptnv2.GetTriggeredEventType(keptnv2.ConfigureMonitoringTaskName): // sh.keptn.event.configure-monitoring.triggered - log.Printf("Processing configure-monitoring.Triggered Event") - - eventData := &keptnv2.ConfigureMonitoringTriggeredEventData{} - parseKeptnCloudEventPayload(event, eventData) - - return HandleConfigureMonitoringTriggeredEvent(myKeptn, event, eventData) - case keptnv2.GetStartedEventType(keptnv2.ConfigureMonitoringTaskName): // sh.keptn.event.configure-monitoring.started - log.Printf("Processing configure-monitoring.Started Event") - - eventData := &keptnv2.ConfigureMonitoringStartedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - case keptnv2.GetFinishedEventType(keptnv2.ConfigureMonitoringTaskName): // sh.keptn.event.configure-monitoring.finished - log.Printf("Processing configure-monitoring.Finished Event") - - eventData := &keptnv2.ConfigureMonitoringFinishedEventData{} - parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - // ------------------------------------------------------- - // your custom cloud event, e.g., sh.keptn.your-event - // see https://github.com/keptn-sandbox/echo-service/blob/a90207bc119c0aca18368985c7bb80dea47309e9/pkg/events.go - // for an example on how to generate your own CloudEvents and structs - case keptnv2.GetTriggeredEventType("your-event"): // sh.keptn.event.your-event.triggered - log.Printf("Processing your-event.triggered Event") - - // eventData := &keptnv2.YourEventTriggeredEventData{} - // parseKeptnCloudEventPayload(event, eventData) - - break - case keptnv2.GetStartedEventType(keptnv2.ConfigureMonitoringTaskName): // sh.keptn.event.your-event.started - log.Printf("Processing your-event.started Event") - - // eventData := &keptnv2.YourEventStartedEventData{} - // parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - // return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - break - case keptnv2.GetFinishedEventType(keptnv2.ConfigureMonitoringTaskName): // sh.keptn.event.your-event.finished - log.Printf("Processing your-event.finished Event") - - // eventData := &keptnv2.YourEventFinishedEventData{} - // parseKeptnCloudEventPayload(event, eventData) - - // Just log this event - // return GenericLogKeptnCloudEventHandler(myKeptn, event, eventData) - - break - } - - // Unknown Event -> Throw Error! - var errorMsg string - errorMsg = fmt.Sprintf("Unhandled Keptn Cloud Event: %s", event.Type()) - - log.Print(errorMsg) - return errors.New(errorMsg) -} - -/** - * Usage: ./main - * no args: starts listening for cloudnative events on localhost:port/path - * - * Environment Variables - * env=runlocal -> will fetch resources from local drive instead of configuration service - */ func main() { - var env envConfig - if err := envconfig.Process("", &env); err != nil { - log.Fatalf("Failed to process env var: %s", err) - } - - os.Exit(_main(os.Args[1:], env)) -} - -/** - * Opens up a listener on localhost:port/path and passes incoming requets to gotEvent - */ -func _main(args []string, env envConfig) int { - // configure keptn options - if env.Env == "local" { - log.Println("env=local: Running with local filesystem to fetch resources") - keptnOptions.UseLocalFileSystem = true - } - - keptnOptions.ConfigurationServiceURL = env.ConfigurationServiceUrl - - log.Println("Starting keptn-service-template-go...") - log.Printf(" on Port = %d; Path=%s", env.Port, env.Path) - - ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - - log.Printf("Creating new http handler") - - // configure http server to receive cloudevents - p, err := cloudevents.NewHTTP( - cloudevents.WithPath(env.Path), cloudevents.WithPort(env.Port), cloudevents.WithGetHandlerFunc(HTTPGetHandler), - ) - - if err != nil { - log.Fatalf("failed to create client, %v", err) - } - c, err := cloudevents.NewClient(p) - if err != nil { - log.Fatalf("failed to create client, %v", err) - } - - err = c.StartReceiver(ctx, processKeptnCloudEvent) - if err != nil { - log.Fatalf("CloudEvent receiver stopped with error: %v", err) - } - log.Printf("Shutdown complete.") - return 0 -} - -// HTTPGetHandler will handle all requests for '/health' and '/ready' -func HTTPGetHandler(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/health": - healthEndpointHandler(w, r) - case "/ready": - healthEndpointHandler(w, r) - default: - endpointNotFoundHandler(w, r) - } -} - -// HealthHandler rerts a basic health check back -func healthEndpointHandler(w http.ResponseWriter, r *http.Request) { - type StatusBody struct { - Status string `json:"status"` - } - - status := StatusBody{Status: "OK"} - - body, _ := json.Marshal(status) - - w.Header().Set("content-type", "application/json") - - _, err := w.Write(body) - if err != nil { - log.Println(err) - } -} - -// endpointNotFoundHandler will return 404 for requests -func endpointNotFoundHandler(w http.ResponseWriter, r *http.Request) { - type StatusBody struct { - Status string `json:"status"` - } - - status := StatusBody{Status: "NOT FOUND"} - - body, _ := json.Marshal(status) - - w.Header().Set("content-type", "application/json") - - _, err := w.Write(body) - if err != nil { - log.Println(err) - } + if os.Getenv(envVarLogLevel) != "" { + logLevel, err := logrus.ParseLevel(os.Getenv(envVarLogLevel)) + if err != nil { + logrus.WithError(err).Error("could not parse log level provided by 'LOG_LEVEL' env var") + logrus.SetLevel(logrus.InfoLevel) + } else { + logrus.SetLevel(logLevel) + } + } + + log.Printf("Starting %s", serviceName) + + log.Fatal(sdk.NewKeptn( + serviceName, + sdk.WithTaskHandler( + actionTriggeredEvent, + handler.NewActionTriggeredEventHandler()), + sdk.WithTaskHandler( + getSliTriggeredEvent, + handler.NewGetSliEventHandler()), + sdk.WithLogger(logrus.New()), + ).Start()) } diff --git a/test/events/action_triggered.json b/test/events/action_triggered.json new file mode 100644 index 0000000..5dcae6a --- /dev/null +++ b/test/events/action_triggered.json @@ -0,0 +1,29 @@ +{ + "type": "sh.keptn.event.action.triggered", + "specversion": "1.0", + "source": "test-events", + "id": "f2b878d3-03c0-4e8f-bc3f-454bc1b3d79b", + "time": "2019-06-07T07:02:15.64489Z", + "contenttype": "application/json", + "shkeptncontext": "08735340-6f9e-4b32-97ff-3b6c292bc50i", + "data": { + "project": "user-managed", + "stage": "dev", + "service": "nginx", + "labels": { + "testId": "4711", + "buildId": "build-17", + "owner": "JohnDoe" + }, + "status": "succeeded", + "result": "pass", + "action": { + "name": "action-xyz", + "action": "action-xyz", + "description": "action-xyz", + "value": "1" + }, + "problem": { + } + } +} \ No newline at end of file diff --git a/test/events/get_sli_triggered.json b/test/events/get_sli_triggered.json new file mode 100644 index 0000000..5d6be72 --- /dev/null +++ b/test/events/get_sli_triggered.json @@ -0,0 +1,27 @@ +{ + "data": { + "get-sli": { + "customFilters": [], + "end": "2021-01-15T15:09:45.000Z", + "indicators": [ + "response_time_p95", + "some_other_metric" + ], + "sliProvider": "keptn-service-template-go", + "start": "2021-01-15T15:04:45.000Z" + }, + "labels": null, + "message": "", + "project": "user-managed", + "result": "", + "service": "nginx", + "stage": "dev", + "status": "" + }, + "id": "409539ae-c0b9-436e-abc6-c257292e28ff", + "source": "test-events", + "specversion": "1.0", + "time": "2021-01-15T15:09:46.144Z", + "type": "sh.keptn.event.get-sli.triggered", + "shkeptncontext": "da7aec34-78c4-4182-a2c8-51eb88f5871d" +} \ No newline at end of file