add step healthExecuteCheck (#339)

This step allows to perform a basic health check on an installed application.
It verifies that your app has a simple health endpoint available and that there is no error when calling it.
This commit is contained in:
Oliver Nocon
2018-10-12 16:06:41 +02:00
committed by GitHub
parent a56b9c0c8c
commit 17e8390511
5 changed files with 199 additions and 0 deletions
@@ -0,0 +1,62 @@
# healthExecuteCheck
## Description
Calls the health endpoint url of the application.
The intention of the check is to verify that a suitable health endpoint is available. Such a health endpoint is required for operation purposes.
This check is used as a real-life test for your productive health endpoints.
!!! note "Check Depth"
Typically, tools performing simple health checks are not too smart. Therefore it is important to choose an endpoint for checking wisely.
This check therefore only checks if the application/service url returns `HTTP 200`.
This is in line with health check capabilities of platforms which are used for example in load balancing scenarios. Here you can find an [example for Amazon AWS](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-healthchecks.html).
## Prerequisites
Endpoint for health check is configured.
!!! warning
The health endpoint needs to be available without authentication!
!!! tip
If using Spring Boot framework, ideally the provided `/health` endpoint is used and extended by development. Further information can be found in the [Spring Boot documenation for Endpoints](http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html)
## Example
Pipeline step:
```groovy
healthExecuteCheck testServerUrl: 'https://testserver.com'
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|healthEndpoint|no|``||
|testServerUrl|no|||
Details:
* `script` defines the global script environment of the Jenkinsfile run. Typically `this` is passed to this parameter. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for storing the measured duration.
* Health check function is called providing full qualified `testServerUrl` (and optionally with `healthEndpoint` if endpoint is not the standard url) to the health check.
* In case response of the call is different than `HTTP 200 OK` the **health check fails and the pipeline stops**.
## Step configuration
We recommend to define values of step parameters via [config.yml file](../configuration.md).
In following sections the configuration is possible:
| parameter | general | step | stage |
| ----------|-----------|---------|-----------------|
|script||||
|healthEndpoint|X|X|X|
|testServerUrl|X|X|X|
+1
View File
@@ -12,6 +12,7 @@ nav:
- dockerExecuteOnKubernetes: steps/dockerExecuteOnKubernetes.md
- durationMeasure: steps/durationMeasure.md
- handlePipelineStepErrors: steps/handlePipelineStepErrors.md
- healthExecuteCheck: steps/healthExecuteCheck.md
- influxWriteData: steps/influxWriteData.md
- mavenExecute: steps/mavenExecute.md
- mtaBuild: steps/mtaBuild.md
@@ -139,6 +139,8 @@ steps:
workspace: '**/*.*'
stashExcludes:
workspace: 'nohup.out'
healthExecuteCheck:
healthEndpoint: ''
influxWriteData:
influxServer: 'jenkins'
mavenExecute:
+88
View File
@@ -0,0 +1,88 @@
#!groovy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.BasePiperTest
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.JenkinsStepRule
import util.Rules
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class HealthExecuteCheckTest extends BasePiperTest {
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jlr)
.around(jsr)
.around(thrown)
@Before
void init() throws Exception {
// register Jenkins commands with mock values
def command1 = "curl -so /dev/null -w '%{response_code}' http://testserver"
def command2 = "curl -so /dev/null -w '%{response_code}' http://testserver/endpoint"
helper.registerAllowedMethod('sh', [Map.class], {map ->
return map.script == command1 || map.script == command2 ? "200" : "404"
})
}
@Test
void testHealthCheckOk() throws Exception {
def testUrl = 'http://testserver/endpoint'
jsr.step.healthExecuteCheck(
script: nullScript,
testServerUrl: testUrl
)
assertThat(jlr.log, containsString("Health check for ${testUrl} successful"))
}
@Test
void testHealthCheck404() throws Exception {
def testUrl = 'http://testserver/404'
thrown.expect(Exception)
thrown.expectMessage('Health check failed: 404')
jsr.step.healthExecuteCheck(
script: nullScript,
testServerUrl: testUrl
)
}
@Test
void testHealthCheckWithEndPoint() throws Exception {
jsr.step.healthExecuteCheck(
script: nullScript,
testServerUrl: 'http://testserver',
healthEndpoint: 'endpoint'
)
assertThat(jlr.log, containsString("Health check for http://testserver/endpoint successful"))
}
@Test
void testHealthCheckWithEndPointTrailingSlash() throws Exception {
jsr.step.healthExecuteCheck(
script: nullScript,
testServerUrl: 'http://testserver/',
healthEndpoint: 'endpoint'
)
assertThat(jlr.log, containsString("Health check for http://testserver/endpoint successful"))
}
}
+46
View File
@@ -0,0 +1,46 @@
import com.sap.piper.ConfigurationHelper
import groovy.transform.Field
@Field String STEP_NAME = 'healthExecuteCheck'
@Field Set STEP_CONFIG_KEYS = [
'healthEndpoint',
'testServerUrl'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.withMandatoryProperty('testServerUrl')
.use()
def checkUrl = config.testServerUrl
if(config.healthEndpoint){
if(!checkUrl.endsWith('/'))
checkUrl += '/'
checkUrl += config.healthEndpoint
}
def statusCode = curl(checkUrl)
if (statusCode != '200') {
error "Health check failed: ${statusCode}"
} else {
echo "Health check for ${checkUrl} successful"
}
}
}
def curl(url){
return sh(
returnStdout: true,
script: "curl -so /dev/null -w '%{response_code}' ${url}"
).trim()
}