diff --git a/documentation/bin/createDocu.groovy b/documentation/bin/createDocu.groovy index 86b9f4b39..ee8516417 100644 --- a/documentation/bin/createDocu.groovy +++ b/documentation/bin/createDocu.groovy @@ -72,7 +72,7 @@ class TemplateHelper { parameters.keySet().toSorted().each { def props = parameters.get(it) - t += "| `${it}` | ${props.GENERAL_CONFIG ? 'X' : ''} | ${props.STEP_CONFIG ? 'X' : ''} | ${props.STAGE_CONFIG ? 'X' : ''} |\n" + t += "| `${it}` | ${props.GENERAL_CONFIG ? 'X' : ''} | ${props.STEP_CONFIG ? 'X' : ''} | ${props.STAGE_CONFIG ? 'X' : ''} |\n" } t.trim() diff --git a/documentation/docs/steps/dockerExecuteOnKubernetes.md b/documentation/docs/steps/dockerExecuteOnKubernetes.md index 83f44ab78..22ddbbbfb 100644 --- a/documentation/docs/steps/dockerExecuteOnKubernetes.md +++ b/documentation/docs/steps/dockerExecuteOnKubernetes.md @@ -2,7 +2,7 @@ ## Description -Executes a closure inside a container in a kubernetes pod. Proxy environment variables defined on the Jenkins machine are also available in the container. +Content here is generated from corresponnding step, see `vars`. ## Prerequisites @@ -13,65 +13,11 @@ Executes a closure inside a container in a kubernetes pod. Proxy environment var ## Parameters -| parameter | mandatory | default | possible values | -| ----------|-----------|---------|-----------------| -|script|yes||| -|containerCommand|no||| -|containerCommands|no||| -|containerEnvVars|no||| -|containerPullImageFlags|no|true|boolean value: `true`, `false` | -|containerMap|no|`[:]`|| -|containerName|no||| -|containerPortMappings|no||| -|containerShell|no||| -|containerWorkspaces|no||| -|dockerEnvVars|no|`[:]`|| -|dockerImage|yes||| -|dockerPullImage|no|true|boolean value: `true`, `false` | -|dockerWorkspace|no|`''`|| -|jenkinsKubernetes|no|`[jnlpAgent:s4sdk/jenkins-agent-k8s:latest]`|| -|stashExcludes|no|`[workspace:nohup.out]`|| -|stashIncludes|no|`[workspace:**/*.*]`|| - -* `script` defines the global script environment of the Jenkins file run. Typically `this` is passed to this parameter. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for storing the measured duration. -* `containerCommand`: allows to specify start command for container created with dockerImage parameter to overwrite Piper default (`/usr/bin/tail -f /dev/null`). -* `containerCommands` specifies start command for containers to overwrite Piper default (`/usr/bin/tail -f /dev/null`). If container's defaultstart command should be used provide empty string like: `['selenium/standalone-chrome': '']`. -* `containerEnvVars` specifies environment variables per container. If not provided `dockerEnvVars` will be used. -* `containerPullImageFlags` specifies the pullImage flag per container. -* `containerMap` A map of docker image to the name of the container. The pod will be created with all the images from this map and they are labled based on the value field of each map entry. - Example: `['maven:3.5-jdk-8-alpine': 'mavenExecute', 'selenium/standalone-chrome': 'selenium', 'famiko/jmeter-base': 'checkJMeter', 's4sdk/docker-cf-cli': 'cloudfoundry']` -* `containerName`: optional configuration in combination with containerMap to define the container where the commands should be executed in -* `containerPortMappings`: Map which defines per docker image the port mappings, like `containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]]` -* `containerShell` allows to specify the shell to be executed for container with containerName -* `containerWorkspaces` specifies workspace (=home directory of user) per container. If not provided `dockerWorkspace` will be used. If empty, home directory will not be set. -* `dockerImage` Name of the docker image that should be used. If empty, Docker is not used. -* `dockerEnvVars` Environment variables to set in the container, e.g. [http_proxy:'proxy:8080'] -* `dockerPullImage`: Set this to 'false' to bypass a docker image pull. Usefull during development process. Allows testing of images which are available in the local registry only. -* `dockerWorkspace` Docker options to be set when starting the container. It can be a list or a string. +Content here is generated from corresponnding step, see `vars`. ## 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|||| -|containerCommands||X|X| -|containerEnvVars||X|X| -|containerPullImageFlags||X|X| -|containerMap||X|X| -|containerName||X|X| -|containerPortMappings||X|X| -|containerWorkspaces||X|X| -|dockerEnvVars||X|X| -|dockerImage||X|X| -|dockerPullImage||X|X| -|dockerWorkspace||X|X| -|jenkinsKubernetes|X||| -|stashExcludes||X|X| -|stashIncludes||X|X| +Content here is generated from corresponnding step, see `vars`. ## Side effects diff --git a/documentation/docs/steps/newmanExecute.md b/documentation/docs/steps/newmanExecute.md index 290d8fe2b..b89b4d5c5 100644 --- a/documentation/docs/steps/newmanExecute.md +++ b/documentation/docs/steps/newmanExecute.md @@ -55,7 +55,9 @@ testsPublishResults script: this, junit: [pattern: '**/newman/TEST-*.xml'] 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 | +In following sections the configuration is possible: + +| parameter | general | step | stage | |-----------|---------|------|-------| | `dockerImage` |  | X | X | | `failOnError` |  | X | X | diff --git a/documentation/docs/steps/testsPublishResults.md b/documentation/docs/steps/testsPublishResults.md index 5270af979..a171c6ad2 100644 --- a/documentation/docs/steps/testsPublishResults.md +++ b/documentation/docs/steps/testsPublishResults.md @@ -33,6 +33,7 @@ Available parameters: | parameter | mandatory | default | possible values | | ----------|-----------|---------|-----------------| | script | yes | | | +| `failOnError` | no | `false` | `true`, `false` | | junit | no | `false` | true, false | | jacoco | no | `false` | true, false | | cobertura | no | `false` | true, false | @@ -43,6 +44,7 @@ Available parameters: with the `this` parameter, as in `script: this`. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for retrieving, for example, configuration parameters. +* `failOnError` - If `failOnError` it set to `true` the step will fail the build if JUnit detected any failing tests. * `junit` - Publishes test results files in JUnit format with the [JUnit Plugin](https://plugins.jenkins.io/junit). * `jacoco` - Publishes code coverage with the [JaCoCo plugin](https://plugins.jenkins.io/jacoco) . * `cobertura` - Publishes code coverage with the [Cobertura plugin](https://plugins.jenkins.io/cobertura). diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index 0453978fb..d8b637e41 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -35,6 +35,7 @@ nav: - transportRequestCreate: steps/transportRequestCreate.md - transportRequestRelease: steps/transportRequestRelease.md - transportRequestUploadFile: steps/transportRequestUploadFile.md + - uiVeri5ExecuteTests: steps/uiVeri5ExecuteTests.md - 'Scenarios': - 'Build and Deploy Hybrid Applications with Jenkins and SAP Solution Manager': scenarios/changeManagement.md - 'Create a Pipeline for SAP UI5 or SAP Fiori on SAP Cloud Platform': scenarios/ui5-sap-cp/Readme.md diff --git a/resources/default_pipeline_environment.yml b/resources/default_pipeline_environment.yml index 4f77f47df..9c781f17b 100644 --- a/resources/default_pipeline_environment.yml +++ b/resources/default_pipeline_environment.yml @@ -317,6 +317,7 @@ steps: toJson: false toHtml: false testsPublishResults: + failOnError: false junit: pattern: '**/TEST-*.xml' updateResults: false diff --git a/src/com/sap/piper/JenkinsUtils.groovy b/src/com/sap/piper/JenkinsUtils.groovy index 074172af4..0535aec73 100644 --- a/src/com/sap/piper/JenkinsUtils.groovy +++ b/src/com/sap/piper/JenkinsUtils.groovy @@ -3,6 +3,7 @@ package com.sap.piper import com.cloudbees.groovy.cps.NonCPS import jenkins.model.Jenkins import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException +import hudson.tasks.junit.TestResultAction @API @NonCPS @@ -10,6 +11,14 @@ static def isPluginActive(pluginId) { return Jenkins.instance.pluginManager.plugins.find { p -> p.isActive() && p.getShortName() == pluginId } } +static boolean hasTestFailures(build){ + //build: https://javadoc.jenkins.io/plugin/workflow-support/org/jenkinsci/plugins/workflow/support/steps/build/RunWrapper.html + //getRawBuild: https://javadoc.jenkins.io/plugin/workflow-job/org/jenkinsci/plugins/workflow/job/WorkflowRun.html + //getAction: http://www.hudson-ci.org/javadoc/hudson/tasks/junit/TestResultAction.html + def action = build?.getRawBuild()?.getAction(TestResultAction.class) + return action && action.getFailCount() != 0 +} + def nodeAvailable() { try { sh "echo 'Node is available!'" diff --git a/test/groovy/TestsPublishResultsTest.groovy b/test/groovy/TestsPublishResultsTest.groovy index 1ab50266e..3a4b9ff10 100644 --- a/test/groovy/TestsPublishResultsTest.groovy +++ b/test/groovy/TestsPublishResultsTest.groovy @@ -3,6 +3,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain +import org.junit.rules.ExpectedException import util.BasePiperTest import util.JenkinsReadYamlRule @@ -16,12 +17,14 @@ class TestsPublishResultsTest extends BasePiperTest { Map publisherStepOptions List archiveStepPatterns + private ExpectedException thrown = ExpectedException.none() private JenkinsStepRule stepRule = new JenkinsStepRule(this) @Rule public RuleChain ruleChain = Rules .getCommonRules(this) .around(new JenkinsReadYamlRule(this)) + .around(thrown) .around(stepRule) @Before @@ -126,4 +129,40 @@ class TestsPublishResultsTest extends BasePiperTest { assertTrue('Cobertura options are not empty', publisherStepOptions.cobertura == null) assertTrue('JMeter options are not empty', publisherStepOptions.jmeter == null) } + + @Test + void testBuildResultStatus() throws Exception { + stepRule.step.testsPublishResults(script: nullScript) + assertJobStatusSuccess() + } + + @Test + void testBuildWithTestFailuresAndWithoutFailOnError() throws Exception { + nullScript.currentBuild.getRawBuild = { + return [getAction: { type -> + return [getFailCount: { + return 6 + }] + }] + } + + stepRule.step.testsPublishResults(script: nullScript) + assertJobStatusSuccess() + } + + @Test + void testBuildWithTestFailuresAndWithFailOnError() throws Exception { + nullScript.currentBuild.getRawBuild = { + return [getAction: { type -> + return [getFailCount: { + return 6 + }] + }] + } + + thrown.expect(hudson.AbortException) + thrown.expectMessage('[testsPublishResults] Some tests failed!') + + stepRule.step.testsPublishResults(script: nullScript, failOnError: true) + } } diff --git a/test/groovy/UiVeri5ExecuteTestsTest.groovy b/test/groovy/UiVeri5ExecuteTestsTest.groovy index 217c14c6c..2155ad6fb 100644 --- a/test/groovy/UiVeri5ExecuteTestsTest.groovy +++ b/test/groovy/UiVeri5ExecuteTestsTest.groovy @@ -1,5 +1,4 @@ #!groovy -package steps import static org.hamcrest.Matchers.* diff --git a/vars/dockerExecuteOnKubernetes.groovy b/vars/dockerExecuteOnKubernetes.groovy index 12cb3a786..864e045e0 100644 --- a/vars/dockerExecuteOnKubernetes.groovy +++ b/vars/dockerExecuteOnKubernetes.groovy @@ -1,6 +1,7 @@ import static com.sap.piper.Prerequisites.checkScript import com.sap.piper.ConfigurationHelper +import com.sap.piper.GenerateDocumentation import com.sap.piper.JenkinsUtils import com.sap.piper.Utils import com.sap.piper.k8s.SystemEnv @@ -9,25 +10,77 @@ import hudson.AbortException @Field def STEP_NAME = getClass().getName() @Field def PLUGIN_ID_KUBERNETES = 'kubernetes' + @Field Set GENERAL_CONFIG_KEYS = [ 'jenkinsKubernetes' ] @Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([ - 'containerCommand', // specify start command for container created with dockerImage parameter to overwrite Piper default (`/usr/bin/tail -f /dev/null`). - 'containerCommands', //specify start command for containers to overwrite Piper default (`/usr/bin/tail -f /dev/null`). If container's default start command should be used provide empty string like: `['selenium/standalone-chrome': '']` - 'containerEnvVars', //specify environment variables per container. If not provided dockerEnvVars will be used - 'containerPullImageFlags', // specifies the pullImage flag per container. - 'containerMap', //specify multiple images which then form a kubernetes pod, example: containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute','selenium/standalone-chrome': 'selenium'] - 'containerName', //optional configuration in combination with containerMap to define the container where the commands should be executed in - 'containerPortMappings', //map which defines per docker image the port mappings, like containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]] - 'containerShell', // allows to specify the shell to be executed for container with containerName - 'containerWorkspaces', //specify workspace (=home directory of user) per container. If not provided dockerWorkspace will be used. If empty, home directory will not be set. - 'dockerImage', - 'dockerPullImage', - 'dockerWorkspace', + /** + * Allows to specify start command for container created with dockerImage parameter to overwrite Piper default (`/usr/bin/tail -f /dev/null`). + */ + 'containerCommand', + /** + * Specifies start command for containers to overwrite Piper default (`/usr/bin/tail -f /dev/null`). + * If container's defaultstart command should be used provide empty string like: `['selenium/standalone-chrome': '']`. + */ + 'containerCommands', + /** + * Specifies environment variables per container. If not provided `dockerEnvVars` will be used. + */ + 'containerEnvVars', + /** + * A map of docker image to the name of the container. The pod will be created with all the images from this map and they are labled based on the value field of each map entry. + * Example: `['maven:3.5-jdk-8-alpine': 'mavenExecute', 'selenium/standalone-chrome': 'selenium', 'famiko/jmeter-base': 'checkJMeter', 's4sdk/docker-cf-cli': 'cloudfoundry']` + */ + 'containerMap', + /** + * Optional configuration in combination with containerMap to define the container where the commands should be executed in. + */ + 'containerName', + /** + * Map which defines per docker image the port mappings, e.g. `containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]]`. + */ + 'containerPortMappings', + /** + * Specifies the pullImage flag per container. + */ + 'containerPullImageFlags', + /** + * Allows to specify the shell to be executed for container with containerName. + */ + 'containerShell', + /** + * Specifies a dedicated user home directory per container which will be passed as value for environment variable `HOME`. If not provided `dockerWorkspace` will be used. + */ + 'containerWorkspaces', + /** + * Environment variables to set in the container, e.g. [http_proxy:'proxy:8080']. + */ 'dockerEnvVars', + /** + * Name of the docker image that should be used. If empty, Docker is not used. + */ + 'dockerImage', + /** + * Set this to 'false' to bypass a docker image pull. + * Usefull during development process. Allows testing of images which are available in the local registry only. + */ + 'dockerPullImage', + /** + * Specifies a dedicated user home directory for the container which will be passed as value for environment variable `HOME`. + */ + 'dockerWorkspace', + /** + * Specific stashes that should be considered for the step execution. + */ 'stashContent', + /** + * + */ 'stashExcludes', + /** + * + */ 'stashIncludes' ]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.minus([ @@ -35,6 +88,11 @@ import hudson.AbortException 'stashExcludes' ]) +/** + * Executes a closure inside a container in a kubernetes pod. + * Proxy environment variables defined on the Jenkins machine are also available in the container. + */ +@GenerateDocumentation void call(Map parameters = [:], body) { handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) { @@ -175,7 +233,7 @@ private List getContainerList(config) { return result } -/** +/* * Returns a list of envVar object consisting of set * environment variables, params (Parametrized Build) and working directory. * (Kubernetes-Plugin only!) diff --git a/vars/testsPublishResults.groovy b/vars/testsPublishResults.groovy index 8c08098a1..31d7dd121 100644 --- a/vars/testsPublishResults.groovy +++ b/vars/testsPublishResults.groovy @@ -3,6 +3,7 @@ import static com.sap.piper.Prerequisites.checkScript import com.cloudbees.groovy.cps.NonCPS import com.sap.piper.ConfigurationHelper +import com.sap.piper.JenkinsUtils import com.sap.piper.MapUtils import com.sap.piper.Utils import groovy.transform.Field @@ -13,7 +14,9 @@ import groovy.transform.Field @Field def STEP_NAME = getClass().getName() @Field Set GENERAL_CONFIG_KEYS = TOOLS -@Field Set STEP_CONFIG_KEYS = TOOLS +@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([ + 'failOnError' +]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS /** @@ -24,10 +27,7 @@ import groovy.transform.Field */ void call(Map parameters = [:]) { handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) { - - def script = checkScript(this, parameters) - if (script == null) - script = this + def script = checkScript(this, parameters) ?: this prepare(parameters) @@ -46,13 +46,15 @@ void call(Map parameters = [:]) { stepParam1: parameters?.script == null ], configuration) - // UNIT TESTS publishJUnitReport(configuration.get('junit')) - // CODE COVERAGE publishJacocoReport(configuration.get('jacoco')) publishCoberturaReport(configuration.get('cobertura')) - // PERFORMANCE publishJMeterReport(configuration.get('jmeter')) + + if (configuration.failOnError && JenkinsUtils.hasTestFailures(script.currentBuild)) { + script.currentBuild.result = 'FAILURE' + error "[${STEP_NAME}] Some tests failed!" + } } }