1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-30 05:59:39 +02:00

Allow resilient error handling in pipelines (#583)

* add resilience to error handling
* adapt documentation
* make ConfigLoader more resilient
This commit is contained in:
Oliver Nocon 2019-04-04 08:38:54 +02:00 committed by GitHub
parent ec3d350252
commit 138656b49b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 18 deletions

View File

@ -6,16 +6,6 @@
none
## ${docGenParameters}
## Step configuration
none
## Exceptions
none
## Example
```groovy
@ -53,3 +43,7 @@ Further information:
* GitHub repository for pipeline steps: https://...
----------------------------------------------------------
```
## ${docGenParameters}
## ${docGenConfiguration}

View File

@ -221,8 +221,10 @@ steps:
testOptions: 'specs'
handlePipelineStepErrors:
echoDetails: true
failOnError: true
libraryDocumentationUrl: 'https://sap.github.io/jenkins-library/'
libraryRepositoryUrl: 'https://github.com/SAP/jenkins-library/'
mandatorySteps: []
healthExecuteCheck:
healthEndpoint: ''
influxWriteData:

View File

@ -26,7 +26,11 @@ class ConfigurationLoader implements Serializable {
@NonCPS
static Map generalConfiguration(script){
try {
return script?.commonPipelineEnvironment?.configuration?.general ?: [:]
} catch (groovy.lang.MissingPropertyException mpe) {
return [:]
}
}
@NonCPS
@ -43,7 +47,12 @@ class ConfigurationLoader implements Serializable {
private static Map loadConfiguration(script, String type, String entryName, ConfigurationType configType){
switch (configType) {
case ConfigurationType.CUSTOM_CONFIGURATION:
try {
return script?.commonPipelineEnvironment?.configuration?.get(type)?.get(entryName) ?: [:]
} catch (groovy.lang.MissingPropertyException mpe) {
return [:]
}
case ConfigurationType.DEFAULT_CONFIGURATION:
return DefaultValueCache.getInstance()?.getDefaultValues()?.get(type)?.get(entryName) ?: [:]
default:

View File

@ -1,4 +1,6 @@
#!groovy
import hudson.AbortException
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.not
import static org.hamcrest.Matchers.containsString
@ -81,4 +83,62 @@ class HandlePipelineStepErrorsTest extends BasePiperTest {
assertThat(loggingRule.log, containsString('[something:anything]'))
}
}
@Test
void testHandleErrorsIgnoreFailure() {
def errorOccured = false
try {
stepRule.step.handlePipelineStepErrors([
stepName: 'test',
stepParameters: [jenkinsUtilsStub: jenkinsUtils, script: nullScript],
failOnError: false
]) {
throw new AbortException('TestError')
}
} catch (err) {
errorOccured = true
}
assertThat(errorOccured, is(false))
assertThat(nullScript.currentBuild.result, is('UNSTABLE'))
}
@Test
void testHandleErrorsIgnoreFailureBlacklist() {
def errorOccured = false
//define blacklist in defaults
helper.registerAllowedMethod("readYaml", [Map], { Map m ->
return [steps: [handlePipelineStepErrors: [mandatorySteps: ['step1', 'test']]]]
})
try {
stepRule.step.handlePipelineStepErrors([
stepName: 'test',
stepParameters: [jenkinsUtilsStub: jenkinsUtils, script: nullScript],
failOnError: false
]) {
throw new AbortException('TestError')
}
} catch (err) {
errorOccured = true
}
assertThat(errorOccured, is(true))
}
@Test
void testHandleErrorsIgnoreFailureNoScript() {
def errorOccured = false
try {
stepRule.step.handlePipelineStepErrors([
stepName: 'test',
stepParameters: [jenkinsUtilsStub: jenkinsUtils],
failOnError: false
]) {
throw new AbortException('TestError')
}
} catch (err) {
errorOccured = true
}
assertThat(errorOccured, is(false))
}
}

View File

@ -5,12 +5,21 @@ import com.sap.piper.ConfigurationHelper
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
import hudson.AbortException
@Field STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = []
@Field Set STEP_CONFIG_KEYS = []
@Field Set PARAMETER_KEYS = [
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
/**
* Defines the behavior, in case an error occurs which is handled by this step. When set to `false` an error results in an "UNSTABLE" build result and the pipeline can continue.
* @possibleValues `true`, `false`
*/
'failOnError',
/** Defines a list of mandatory steps (step names) which have to be successful (=stop the pipeline), even if `failOnError: false` */
'mandatorySteps'
])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([
/**
* If it is set to true details will be output to the console. See example below.
* @possibleValues `true`, `false`
@ -20,13 +29,13 @@ import groovy.transform.Field
'libraryDocumentationUrl',
/** Defines the url of the library's repository that will be used to generate the corresponding links to the step implementation.*/
'libraryRepositoryUrl',
/** Defines the name of the step executed that will be shown in the console output.*/
/** Defines the name of the step for which the error handling is active. It will be shown in the console log.*/
'stepName',
/** */
/** Defines the documented step, in case the documentation reference should point to a different step. */
'stepNameDoc',
/** Defines the parameters from the step to be executed. The list of parameters is then shown in the console output.*/
/** Passes the parameters of the step which uses the error handling onto the error handling. The list of parameters is then shown in the console output.*/
'stepParameters'
]
])
/**
* Used by other steps to make error analysis easier. Lists parameters and other data available to the step in which the error occurs.
@ -34,8 +43,12 @@ import groovy.transform.Field
@GenerateDocumentation
void call(Map parameters = [:], body) {
// load default & individual configuration
def cpe = parameters.stepParameters?.script?.commonPipelineEnvironment ?: commonPipelineEnvironment
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(cpe, GENERAL_CONFIG_KEYS)
.mixinStepConfig(cpe, STEP_CONFIG_KEYS)
.mixinStageConfig(cpe, parameters.stepParameters?.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.withMandatoryProperty('stepParameters')
.withMandatoryProperty('stepName')
@ -48,6 +61,19 @@ void call(Map parameters = [:], body) {
echo "--- Begin library step of: ${config.stepName} ---"
body()
} catch (AbortException ae) {
if (config.echoDetails)
message += formatErrorMessage(config, ae)
writeErrorToInfluxData(config, ae)
if (config.failOnError || config.stepName in config.mandatorySteps) {
throw ae
}
if (config.stepParameters?.script) {
config.stepParameters?.script.currentBuild.result = 'UNSTABLE'
} else {
currentBuild.result = 'UNSTABLE'
}
} catch (Throwable error) {
if (config.echoDetails)
message += formatErrorMessage(config, error)