From e5f45636e59ea36f0d07eb7c3ad2c3b578fc7e96 Mon Sep 17 00:00:00 2001 From: Christian Kreuzberger Date: Thu, 14 Oct 2021 12:49:40 +0200 Subject: [PATCH 1/2] #42 Allow sending a finished event Signed-off-by: Christian Kreuzberger --- README.md | 15 ++++-- src/sh/keptn/Keptn.groovy | 108 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ed959fe..a10c3a3 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,18 @@ def keptnContext = keptn.sendStartEvaluationEvent starttime:"2019-06-07T07:00:00 // Example #4: Mark a starting timestamp before executing your tests // Following example will fill starttime with the time when you called markEvaluationStartTime and as end is empty will default to Now() keptn.markEvaluationStartTime() -... here is where you would execute any existing tests + +// ... +// ^^^ here is where you would execute any existing tests + +// Keptn 0.7.x and prior: send start evaluation event to Keptn def keptnContext = keptn.sendStartEvaluationEvent starttime:"", endtime:"" echo "Open Keptns Bridge: ${keptn_bridge}/trace/${keptnContext}" +// Keptn 0.8.x and newer: Send a test.finished event +def keptnContext = keptn.sendFinishedEvent eventType: "test", keptnContext: "${params.shkeptncontext}", triggeredId: "${params.triggeredid}", result:"pass", status:"succeeded" +echo "Open Keptns Bridge: ${keptn_bridge}/trace/${keptnContext}" + // Performance Testing as a Service Use Case // ------------------------------------------- @@ -132,8 +140,8 @@ echo "Open Keptns Bridge: ${keptn_bridge}/trace/${keptnContext}" def keptnContext = keptn.sendDeploymentFinishedEvent testStrategy:"performance", deploymentURI:"http://yourapp.yourdomain.local" echo "Open Keptns Bridge: ${keptn_bridge}/trace/${keptnContext}" - -// Progressive Delivery Use Case +// ------------------------------------------- +// Progressive Delivery Use Case (Keptn 0.7.x and prior) // ------------------------------------------- // If you want Keptn to deploy, test and evaluate then we can simply inform Keptn about a new configuration (=container image) you have // Typically you would use your Jenkins to build and push a container to your container registry. After that you notify Keptn about it @@ -141,6 +149,7 @@ def keptnContext = keptn.sendDeliveryTriggeredEvent testStrategy:"${params.TestS String keptn_bridge = env.KEPTN_BRIDGE echo "Open Keptns Bridge: ${keptn_bridge}/trace/${keptnContext}" +// -------------------------------------------- // Waiting for Quality Gate Result // -------------------------------------------- def result = keptn.waitForEvaluationDoneEvent setBuildResult:true, waitTime:waitTime diff --git a/src/sh/keptn/Keptn.groovy b/src/sh/keptn/Keptn.groovy index 9bb0e47..2f52dc4 100644 --- a/src/sh/keptn/Keptn.groovy +++ b/src/sh/keptn/Keptn.groovy @@ -1,10 +1,17 @@ package sh.keptn +import groovy.transform.Field import java.time.temporal.ChronoUnit import org.jenkinsci.plugins.plaincredentials.StringCredentials import com.cloudbees.plugins.credentials.CredentialsProvider import com.cloudbees.plugins.credentials.domains.DomainRequirement +@groovy.transform.Field +def KEPTN_SPEC_VERSION = "0.2.3" + +@groovy.transform.Field +def KEPTN_EVENT_SOURCE = "jenkins-library" + /** * Downloads a file from the given url and stores it in the local workspace */ @@ -209,7 +216,7 @@ def keptnInit(Map args) { | "type": "${monitoring}" |}, |"datacontenttype": "application/json", - | "source": "Jenkins", + | "source": "${KEPTN_EVENT_SOURCE}", | "specversion": "1.0", | "type": "sh.keptn.event.monitoring.configure" |} @@ -606,9 +613,10 @@ def sendStartEvaluationEvent(Map args) { | "tag" : "${tag}", | }, | "datacontenttype": "application/json", - | "source": "Jenkins", + | "source": "${KEPTN_EVENT_SOURCE}", | "specversion": "1.0", - | "type": "sh.keptn.event.${stage}.evaluation.triggered" + | "type": "sh.keptn.event.${stage}.evaluation.triggered", + | "shkeptnspecversion": "${KEPTN_SPEC_VERSION}" |} """.stripMargin() @@ -738,6 +746,94 @@ def waitForEvaluationDoneEvent(Map args) { return score } +/** + * sendFinishedEvent(KEPTN_INIT_PARAMS, keptnContext, eventType, triggeredId, [result, status, message, labels]) + * Will send a finished event of type eventType (e.g., eventType.finished) + * will stores the API result in keptn.context.json + */ +def sendFinishedEvent(Map args) { + // load KEPTN_INIT_PARAMS such as project, service, stage, API endpoint, API token + def keptnInit = keptnLoadFromInit(args) + + String keptn_endpoint = keptnInit['keptn_endpoint'] + String keptn_api_token = keptnInit['keptn_api_token'] + String project = keptnInit['project'] + String stage = keptnInit['stage'] + String service = keptnInit['service'] + + // load labels from args (if set) + def labels = args.containsKey('labels') ? args.labels : [:] + + // verify keptnContext is set in args + if (!args.containsKey('keptnContext')) { + error("sendFinishedEvent requires keptnContext to be passed") + return "" + } + + // verify eventType is set in args + if (!args.containsKey('eventType')) { + error("sendFinishedEvent requires eventType to be passed") + return "" + } + + // verify triggeredID is set in args + if (!args.containsKey('triggeredId')) { + error("sendFinishedEvent requires triggeredId to be passed") + return "" + } + + String KEPTN_CONTEXT = args.keptnContext + String eventType = args.eventType + String triggeredId = args.triggeredId + + String result = args.containsKey('result') ? args.result : "pass" + String status = args.containsKey('status') ? args.status : "succeeded" + String message = args.containsKey('message') ? args.message : "" + + echo "Sending a ${eventType}.finished event to Keptn for ${project}.${stage}.${service} (status=${status},result=${result})" + + def requestBody = """{ + | "data": { + | "project": "${project}", + | "stage": "${stage}", + | "service": "${service}", + | "labels": { + | "jobname" : "${JOB_NAME}", + | "buildNumber": "${BUILD_NUMBER}", + | "joburl" : "${BUILD_URL}" + | }, + | "result": "${result}", + | "status": "${status}", + | "message": "${message}" + | }, + | "datacontenttype": "application/json", + | "source": "${KEPTN_EVENT_SOURCE}", + | "specversion": "1.0", + | "shkeptncontext": "${KEPTN_CONTEXT}", + | "shkeptnspecversion": "${KEPTN_SPEC_VERSION}", + | "triggeredid": "${triggeredId}", + | "type": "sh.keptn.event.${eventType}.finished" + |} + """.stripMargin() + + // lets add our custom labels + requestBody = addCustomLabels(requestBody, labels) + + def response = httpRequest contentType: 'APPLICATION_JSON', + customHeaders: [[maskValue: true, name: 'x-token', value: "${keptn_api_token}"]], + httpMode: 'POST', + requestBody: requestBody, + responseHandle: 'STRING', + url: "${keptn_endpoint}/v1/event", + validResponseCodes: "100:404", + ignoreSslErrors: true + + // write response to keptn.context.json & add to artifacts + def keptnContext = writeKeptnContextFiles(response) + + return keptnContext +} + /** * sendDeploymentFinishedEvent(project, stage, service, deploymentURI, testStrategy [labels, keptn_endpoint, keptn_api_token]) * Example: sendDeploymentFinishedEvent deploymentURI:"http://mysampleapp.mydomain.local" testStrategy:"performance" @@ -787,7 +883,8 @@ def sendDeploymentFinishedEvent(Map args) { | "datacontenttype": "application/json", | "source": "jenkins-library", | "specversion": "1.0", - | "type": "sh.keptn.event.deployment.finished" + | "type": "sh.keptn.event.deployment.finished", + | "shkeptnspecversion": "${KEPTN_SPEC_VERSION}" |} """.stripMargin() @@ -860,7 +957,8 @@ def sendDeploymentTriggeredEvent(Map args) { | "datacontenttype": "application/json", | "source": "jenkins-library", | "specversion": "1.0", - | "type": "sh.keptn.event.deployment.triggered" + | "type": "sh.keptn.event.deployment.triggered", + | "shkeptnspecversion": "${KEPTN_SPEC_VERSION}" |} """.stripMargin() From 5374a4536e94d20bcde00578d1f0efd4bc411c02 Mon Sep 17 00:00:00 2001 From: Christian Kreuzberger Date: Fri, 29 Oct 2021 09:08:42 +0200 Subject: [PATCH 2/2] Prepare version 5.0 Signed-off-by: Christian Kreuzberger --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a10c3a3..5fc5a40 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,11 @@ You can find out the latest release on the [GitHub releases](https://github.com/ | 3.5 | 0.7.x | Keptn API Token now configurable via Jenkins Credentials | | 4.0 | 0.8.0 | Now supporting Keptn 0.8.0 | | 4.1 | 0.8.x, 0.9.x | Supporting Keptn 0.9.x, bug fixes | +| 5.0 | 0.9.x, 0.10.0 | Supporting Keptn 0.10.0, bug fixes, Cleanups | Please make sure to always specify a version when including the library in your Jenkinsfile, e.g. ```groovy -@Library('keptn-library@4.1') +@Library('keptn-library@5.0') import sh.keptn.Keptn def keptn = new sh.keptn.Keptn() ``` @@ -77,7 +78,7 @@ The KEPTN_BRIDGE is the link to your keptn bridge so that the Library can genera Once you have everything configured use it in your Jenkins Pipeline like this ```groovy -@Library('keptn-library@4.1') +@Library('keptn-library@5.0') import sh.keptn.Keptn def keptn = new sh.keptn.Keptn()