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:
parent
ec3d350252
commit
138656b49b
@ -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}
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user