1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-03-03 15:02:35 +02:00

merge remote-tracking branch 'github/master' into HEAD

This commit is contained in:
Marcus Holl 2018-10-22 10:30:08 +02:00
commit a2393c3f92
75 changed files with 2820 additions and 413 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
.idea/
bin/
.settings
logs
reports

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -61,6 +61,10 @@ Deployment can be done
- cfOrg
- cfSpace
!!! note
Due to [an incompatible change](https://github.com/cloudfoundry/cli/issues/1445) in the Cloud Foundry CLI, multiple buildpacks are not supported by this step.
If your `application` contains a list of `buildpacks` instead a single `buildpack`, this will be automatically re-written by the step when blue-green deployment is used.
* `deployTool` defines the tool which should be used for deployment.
* `deployType` defines the type of deployment, either `standard` deployment which results in a system downtime or a zero-downtime `blue-green` deployment.
* `dockerImage` defines the Docker image containing the deployment tools (like cf cli, ...) and `dockerWorkspace` defines the home directory of the default user of the `dockerImage`
@ -106,7 +110,11 @@ The following parameters can also be specified as step/stage/general parameters
## Example
```groovy
artifactSetVersion script: this, buildTool: 'maven'
cloudFoundryDeploy(
script: script,
deployType: 'blue-green',
cloudFoundry: [apiEndpoint: 'https://test.server.com', appName:'cfAppName', credentialsId: 'cfCredentialsId', manifest: 'cfManifest', org: 'cfOrg', space: 'cfSpace'],
deployTool: 'cf_native'
)
```

View File

@ -2,32 +2,73 @@
## Description
Executes a closure inside a docker container with the specified docker image.
Executes a closure inside a docker container with the specified docker image.
The workspace is mounted into the docker image.
Proxy environment variables defined on the Jenkins machine are also available in the Docker container.
## Parameters
| parameter | mandatory | default | possible values |
| -------------------|-----------|-----------------------------------|----------------------------|
| `script` | no | empty `globalPipelineEnvironment` | |
| `dockerImage` | no | '' | |
| `dockerEnvVars` | no | [:] | |
| `dockerOptions` | no | '' | |
| `dockerVolumeBind` | no | [:] | |
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|containerPortMappings|no|||
|dockerEnvVars|no|`[:]`||
|dockerImage|no|`''`||
|dockerName|no|||
|dockerOptions|no|`''`||
|dockerVolumeBind|no|`[:]`||
|dockerWorkspace|no|||
|jenkinsKubernetes|no|`[jnlpAgent:s4sdk/jenkins-agent-k8s:latest]`||
|sidecarEnvVars|no|||
|sidecarImage|no|||
|sidecarName|no|||
|sidecarOptions|no|||
|sidecarVolumeBind|no|||
|sidecarWorkspace|no|||
* `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.
* `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']
* `containerPortMappings`: Map which defines per docker image the port mappings, like `containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]]`
* `dockerEnvVars`: Environment variables to set in the container, e.g. [http_proxy:'proxy:8080']
* `dockerImage`: Name of the docker image that should be used. If empty, Docker is not used and the command is executed directly on the Jenkins system.
* `dockerName`: only relevant for Kubernetes case: Name of the container launching `dockerImage`
* `dockerOptions` Docker options to be set when starting the container. It can be a list or a string.
* `dockerVolumeBind` Volumes that should be mounted into the container.
* `dockerWorkspace`: only relevant for Kubernetes case: specifies a dedicated user home directory for the container which will be passed as value for environment variable `HOME`
* `sidecarEnvVars` defines environment variables for the sidecar container, similar to `dockerEnvVars`
* `sidecarImage`: Name of the docker image of the sidecar container. Do not provide this value if no sidecar container is required.
* `sidecarName`: as `dockerName` for the sidecar container
* `sidecarOptions`: as `dockerOptions` for the sidecar container
* `sidecarVolumeBind`: as `dockerVolumeBind` for the sidecar container
* `sidecarWorkspace`: as `dockerWorkspace` for the sidecar container
## Kubernetes support
If the Jenkins is setup on a Kubernetes cluster, then you can execute the closure inside a container of a pod by setting an environment variable `ON_K8S` to `true`. However, it will ignore both `dockeOptions` and `dockerVolumeBind` values.
If the Jenkins is setup on a Kubernetes cluster, then you can execute the closure inside a container of a pod by setting an environment variable `ON_K8S` to `true`. However, it will ignore `containerPortMappings`, `dockerOptions` and `dockerVolumeBind` values.
## Step configuration
none
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||||
|containerPortMappings||X|X|
|dockerEnvVars||X|X|
|dockerImage||X|X|
|dockerName||X|X|
|dockerOptions||X|X|
|dockerVolumeBind||X|X|
|dockerWorkspace||X|X|
|jenkinsKubernetes|X|||
|sidecarEnvVars||X|X|
|sidecarImage||X|X|
|sidecarName||X|X|
|sidecarOptions||X|X|
|sidecarVolumeBind||X|X|
|sidecarWorkspace||X|X|
## Return value
none
@ -38,7 +79,7 @@ none
## Exceptions
none
## Example 1: Run closure inside a docker container
## Example 1: Run closure inside a docker container
```groovy
dockerExecute(dockerImage: 'maven:3.5-jdk-7'){
@ -48,7 +89,7 @@ dockerExecute(dockerImage: 'maven:3.5-jdk-7'){
## Example 2: Run closure inside a container in a kubernetes pod
```sh
# set environment variable
# set environment variable
export ON_K8S=true"
```
@ -58,7 +99,26 @@ dockerExecute(script: this, dockerImage: 'maven:3.5-jdk-7'){
}
```
In the above example, the `dockerEcecute` step will internally invoke [dockerExecuteOnKubernetes](dockerExecuteOnKubernetes.md) step and execute the closure inside a pod.
In the above example, the `dockerEcecute` step will internally invoke [dockerExecuteOnKubernetes](dockerExecuteOnKubernetes.md) step and execute the closure inside a pod.
## Example 3: Run closure inside a container which is attached to a sidecar container (as for example used in [seleniumExecuteTests](seleniumExecuteTests.md):
```groovy
dockerExecute(
script: script,
containerPortMappings: [containerPortMappings:'selenium/standalone-chrome':[containerPort: 4444, hostPort: 4444]],
dockerImage: 'node:8-stretch',
dockerName: 'node',
dockerWorkspace: '/home/node',
sidecarImage: 'selenium/standalone-chrome',
sidecarName: 'selenium',
) {
git url: 'https://github.wdf.sap.corp/XXXXX/WebDriverIOTest.git'
sh '''npm install
node index.js
'''
}
```

View File

@ -4,29 +4,64 @@
Executes a closure inside a container in a kubernetes pod. Proxy environment variables defined on the Jenkins machine are also available in the container.
## Prerequisites
## Prerequisites
* The Jenkins should be running on kubernetes.
* An environment variable `ON_K8S` should be created on Jenkins and initialized to `true`.
* An environment variable `ON_K8S` should be created on Jenkins and initialized to `true`. This could for example be done via _Jenkins_ - _Manage Jenkins_ - _Configure System_ - _Global properties_ - _Environment variables_
![Jenkins environment variable configuration](../images/k8s_env.png)
## Parameters
| parameter | mandatory | default | possible values |
| -------------------|-----------|-----------------------------------|----------------------------|
| `script` | no | empty `globalPipelineEnvironment` | |
| `dockerImage` | yes | | |
| `dockerEnvVars` | no | [:] | |
| `dockerWorkspace` | no | '' | |
| `containerMap` | no | [:] | |
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|containerCommands|no|||
|containerEnvVars|no|||
|containerMap|no|`[:]`||
|containerName|no|||
|containerPortMappings|no|||
|containerWorkspaces|no|||
|dockerEnvVars|no|`[:]`||
|dockerImage|yes|||
|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.
* `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.
* `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]]]`
* `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']
* `dockerWorkspace` Docker options to be set when starting the container. It can be a list or a string.
* `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.
Ex `['maven:3.5-jdk-8-alpine': 'mavenExecute', 'famiko/jmeter-base': 'checkJMeter', 's4sdk/docker-cf-cli': 'cloudfoundry']`
## Step configuration
none
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|
|containerMap||X|X|
|containerName||X|X|
|containerPortMappings||X|X|
|containerWorkspaces||X|X|
|dockerEnvVars||X|X|
|dockerImage||X|X|
|dockerWorkspace||X|X|
|jenkinsKubernetes|X|||
|stashExcludes||X|X|
|stashIncludes||X|X|
## Return value
none
@ -39,13 +74,13 @@ none
## Example 1: Run a closure in a single container pod
```sh
# set environment variable
# set environment variable
export ON_K8S=true"
```
```groovy
dockerExecuteOnKubernetes(script: script, dockerImage: 'maven:3.5-jdk-7'){
sh "mvn clean install"
sh "mvn clean install"
}
```
@ -53,14 +88,14 @@ In the above example, a pod will be created with a docker container of image `ma
## Example 2: Run a closure in a multi-container pod
```sh
# set environment variable
# set environment variable
export ON_K8S=true"
```
```groovy
dockerExecuteOnKubernetes(script: script, containerMap: ['maven:3.5-jdk-8-alpine': 'maven', 's4sdk/docker-cf-cli': 'cfcli']){
container('maven'){
sh "mvn clean install"
sh "mvn clean install"
}
container('cfcli'){
sh "cf plugins"
@ -68,7 +103,25 @@ dockerExecuteOnKubernetes(script: script, containerMap: ['maven:3.5-jdk-8-alpine
}
```
In the above example, a pod will be created with multiple Docker containers that are passed as a `containerMap`. The containers can be chosen for executing by referring their labels as shown in the example.
In the above example, a pod will be created with multiple Docker containers that are passed as a `containerMap`. The containers can be chosen for executing by referring their labels as shown in the example.
## Example 3: Running a closure in a dedicated container of a multi-container pod
```sh
# set environment variable
export ON_K8S=true"
```
```groovy
dockerExecuteOnKubernetes(
script: script,
containerCommands: ['selenium/standalone-chrome': ''],
containerMap: ['maven:3.5-jdk-8-alpine': 'maven', 'selenium/standalone-chrome': 'selenium'],
containerName: 'maven',
containerPortMappings: ['selenium/standalone-chrome': [containerPort: 4444, hostPort: 4444]]
containerWorkspaces: ['selenium/standalone-chrome': '']
){
echo "Executing inside a Kubernetes Pod inside 'maven' container to run Selenium tests"
sh "mvn clean install"
}
```

View File

@ -0,0 +1,102 @@
# gaugeExecuteTests
## Description
In this step Gauge ([getgauge.io](http:getgauge.io)) acceptance tests are executed.
Using Gauge it will be possible to have a three-tier test layout:
* Acceptance Criteria
* Test implemenation layer
* Application driver layer
This layout is propagated by Jez Humble and Dave Farley in their book "Continuous Delivery" as a way to create maintainable acceptance test suites (see "Continuous Delivery", p. 190ff).
Using Gauge it is possible to write test specifications in [Markdown syntax](http://daringfireball.net/projects/markdown/syntax) and therefore allow e.g. product owners to write the relevant acceptance test specifications. At the same time it allows the developer to implement the steps described in the specification in her development environment.
You can use the sample projects of Gauge, for example: https://github.com/getgauge/gauge-mvn-archetypes
!!! note "Make sure to run against a Selenium Hub configuration"
In the test example of _gauge-archetype-selenium_ please make sure to allow it to run against a Selenium hub:
Please extend DriverFactory.java for example in following way:
``` java
String hubUrl = System.getenv("HUB_URL");
//when running on a Docker deamon (and not using Kubernetes plugin), Docker images will be linked
//in this case hubUrl will be http://selenium:4444/wd/hub due to the linking of the containers
hubUrl = (hubUrl == null) ? "http://localhost:4444/wd/hub" : hubUrl;
Capabilities chromeCapabilities = DesiredCapabilities.chrome();
System.out.println("Running on Selenium Hub: " + hubUrl);
return new RemoteWebDriver(new URL(hubUrl), chromeCapabilities);
```
## Prerequsites
none
## Example
Pipeline step:
```groovy
gaugeExecuteTests script: this, testServerUrl: 'http://test.url'
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|buildTool|no|`maven`||
|dockerEnvVars|no|`[HUB:TRUE, HUB_URL:http://localhost:4444/wd/hub]`||
|dockerImage|no|buildTool=`maven`: `maven:3.5-jdk-8`<br />buildTool=`npm`: `node:8-stretch`<br />||
|dockerName|no|buildTool=`maven`: `maven`<br />buildTool=`npm`: `npm`<br />||
|dockerWorkspace|no|buildTool=`maven`: ``<br />buildTool=`npm`: `/home/node`<br />||
|failOnError|no|`false`||
|gitBranch|no|||
|gitSshKeyCredentialsId|no|``||
|installCommand|no|`curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge`||
|languageRunner|no|buildTool=`maven`: `java`<br />buildTool=`npm`: `js`<br />||
|runCommand|no|buildTool=`maven`: `mvn test-compile gauge:execute`<br />buildTool=`npm`: `gauge run`<br />||
|stashContent|no|<ul><li>`buildDescriptor`</li><li>`tests`</li></ul>||
|testOptions|no|buildTool=`maven`: `-DspecsDir=specs`<br />buildTool=`npm`: `specs`<br />||
|testRepository|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.
* `buildTool` defines the build tool to be used for the test execution.
* `dockerEnvVars`, see step [dockerExecute](dockerExecute.md)
* `dockerImage`, see step [dockerExecute](dockerExecute.md)
* `dockerName`, see step [dockerExecute](dockerExecute.md)
* `dockerWorkspace`, see step [dockerExecute](dockerExecute.md)
* With `failOnError` you can define the behavior, in case tests fail. When this is set to `true` test results cannot be recorded using the `publishTestResults` step afterwards.
* `installCommand` defines the command for installing Gauge. In case the `dockerImage` already contains Gauge it can be set to empty: ``.
* `languageRunner` defines the Gauge language runner to be used.
* `runCommand` defines the command which is used for executing Gauge.
* If specific stashes should be considered for the tests, you can pass this via parameter `stashContent`
* `testOptions` allows to set specific options for the Gauge execution. Details can be found for example [in the Gauge Maven plugin documentation](https://github.com/getgauge/gauge-maven-plugin#executing-specs)
* In case the test implementation is stored in a different repository than the code itself, you can define the repository containing the tests using parameter `testRepository` and if required `gitBranch` (for a different branch than master) and `gitSshKeyCredentialsId` (for protected repositories). For protected repositories the `testRepository` needs to contain the ssh git url.
* `testServerUrl` is passed as environment variable `TARGET_SERVER_URL` to the test execution. Tests running against the system should read the host information from this environment variable in order to be infrastructure agnostic.
## 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||||
|buildTool||X|X|
|dockerEnvVars||X|X|
|dockerImage||X|X|
|dockerName||X|X|
|dockerWorkspace||X|X|
|failOnError||X|X|
|gitBranch||X|X|
|gitSshKeyCredentialsId||X|X|
|stashContent||X|X|
|testOptions||X|X|
|testRepository||X|X|
|testServerUrl||X|X|

View File

@ -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|

View File

@ -0,0 +1,96 @@
# mailSendNotification
## Description
Sends notifications to all potential culprits of a current or previous build failure plus to fixed list of recipients.
It will attach the current build log to the email.
Notifications are sent in following cases:
* current build failed or is unstable
* current build is successful and previous build failed or was unstable
## Prerequsites
none
## Example
Usage of pipeline step:
```groovy
mailSendNotification script: this
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|buildResult|no|||
|gitCommitId|no|`script.commonPipelineEnvironment.getGitCommitId()`||
|gitSshKeyCredentialsId|no|``||
|gitUrl|no|||
|notificationAttachment|no|`true`||
|notificationRecipients|no|||
|notifyCulprits|no|`true`||
|numLogLinesInBody|no|`100`||
|projectName|no|||
|wrapInNode|no|`false`||
### 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.
* `buildResult` may be used to overrule the build result coming from `currentBuild.result`. This is for example used in the step `pipelineRestartSteps`
* `gitCommitId` defines a dedicated git commitId for culprit retrieval.
* `gitUrl` and `gitCommitId` are used to retrieve culprit information.
* `gitSshKeyCredentialsId` only required if your git repository is protected. It defines the credentialsId for the git ssh credentials.
* `notificationAttachment` defines if the console log file should be attached to the notification mail.
* `notificationRecipients` defines the fixed list of recipient that always get the notification. In case you want to send the notification to the culprits only set it to an empty string `''`.
!!! note
Multiple recipients need to be separated with the `space` character.
In case you do not want to have any fixed recipients of the notifications leave the property empty.
* `notifyCulprits` defines if potential culprits should receive an email.
* `numLogLinesInBody` defines the number of log lines (=last lines of the log) which are included into the body of the notification email.
* `projectName` may be used to specify a different name in the email subject.
* `wrapInNode` needs to be set to `true` if step is used outside of a node context, e.g. post actions in a declarative pipeline script.
## 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||||
|buildResult||X|X|
|gitCommitId||X|X|
|gitSshKeyCredentialsId|X|X|X|
|gitUrl||X|X|
|notificationAttachment||X|X|
|notificationRecipients||X|X|
|notifyCulprits||X|X|
|numLogLinesInBody||X|X|
|projectName||X|X|
|wrapInNode||X|X|
## Return value
none
## Side effects
none
## Exceptions
none

View File

@ -0,0 +1,75 @@
# pipelineRestartSteps
## Description
Support of restarting failed stages or steps in a pipeline is limited in Jenkins.
This has been documented in the [Jenkins Jira issue JENKINS-33846](https://issues.jenkins-ci.org/browse/JENKINS-33846).
For declarative pipelines there is a solution available which partially addresses this topic:
https://jenkins.io/doc/book/pipeline/running-pipelines/#restart-from-a-stage.
Nonetheless, still features are missing, so it can't be used in all cases.
The more complex Piper pipelines which share a state via [`commonPipelineEnvironment`](commonPipelineEnvironment.md) will for example not work with the standard _restart-from-stage_.
The step `pipelineRestartSteps` aims to address this gap and allows individual parts of a pipeline (e.g. a failed deployment) to be restarted.
This is done in a way that the pipeline waits for user input to restart the pipeline in case of a failure. In case this user input is not provided the pipeline stops after a timeout which can be configured.
## Prerequisites
none
## Example
Usage of pipeline step:
```groovy
pipelineRestartSteps (script: this) {
node {
//your steps ...
}
}
```
!!! caution
Use `node` inside the step. If a `node` exists outside the step context, the `input` step which is triggered in the process will block a Jenkins executor.
In case you cannot use `node` inside this step, please choose the parameter `timeoutInSeconds` carefully!
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|sendMail|no|`true`||
|timeoutInSeconds|no|`900`||
### 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.
* If `sendMail: true` the step `mailSendNotification` will be triggered in case of an error
* `timeoutInSeconds` defines the time period where the job waits for input. Default is 15 minutes. Once this time is passed the job enters state FAILED.
## 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||||
|sendMail|X|X|X|
|timeoutInSeconds|X|X|X|
## Return value
none
## Side effects
none
## Exceptions
none

View File

@ -0,0 +1,143 @@
# seleniumExecuteTests
## Description
Enables UI test execution with Selenium in a sidecar container.
The step executes a closure (see example below) connecting to a sidecar container with a Selenium Server.
When executing in a
* local Docker environment, please make sure to set Selenium host to **`selenium`** in your tests.
* Kubernetes environment, plese make sure to set Seleniums host to **`localhost`** in your tests.
!!! note "Proxy Environments"
If work in an environment containing a proxy, please make sure that `localhost`/`selenium` is added to your proxy exclusion list, e.g. via environment variable `NO_PROXY` & `no_proxy`. You can pass those via parameters `dockerEnvVars` and `sidecarEnvVars` directly to the containers if required.
## Prerequisites
none
## Example
```groovy
seleniumExecuteTests (script: this) {
git url: 'https://github.wdf.sap.corp/xxxxx/WebDriverIOTest.git'
sh '''npm install
node index.js'''
}
```
### Example test using WebdriverIO
Example based on http://webdriver.io/guide/getstarted/modes.html and http://webdriver.io/guide.html
#### Configuration for Local Docker Environment
```
var webdriverio = require('webdriverio');
var options = {
host: 'selenium',
port: 4444,
desiredCapabilities: {
browserName: 'chrome'
}
};
```
#### Configuration for Kubernetes Environment
```
var webdriverio = require('webdriverio');
var options = {
host: 'localhost',
port: 4444,
desiredCapabilities: {
browserName: 'chrome'
}
};
```
#### Test Code (index.js)
```
// ToDo: add configuration from above
webdriverio
.remote(options)
.init()
.url('http://www.google.com')
.getTitle().then(function(title) {
console.log('Title was: ' + title);
})
.end()
.catch(function(err) {
console.log(err);
});
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|buildTool|no|`npm`|`maven`, `npm`|
|containerPortMappings|no|`[selenium/standalone-chrome:[[containerPort:4444, hostPort:4444]]]`||
|dockerEnvVars|no|||
|dockerImage|no|buildTool=`maven`: `maven:3.5-jdk-8`<br />buildTool=`npm`: `node:8-stretch`<br />||
|dockerName|no|buildTool=`maven`: `maven`<br />buildTool=`npm`: `npm`<br />||
|dockerWorkspace|no|buildTool=`maven`: ``<br />buildTool=`npm`: `/home/node`<br />||
|failOnError|no|`true`||
|gitBranch|no|||
|gitSshKeyCredentialsId|no|``||
|sidecarEnvVars|no|||
|sidecarImage|no|`selenium/standalone-chrome`||
|sidecarName|no|`selenium`||
|sidecarVolumeBind|no|`[/dev/shm:/dev/shm]`||
|stashContent|no|<ul><li>`tests`</li></ul>||
|testRepository|no|||
* `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.
* `buildTool` defines the build tool to be used for the test execution.
* `containerPortMappings`, see step [dockerExecute](dockerExecute.md)
* `dockerEnvVars`, see step [dockerExecute](dockerExecute.md)
* `dockerImage`, see step [dockerExecute](dockerExecute.md)
* `dockerName`, see step [dockerExecute](dockerExecute.md)
* `dockerWorkspace`, see step [dockerExecute](dockerExecute.md)
* `failOnError` specifies if the step should fail in case the execution of the body of this step fails.
* `sidecarEnvVars`, see step [dockerExecute](dockerExecute.md)
* `sidecarImage`, see step [dockerExecute](dockerExecute.md)
* `sidecarName`, see step [dockerExecute](dockerExecute.md)
* `sidecarVolumeBind`, see step [dockerExecute](dockerExecute.md)
* If specific stashes should be considered for the tests, you can pass this via parameter `stashContent`
* In case the test implementation is stored in a different repository than the code itself, you can define the repository containing the tests using parameter `testRepository` and if required `gitBranch` (for a different branch than master) and `gitSshKeyCredentialsId` (for protected repositories). For protected repositories the testRepository needs to contain the ssh git url.
## 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||||
|buildTool||X|X|
|containerPortMappings|X|X|X|
|dockerEnvVars|X|X|X|
|dockerImage|X|X|X|
|dockerName|X|X|X|
|dockerWorkspace|X|X|X|
|failOnError|X|X|X|
|gitBranch|X|X|X|
|gitSshKeyCredentialsId|X|X|X|
|sidecarEnvVars|X|X|X|
|sidecarImage|X|X|X|
|sidecarName|X|X|X|
|sidecarVolumeBind|X|X|X|
|stashContent|X|X|X|
|testRepository|X|X|X|
## Return value
none
## Side effects
none
## Exceptions
none

View File

@ -6,20 +6,27 @@ nav:
- artifactSetVersion: steps/artifactSetVersion.md
- batsExecuteTests: steps/batsExecuteTests.md
- checkChangeInDevelopment: steps/checkChangeInDevelopment.md
- checksPublishResults: steps/checksPublishResults.md
- cloudFoundryDeploy: steps/cloudFoundryDeploy.md
- commonPipelineEnvironment: steps/commonPipelineEnvironment.md
- dockerExecute: steps/dockerExecute.md
- dockerExecuteOnKubernetes: steps/dockerExecuteOnKubernetes.md
- durationMeasure: steps/durationMeasure.md
- gaugeExecuteTests: steps/gaugeExecuteTests.md
- handlePipelineStepErrors: steps/handlePipelineStepErrors.md
- healthExecuteCheck: steps/healthExecuteCheck.md
- influxWriteData: steps/influxWriteData.md
- mailSendNotification: steps/mailSendNotification.md
- mavenExecute: steps/mavenExecute.md
- mtaBuild: steps/mtaBuild.md
- neoDeploy: steps/neoDeploy.md
- pipelineExecute: steps/pipelineExecute.md
- pipelineRestartSteps: steps/pipelineRestartSteps.md
- pipelineStashFiles: steps/pipelineStashFiles.md
- prepareDefaultValues: steps/prepareDefaultValues.md
- seleniumExecuteTests: steps/seleniumExecuteTests.md
- setupCommonPipelineEnvironment: steps/setupCommonPipelineEnvironment.md
- testsPublishResults: steps/testsPublishResults.md
- toolValidate: steps/toolValidate.md
- transportRequestCreate: steps/transportRequestCreate.md
- transportRequestRelease: steps/transportRequestRelease.md

View File

@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.sap.cp.jenkins</groupId>
<artifactId>jenkins-library</artifactId>
<version>0.6</version>
<version>0.8</version>
<name>SAP CP Piper Library</name>
<description>Shared library containing steps and utilities to set up continuous deployment processes for SAP technologies.</description>

View File

@ -0,0 +1,6 @@
<a href="${env.BUILD_URL}">${env.BUILD_URL}</a>
<br>
To have a detailed look at the different pipeline stages: <a href="${env.RUN_DISPLAY_URL}">${env.RUN_DISPLAY_URL}</a>
<br>
<h3>Last lines of output</h3>
<pre>${log}</pre>

View File

@ -0,0 +1,3 @@
<a href="${env.BUILD_URL}">${env.BUILD_URL}</a>
<br>
To have a detailed look at the different pipeline stages: <a href="${env.RUN_DISPLAY_URL}">${env.RUN_DISPLAY_URL}</a>

View File

@ -140,8 +140,39 @@ steps:
workspace: '**/*.*'
stashExcludes:
workspace: 'nohup.out'
gaugeExecuteTests:
buildTool: 'maven'
dockerEnvVars:
HUB: 'TRUE'
HUB_URL: 'http://localhost:4444/wd/hub'
failOnError: false
installCommand: 'curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge'
stashContent:
- 'buildDescriptor'
- 'tests'
maven:
dockerImage: 'maven:3.5-jdk-8'
dockerName: 'maven'
dockerWorkspace: ''
languageRunner: 'java'
runCommand: 'mvn test-compile gauge:execute'
testOptions: '-DspecsDir=specs'
npm:
dockerImage: 'node:8-stretch'
dockerName: 'npm'
dockerWorkspace: '/home/node'
languageRunner: 'js'
runCommand: 'gauge run'
testOptions: 'specs'
healthExecuteCheck:
healthEndpoint: ''
influxWriteData:
influxServer: 'jenkins'
mailSendNotification:
notificationAttachment: true
notifyCulprits: true
numLogLinesInBody: 100
wrapInNode: false
mavenExecute:
dockerImage: 'maven:3.5-jdk-7'
logSuccessfulMavenTransfers: false
@ -163,6 +194,9 @@ steps:
newmanRunCommand: "run ${config.newmanCollection} --environment '${config.newmanEnvironment}' --globals '${config.newmanGlobals}' --reporters junit,html --reporter-junit-export target/newman/TEST-${collectionDisplayName}.xml --reporter-html-export target/newman/TEST-${collectionDisplayName}.html"
stashContent:
- 'tests'
pipelineRestartSteps:
sendMail: true
timeoutInSeconds: 900
pipelineStashFilesAfterBuild:
runOpaTests: false
stashIncludes:
@ -193,6 +227,28 @@ steps:
pipelineConfigAndTests: ''
securityDescriptor: ''
tests: ''
seleniumExecuteTests:
buildTool: 'npm'
containerPortMappings:
'selenium/standalone-chrome':
- containerPort: 4444
hostPort: 4444
dockerLinkAlias: 'selenium'
failOnError: true
sidecarImage: 'selenium/standalone-chrome'
sidecarName: 'selenium'
sidecarVolumeBind:
'/dev/shm': '/dev/shm'
stashContent:
- 'tests'
maven:
dockerImage: 'maven:3.5-jdk-8'
dockerName: 'maven'
dockerWorkspace: ''
npm:
dockerImage: 'node:8-stretch'
dockerName: 'npm'
dockerWorkspace: '/home/node'
snykExecute:
buildDescriptorFile: './package.json'
dockerImage: 'node:8-stretch'

View File

@ -0,0 +1,26 @@
package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
class CfManifestUtils {
@NonCPS
static Map transform(Map manifest) {
if (manifest.applications[0].buildpacks) {
manifest['applications'].each { Map application ->
def buildpacks = application['buildpacks']
if (buildpacks) {
if (buildpacks instanceof List) {
if (buildpacks.size > 1) {
throw new RuntimeException('More than one Cloud Foundry Buildpack is not supported. Please check your manifest.yaml file.')
}
application['buildpack'] = buildpacks[0]
application.remove('buildpacks')
} else {
throw new RuntimeException('"buildpacks" in manifest.yaml is not a list. Please check your manifest.yaml file.')
}
}
}
}
return manifest
}
}

View File

@ -3,50 +3,52 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
class ConfigurationHelper implements Serializable {
static ConfigurationHelper loadStepDefaults(Script step){
return new ConfigurationHelper(step)
.initDefaults(step)
.loadDefaults()
static ConfigurationHelper newInstance(Script step, Map config = [:]) {
new ConfigurationHelper(step, config)
}
private Map config = [:]
private String name
ConfigurationHelper(Script step){
name = step.STEP_NAME
if(!name) throw new IllegalArgumentException('Step has no public name property!')
}
private final ConfigurationHelper initDefaults(Script step){
step.prepareDefaultValues()
return this
}
private final ConfigurationHelper loadDefaults(){
config = ConfigurationLoader.defaultGeneralConfiguration()
ConfigurationHelper loadStepDefaults() {
this.step.prepareDefaultValues()
this.config = ConfigurationLoader.defaultGeneralConfiguration()
mixin(ConfigurationLoader.defaultStepConfiguration(null, name))
}
private Map config
private Script step
private String name
private Map validationResults = null
private ConfigurationHelper(Script step, Map config){
this.config = config ?: [:]
this.step = step
this.name = step.STEP_NAME
if(!this.name) throw new IllegalArgumentException('Step has no public name property!')
}
ConfigurationHelper collectValidationFailures() {
validationResults = validationResults ?: [:]
return this
}
ConfigurationHelper mixinGeneralConfig(commonPipelineEnvironment, Set filter = null, Script step = null, Map compatibleParameters = [:]){
ConfigurationHelper mixinGeneralConfig(commonPipelineEnvironment, Set filter = null, Map compatibleParameters = [:]){
Map stepConfiguration = ConfigurationLoader.generalConfiguration([commonPipelineEnvironment: commonPipelineEnvironment])
return mixin(stepConfiguration, filter, step, compatibleParameters)
return mixin(stepConfiguration, filter, compatibleParameters)
}
ConfigurationHelper mixinStageConfig(commonPipelineEnvironment, stageName, Set filter = null, Script step = null, Map compatibleParameters = [:]){
ConfigurationHelper mixinStageConfig(commonPipelineEnvironment, stageName, Set filter = null, Map compatibleParameters = [:]){
Map stageConfiguration = ConfigurationLoader.stageConfiguration([commonPipelineEnvironment: commonPipelineEnvironment], stageName)
return mixin(stageConfiguration, filter, step, compatibleParameters)
return mixin(stageConfiguration, filter, compatibleParameters)
}
ConfigurationHelper mixinStepConfig(commonPipelineEnvironment, Set filter = null, Script step = null, Map compatibleParameters = [:]){
if(!name) throw new IllegalArgumentException('Step has no public name property!')
ConfigurationHelper mixinStepConfig(commonPipelineEnvironment, Set filter = null, Map compatibleParameters = [:]){
Map stepConfiguration = ConfigurationLoader.stepConfiguration([commonPipelineEnvironment: commonPipelineEnvironment], name)
return mixin(stepConfiguration, filter, step, compatibleParameters)
return mixin(stepConfiguration, filter, compatibleParameters)
}
ConfigurationHelper mixin(Map parameters, Set filter = null, Script step = null, Map compatibleParameters = [:]){
final ConfigurationHelper mixin(Map parameters, Set filter = null, Map compatibleParameters = [:]){
if (parameters.size() > 0 && compatibleParameters.size() > 0) {
parameters = ConfigurationMerger.merge(handleCompatibility(step, compatibleParameters, parameters), null, parameters)
parameters = ConfigurationMerger.merge(handleCompatibility(compatibleParameters, parameters), null, parameters)
}
if (filter) {
filter.add('collectTelemetryData')
@ -55,12 +57,12 @@ class ConfigurationHelper implements Serializable {
return this
}
private Map handleCompatibility(Script step, Map compatibleParameters, String paramStructure = '', Map configMap ) {
private Map handleCompatibility(Map compatibleParameters, String paramStructure = '', Map configMap ) {
Map newConfig = [:]
compatibleParameters.each {entry ->
if (entry.getValue() instanceof Map) {
paramStructure = (paramStructure ? paramStructure + '.' : '') + entry.getKey()
newConfig[entry.getKey()] = handleCompatibility(step, entry.getValue(), paramStructure, configMap)
newConfig[entry.getKey()] = handleCompatibility(entry.getValue(), paramStructure, configMap)
} else {
def configSubMap = configMap
for(String key in paramStructure.tokenize('.')){
@ -69,8 +71,8 @@ class ConfigurationHelper implements Serializable {
if (configSubMap == null || (configSubMap != null && configSubMap[entry.getKey()] == null)) {
newConfig[entry.getKey()] = configMap[entry.getValue()]
def paramName = (paramStructure ? paramStructure + '.' : '') + entry.getKey()
if (step && configMap[entry.getValue()] != null) {
step.echo ("[INFO] The parameter '${entry.getValue()}' is COMPATIBLE to the parameter '${paramName}'")
if (configMap[entry.getValue()] != null) {
this.step.echo ("[INFO] The parameter '${entry.getValue()}' is COMPATIBLE to the parameter '${paramName}'")
}
}
}
@ -101,14 +103,11 @@ class ConfigurationHelper implements Serializable {
@NonCPS // required because we have a closure in the
// method body that cannot be CPS transformed
Map use(){
handleValidationFailures()
MapUtils.traverse(config, { v -> (v instanceof GString) ? v.toString() : v })
return config
}
ConfigurationHelper(Map config = [:]){
this.config = config
}
/* private */ def getConfigPropertyNested(key) {
return getConfigPropertyNested(config, key)
}
@ -140,7 +139,12 @@ class ConfigurationHelper implements Serializable {
if (paramValue == null) {
if(! errorMessage) errorMessage = "ERROR - NO VALUE AVAILABLE FOR ${key}"
throw new IllegalArgumentException(errorMessage)
def iae = new IllegalArgumentException(errorMessage)
if(validationResults == null) {
throw iae
}
validationResults.put(key, iae)
}
}
@ -153,4 +157,16 @@ class ConfigurationHelper implements Serializable {
}
return this
}
@NonCPS
private handleValidationFailures() {
if(! validationResults) return
if(validationResults.size() == 1) throw validationResults.values().first()
String msg = 'ERROR - NO VALUE AVAILABLE FOR: ' +
(validationResults.keySet().stream().collect() as Iterable).join(', ')
IllegalArgumentException iae = new IllegalArgumentException(msg)
validationResults.each { e -> iae.addSuppressed(e.value) }
throw iae
}
}

View File

@ -1,7 +1,6 @@
package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
import com.sap.piper.MapUtils
class ConfigurationMerger {
@NonCPS

View File

@ -37,3 +37,18 @@ String[] extractLogLines(String filter = '',
?.findAll { line -> line ==~ /${filter}/ }
}
static String handleTestRepository(Script steps, Map config){
def stashName = "testContent-${UUID.randomUUID()}".toString()
def options = [url: config.testRepository]
if (config.gitSshKeyCredentialsId)
options.put('credentialsId', config.gitSshKeyCredentialsId)
if (config.gitBranch)
options.put('branch', config.gitBranch)
// checkout test repository
steps.git options
// stash test content
steps.stash stashName
// return stash name
return stashName
}

View File

@ -2,8 +2,19 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
@NonCPS
static def isPluginActive(pluginId) {
return Jenkins.instance.pluginManager.plugins.find { p -> p.isActive() && p.getShortName() == pluginId }
}
def nodeAvailable() {
try {
sh "echo 'Node is available!'"
} catch (MissingContextVariableException e) {
echo "No node context available."
return false
}
return true
}

View File

@ -1,7 +1,5 @@
package com.sap.piper.cm
import java.util.Map
import com.sap.piper.GitUtils
import hudson.AbortException
@ -12,31 +10,6 @@ public class ChangeManagement implements Serializable {
private script
private GitUtils gitUtils
public static class StepHelpers {
static BackendType getBackendTypeAndLogInfoIfCMIntegrationDisabled(def step, Map configuration) {
BackendType backendType
try {
backendType = configuration.changeManagement.type as BackendType
} catch(IllegalArgumentException e) {
step.error "Invalid backend type: '${configuration.changeManagement.type}'. " +
"Valid values: [${BackendType.values().join(', ')}]. " +
"Configuration: 'changeManagement/type'."
}
if (backendType == BackendType.NONE) {
step.echo "[INFO] Change management integration intentionally switched off. " +
"In order to enable it provide 'changeManagement/type with one of " +
"[${BackendType.values().minus(BackendType.NONE).join(', ')}] and maintain " +
"maintain other required properties like 'endpoint', 'credentialsId'."
}
return backendType
}
}
public ChangeManagement(def script, GitUtils gitUtils = null) {
this.script = script
this.gitUtils = gitUtils ?: new GitUtils()

View File

@ -21,7 +21,9 @@ import util.Rules
import static org.hamcrest.Matchers.hasItem
import static org.hamcrest.Matchers.hasItems
import static org.hamcrest.Matchers.not
import static org.hamcrest.Matchers.notNullValue
import static org.hamcrest.Matchers.stringContainsInOrder
import static org.hamcrest.Matchers.containsString
import static org.junit.Assert.assertThat
@ -89,10 +91,13 @@ class ArtifactSetVersionTest extends BasePiperTest {
assertEquals('testCommitId', jer.env.getGitCommitId())
assertThat(jscr.shell, hasItem("mvn --file 'pom.xml' --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn versions:set -DnewVersion=1.2.3-20180101010203_testCommitId -DgenerateBackupPoms=false"))
assertThat(jscr.shell, hasItem('git add .'))
assertThat(jscr.shell, hasItems(containsString("git commit -m 'update version 1.2.3-20180101010203_testCommitId'"),
containsString('git tag build_1.2.3-20180101010203_testCommitId'),
containsString('git push myGitSshUrl build_1.2.3-20180101010203_testCommitId')))
assertThat(jscr.shell.join(), stringContainsInOrder([
"git add .",
"git commit -m 'update version 1.2.3-20180101010203_testCommitId'",
'git tag build_1.2.3-20180101010203_testCommitId',
'git push myGitSshUrl build_1.2.3-20180101010203_testCommitId',
]
))
}
@Test
@ -101,6 +106,7 @@ class ArtifactSetVersionTest extends BasePiperTest {
assertEquals('1.2.3-20180101010203_testCommitId', jer.env.getArtifactVersion())
assertThat(jscr.shell, hasItem("mvn --file 'pom.xml' --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn versions:set -DnewVersion=1.2.3-20180101010203_testCommitId -DgenerateBackupPoms=false"))
assertThat(jscr.shell, not(hasItem(containsString('commit'))))
}
@Test

View File

@ -7,6 +7,7 @@ import util.*
import static org.hamcrest.Matchers.hasItem
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.startsWith
import static org.junit.Assert.assertThat
class BatsExecuteTestsTest extends BasePiperTest {
@ -104,7 +105,7 @@ class BatsExecuteTestsTest extends BasePiperTest {
gitRepository = m
})
helper.registerAllowedMethod('stash', [String.class], {s ->
assertThat(s, is('batsTests'))
assertThat(s, startsWith('testContent-'))
})
jsr.step.batsExecuteTests(
@ -115,7 +116,7 @@ class BatsExecuteTestsTest extends BasePiperTest {
assertThat(gitRepository.size(), is(1))
assertThat(gitRepository.url, is('testRepo'))
assertThat(jder.dockerParams.stashContent, hasItem('batsTests'))
assertThat(jder.dockerParams.stashContent, hasItem(startsWith('testContent-')))
}
@Test
@ -125,7 +126,7 @@ class BatsExecuteTestsTest extends BasePiperTest {
gitRepository = m
})
helper.registerAllowedMethod('stash', [String.class], {s ->
assertThat(s, is('batsTests'))
assertThat(s, startsWith('testContent-'))
})
jsr.step.batsExecuteTests(

View File

@ -43,19 +43,21 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
public void changeIsInStatusDevelopmentTest() {
ChangeManagement cm = getChangeManagementUtils(true)
boolean inDevelopment = jsr.step.checkChangeInDevelopment(
cmUtils: cm,
changeManagement: [
type: 'SOLMAN',
endpoint: 'https://example.org/cm'])
jsr.step.checkChangeInDevelopment(
cmUtils: cm,
changeManagement: [
type: 'SOLMAN',
endpoint: 'https://example.org/cm'],
failIfStatusIsNotInDevelopment: true)
assert inDevelopment
assert cmUtilReceivedParams == [
changeId: '001',
endpoint: 'https://example.org/cm',
credentialsId: 'CM',
cmclientOpts: ''
]
// no exception in thrown, so the change is in status 'in development'.
}
@Test

View File

@ -105,6 +105,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeWithAppName() {
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
juStabUtils: utils,
@ -125,6 +130,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeWithAppNameCustomApi() {
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
juStabUtils: utils,
@ -142,6 +152,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeWithAppNameCompatible() {
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
juStabUtils: utils,
@ -165,7 +180,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeAppNameFromManifest() {
helper.registerAllowedMethod('fileExists', [String.class], { s -> return true })
jryr.registerYaml('test.yml', "[applications: [[name: 'manifestAppName']]]")
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
@ -185,6 +204,10 @@ class CloudFoundryDeployTest extends BasePiperTest {
void testCfNativeWithoutAppName() {
helper.registerAllowedMethod('fileExists', [String.class], { s -> return true })
jryr.registerYaml('test.yml', "applications: [[]]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
thrown.expect(hudson.AbortException)
thrown.expectMessage('[cloudFoundryDeploy] ERROR: No appName available in manifest test.yml.')

View File

@ -0,0 +1,107 @@
import static java.util.stream.Collectors.toList
import static org.hamcrest.Matchers.empty
import static org.hamcrest.Matchers.is
import static org.junit.Assert.assertThat
import static org.junit.Assert.fail
import java.lang.reflect.Field
import java.io.File;
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import groovy.io.FileType
import util.BasePiperTest
import util.Rules
/*
* Intended for collecting generic checks applied to all steps.
*/
public class CommonStepsTest extends BasePiperTest{
@Rule
public RuleChain ruleChain = Rules.getCommonRules(this)
@Test
public void stepsWithWrongFieldNameTest() {
def whitelist = ['commonPipelineEnvironment']
def stepsWithWrongStepName = []
for(def step in getSteps()) {
if(whitelist.contains(step)) continue
def script = loadScript("${step}.groovy")
def fields = script.getClass().getDeclaredFields() as Set
Field stepNameField = fields.find { it.getName() == 'STEP_NAME'}
if(! stepNameField) {
stepsWithWrongStepName.add(step)
continue
}
boolean notAccessible = false;
def fieldName
if(!stepNameField.isAccessible()) {
stepNameField.setAccessible(true)
notAccessible = true
}
try {
fieldName = stepNameField.get(script)
} finally {
if(notAccessible) stepNameField.setAccessible(false)
}
if(fieldName != step) {
stepsWithWrongStepName.add(step)
}
}
assertThat("Steps with wrong step name or without STEP_NAME field.: ${stepsWithWrongStepName}",
stepsWithWrongStepName, is(empty()))
}
/*
* With that test we ensure that all return types of the call methods of all the steps
* are void. Return types other than void are not possible when running inside declarative
* pipelines. Parameters shared between several steps needs to be shared via the commonPipelineEnvironment.
*/
@Test
public void returnTypeForCallMethodsIsVoidTest() {
def stepsWithCallMethodsOtherThanVoid = []
def whitelist = [
'transportRequestCreate',
'durationMeasure',
]
for(def step in getSteps()) {
def methods = loadScript("${step}.groovy").getClass().getDeclaredMethods() as List
Collection callMethodsWithReturnTypeOtherThanVoid =
methods.stream()
.filter { ! whitelist.contains(step) }
.filter { it.getName() == 'call' &&
it.getReturnType() != Void.TYPE }
.collect(toList())
if(!callMethodsWithReturnTypeOtherThanVoid.isEmpty()) stepsWithCallMethodsOtherThanVoid << step
}
assertThat("Steps with call methods with return types other than void: ${stepsWithCallMethodsOtherThanVoid}",
stepsWithCallMethodsOtherThanVoid, is(empty()))
}
private static getSteps() {
List steps = []
new File('vars').traverse(type: FileType.FILES, maxDepth: 0)
{ if(it.getName().endsWith('.groovy')) steps << (it =~ /vars\/(.*)\.groovy/)[0][1] }
return steps
}
}

View File

@ -16,6 +16,8 @@ import util.JenkinsStepRule
import util.PluginMock
import util.Rules
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
import static org.junit.Assert.assertTrue
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertFalse
@ -48,6 +50,8 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
def imageList = []
def containerName = ''
def envList = []
def portList = []
def containerCommands = []
@Before
@ -55,6 +59,8 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
containersList = []
imageList = []
envList = []
portList = []
containerCommands = []
bodyExecuted = false
JenkinsUtils.metaClass.static.isPluginActive = { def s -> new PluginMock(s).isActive() }
helper.registerAllowedMethod('sh', [Map.class], {return whichDockerReturnValue})
@ -65,8 +71,12 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
podLabel = options.label
options.containers.each { option ->
containersList.add(option.name)
imageList.add(option.image)
imageList.add(option.image.toString())
envList.add(option.envVars)
portList.add(option.ports)
if (option.command) {
containerCommands.add(option.command)
}
}
body()
})
@ -77,25 +87,26 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
@Test
void testRunOnPodNoContainerMapOnlyDockerImage() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerOptions: '-it',
dockerVolumeBind: ['my_vol': '/my_vol'],
dockerEnvVars: ['http_proxy': 'http://proxy:8000'], dockerWorkspace: '/home/piper'){
bodyExecuted = true
dockerEnvVars: ['http_proxy': 'http://proxy:8000'], dockerWorkspace: '/home/piper'
){
bodyExecuted = true
}
assertTrue(containersList.contains('container-exec'))
assertTrue(imageList.contains('maven:3.5-jdk-8-alpine'))
assertTrue(envList.toString().contains('http_proxy'))
assertTrue(envList.toString().contains('http://proxy:8000'))
assertTrue(envList.toString().contains('/home/piper'))
assertTrue(bodyExecuted)
assertThat(containersList, hasItem('container-exec'))
assertThat(imageList, hasItem('maven:3.5-jdk-8-alpine'))
assertThat(envList.toString(), containsString('http_proxy'))
assertThat(envList.toString(), containsString('http://proxy:8000'))
assertThat(envList.toString(), containsString('/home/piper'))
assertThat(bodyExecuted, is(true))
assertThat(containerCommands.size(), is(1))
}
@Test
void testDockerExecuteOnKubernetesWithCustomContainerMap() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute']) {
container(name: 'mavenexecute') {
bodyExecuted = true
@ -105,12 +116,13 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
assertTrue(containersList.contains('mavenexecute'))
assertTrue(imageList.contains('maven:3.5-jdk-8-alpine'))
assertTrue(bodyExecuted)
assertThat(containerCommands.size(), is(1))
}
@Test
void testDockerExecuteOnKubernetesWithCustomJnlpWithContainerMap() throws Exception {
nullScript.commonPipelineEnvironment.configuration = ['general': ['jenkinsKubernetes': ['jnlpAgent': 'myJnalpAgent']]]
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute']) {
container(name: 'mavenexecute') {
bodyExecuted = true
@ -127,7 +139,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
@Test
void testDockerExecuteOnKubernetesWithCustomJnlpWithDockerImage() throws Exception {
nullScript.commonPipelineEnvironment.configuration = ['general': ['jenkinsKubernetes': ['jnlpAgent': 'myJnalpAgent']]]
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine') {
bodyExecuted = true
}
@ -141,7 +153,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
@Test
void testDockerExecuteOnKubernetesWithCustomWorkspace() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute'],
dockerWorkspace: '/home/piper') {
container(name: 'mavenexecute') {
@ -154,7 +166,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
@Test
void testDockerExecuteOnKubernetesWithCustomEnv() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute'],
dockerEnvVars: ['customEnvKey': 'customEnvValue']) {
container(name: 'mavenexecute') {
@ -167,7 +179,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
@Test
void testDockerExecuteOnKubernetesUpperCaseContainerName() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
containerMap: ['maven:3.5-jdk-8-alpine': 'MAVENEXECUTE'],
dockerEnvVars: ['customEnvKey': 'customEnvValue']) {
container(name: 'mavenexecute') {
@ -183,7 +195,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
@Test
void testDockerExecuteOnKubernetesEmptyContainerMapNoDockerImage() throws Exception {
exception.expect(IllegalArgumentException.class);
jsr.step.call(script: nullScript,
jsr.step.dockerExecuteOnKubernetes(script: nullScript,
containerMap: [:],
dockerEnvVars: ['customEnvKey': 'customEnvValue']) {
container(name: 'jnlp') {
@ -193,6 +205,55 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest {
assertFalse(bodyExecuted)
}
@Test
void testSidecarDefault() {
List portMapping = []
helper.registerAllowedMethod('portMapping', [Map.class], {m ->
portMapping.add(m)
return m
})
jsr.step.dockerExecuteOnKubernetes(
script: nullScript,
containerCommands: ['selenium/standalone-chrome': ''],
containerEnvVars: [
'selenium/standalone-chrome': ['customEnvKey': 'customEnvValue']
],
containerMap: [
'maven:3.5-jdk-8-alpine': 'mavenexecute',
'selenium/standalone-chrome': 'selenium'
],
containerName: 'mavenexecute',
containerPortMappings: [
'selenium/standalone-chrome': [[containerPort: 4444, hostPort: 4444]]
],
containerWorkspaces: [
'selenium/standalone-chrome': ''
],
dockerWorkspace: '/home/piper'
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(containerName, is('mavenexecute'))
assertThat(containersList, allOf(
hasItem('jnlp'),
hasItem('mavenexecute'),
hasItem('selenium'),
))
assertThat(imageList, allOf(
hasItem('s4sdk/jenkins-agent-k8s:latest'),
hasItem('maven:3.5-jdk-8-alpine'),
hasItem('selenium/standalone-chrome'),
))
assertThat(portList, hasItem(hasItem([name: 'selenium0', containerPort: 4444, hostPort: 4444])))
assertThat(portMapping, hasItem([name: 'selenium0', containerPort: 4444, hostPort: 4444]))
assertThat(containerCommands.size(), is(1))
assertThat(envList, hasItem(hasItem(allOf(hasEntry('key', 'customEnvKey'), hasEntry ('value','customEnvValue')))))
}
private container(options, body) {
containerName = options.name
body()

View File

@ -13,6 +13,8 @@ import util.JenkinsStepRule
import util.PluginMock
import util.Rules
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
import static org.junit.Assert.assertTrue
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertFalse
@ -50,7 +52,7 @@ class DockerExecuteTest extends BasePiperTest {
})
binding.setVariable('env', [POD_NAME: 'testpod', ON_K8S: 'true'])
ContainerMap.instance.setMap(['testpod': ['maven:3.5-jdk-8-alpine': 'mavenexec']])
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerEnvVars: ['http_proxy': 'http://proxy:8000']) {
bodyExecuted = true
@ -65,7 +67,7 @@ class DockerExecuteTest extends BasePiperTest {
helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> body() })
binding.setVariable('env', [ON_K8S: 'true'])
ContainerMap.instance.setMap(['testpod': ['maven:3.5-jdk-8-alpine': 'mavenexec']])
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerEnvVars: ['http_proxy': 'http://proxy:8000']) {
bodyExecuted = true
@ -79,7 +81,7 @@ class DockerExecuteTest extends BasePiperTest {
helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> body() })
binding.setVariable('env', [POD_NAME: 'testpod', ON_K8S: 'true'])
ContainerMap.instance.setMap([:])
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerEnvVars: ['http_proxy': 'http://proxy:8000']) {
bodyExecuted = true
@ -93,7 +95,7 @@ class DockerExecuteTest extends BasePiperTest {
helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> body() })
binding.setVariable('env', [POD_NAME: 'testpod', ON_K8S: 'true'])
ContainerMap.instance.setMap(['testpod':[:]])
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerEnvVars: ['http_proxy': 'http://proxy:8000']) {
bodyExecuted = true
@ -104,7 +106,7 @@ class DockerExecuteTest extends BasePiperTest {
@Test
void testExecuteInsideDockerContainer() throws Exception {
jsr.step.call(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine') {
jsr.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine') {
bodyExecuted = true
}
assertEquals('maven:3.5-jdk-8-alpine', docker.getImageName())
@ -115,7 +117,7 @@ class DockerExecuteTest extends BasePiperTest {
@Test
void testExecuteInsideDockerNoScript() throws Exception {
jsr.step.call(dockerImage: 'maven:3.5-jdk-8-alpine') {
jsr.step.dockerExecute(dockerImage: 'maven:3.5-jdk-8-alpine') {
bodyExecuted = true
}
assertEquals('maven:3.5-jdk-8-alpine', docker.getImageName())
@ -126,7 +128,7 @@ class DockerExecuteTest extends BasePiperTest {
@Test
void testExecuteInsideDockerContainerWithParameters() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerOptions: '-it',
dockerVolumeBind: ['my_vol': '/my_vol'],
@ -142,7 +144,7 @@ class DockerExecuteTest extends BasePiperTest {
@Test
void testExecuteInsideDockerContainerWithDockerOptionsList() throws Exception {
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerOptions: ['-it', '--network=my-network'],
dockerEnvVars: ['http_proxy': 'http://proxy:8000']) {
@ -156,7 +158,7 @@ class DockerExecuteTest extends BasePiperTest {
@Test
void testDockerNotInstalledResultsInLocalExecution() throws Exception {
whichDockerReturnValue = 1
jsr.step.call(script: nullScript,
jsr.step.dockerExecute(script: nullScript,
dockerOptions: '-it') {
bodyExecuted = true
}
@ -166,10 +168,70 @@ class DockerExecuteTest extends BasePiperTest {
assertFalse(docker.isImagePulled())
}
@Test
void testSidecarDefault(){
jsr.step.dockerExecute(
script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
sidecarEnvVars: ['testEnv':'testVal'],
sidecarImage: 'selenium/standalone-chrome',
sidecarVolumeBind: ['/dev/shm':'/dev/shm'],
sidecarName: 'testAlias',
sidecarPorts: ['4444':'4444', '1111':'1111']
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(docker.imagePullCount, is(2))
assertThat(docker.sidecarParameters, allOf(
containsString('--env testEnv=testVal'),
containsString('--volume /dev/shm:/dev/shm')
))
assertThat(docker.parameters, containsString('--link uniqueId:testAlias'))
}
@Test
void testSidecarKubernetes(){
boolean dockerExecuteOnKubernetesCalled = false
binding.setVariable('env', [ON_K8S: 'true'])
helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { params, body ->
dockerExecuteOnKubernetesCalled = true
assertThat(params.containerCommands['selenium/standalone-chrome'], is(''))
assertThat(params.containerEnvVars, allOf(hasEntry('selenium/standalone-chrome', ['testEnv': 'testVal']),hasEntry('maven:3.5-jdk-8-alpine', null)))
assertThat(params.containerMap, allOf(hasEntry('maven:3.5-jdk-8-alpine', 'maven'), hasEntry('selenium/standalone-chrome', 'selenium')))
assertThat(params.containerName, is('maven'))
assertThat(params.containerPortMappings['selenium/standalone-chrome'], hasItem(allOf(hasEntry('containerPort', 4444), hasEntry('hostPort', 4444))))
assertThat(params.containerWorkspaces['maven:3.5-jdk-8-alpine'], is('/home/piper'))
assertThat(params.containerWorkspaces['selenium/standalone-chrome'], is(''))
body()
})
jsr.step.dockerExecute(
script: nullScript,
containerPortMappings: [
'selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]
],
dockerImage: 'maven:3.5-jdk-8-alpine',
dockerName: 'maven',
dockerWorkspace: '/home/piper',
sidecarEnvVars: ['testEnv':'testVal'],
sidecarImage: 'selenium/standalone-chrome',
sidecarName: 'selenium',
sidecarVolumeBind: ['/dev/shm':'/dev/shm'],
dockerLinkAlias: 'testAlias',
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(dockerExecuteOnKubernetesCalled, is(true))
}
private class DockerMock {
private String imageName
private boolean imagePulled = false
private int imagePullCount = 0
private String parameters
private String sidecarParameters
DockerMock image(String imageName) {
this.imageName = imageName
@ -177,6 +239,7 @@ class DockerExecuteTest extends BasePiperTest {
}
void pull() {
imagePullCount++
imagePulled = true
}
@ -185,6 +248,11 @@ class DockerExecuteTest extends BasePiperTest {
body()
}
void withRun(String parameters, body) {
this.sidecarParameters = parameters
body([id: 'uniqueId'])
}
String getImageName() {
return imageName
}

View File

@ -0,0 +1,137 @@
#!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.*
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class GaugeExecuteTestsTest extends BasePiperTest {
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
private JenkinsEnvironmentRule jer = new JenkinsEnvironmentRule(this)
private ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jscr)
.around(jlr)
.around(jer)
.around(jsr)
.around(thrown)
def gitParams = [:]
def seleniumParams = [:]
@Before
void init() throws Exception {
helper.registerAllowedMethod("git", [Map.class], { map -> gitParams = map })
helper.registerAllowedMethod("unstash", [String.class], { s -> return [s]})
helper.registerAllowedMethod('seleniumExecuteTests', [Map.class, Closure.class], {map, body ->
seleniumParams = map
return body()
})
}
@Test
void testExecuteGaugeDefaultSuccess() throws Exception {
jsr.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
testServerUrl: 'http://test.url'
)
assertThat(jscr.shell, hasItem(stringContainsInOrder([
'export HOME=${HOME:-$(pwd)}',
'if [ "$HOME" = "/" ]; then export HOME=$(pwd); fi',
'export PATH=$HOME/bin/gauge:$PATH',
'mkdir -p $HOME/bin/gauge',
'curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge',
'gauge telemetry off',
'gauge install java',
'gauge install html-report',
'gauge install xml-report',
'mvn test-compile gauge:execute -DspecsDir=specs'
])))
assertThat(seleniumParams.dockerImage, is('maven:3.5-jdk-8'))
assertThat(seleniumParams.dockerEnvVars, hasEntry('TARGET_SERVER_URL', 'http://test.url'))
assertThat(seleniumParams.dockerName, is('maven'))
assertThat(seleniumParams.dockerWorkspace, is(''))
assertThat(seleniumParams.stashContent, hasSize(2))
assertThat(seleniumParams.stashContent, allOf(hasItem('buildDescriptor'), hasItem('tests')))
assertJobStatusSuccess()
}
@Test
void testExecuteGaugeNode() throws Exception {
jsr.step.gaugeExecuteTests(
script: nullScript,
buildTool: 'npm',
dockerEnvVars: ['TARGET_SERVER_URL':'http://custom.url'],
juStabUtils: utils,
testOptions: 'testSpec'
)
assertThat(jscr.shell, hasItem(stringContainsInOrder([
'gauge install js',
'gauge run testSpec'
])))
assertThat(seleniumParams.dockerImage, is('node:8-stretch'))
assertThat(seleniumParams.dockerEnvVars, hasEntry('TARGET_SERVER_URL', 'http://custom.url'))
assertThat(seleniumParams.dockerName, is('npm'))
assertThat(seleniumParams.dockerWorkspace, is('/home/node'))
assertJobStatusSuccess()
}
@Test
void testExecuteCustomWithError() throws Exception {
helper.registerAllowedMethod("sh", [String.class], { s ->
throw new RuntimeException('Test Error')
})
thrown.expect(RuntimeException)
thrown.expectMessage('Test Error')
try {
jsr.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
dockerImage: 'testImage',
dockerName: 'testImageName',
dockerWorkspace: '/home/test',
failOnError: true,
stashContent: ['testStash'],
)
} finally{
assertThat(seleniumParams.dockerImage, is('testImage'))
assertThat(seleniumParams.dockerName, is('testImageName'))
assertThat(seleniumParams.dockerWorkspace, is('/home/test'))
assertThat(seleniumParams.stashContent, hasSize(1))
assertThat(jlr.log, containsString('[gaugeExecuteTests] One or more tests failed'))
assertThat(nullScript.currentBuild.result, is('UNSTABLE'))
}
}
@Test
void testExecuteGaugeCustomRepo() throws Exception {
helper.registerAllowedMethod('git', [String.class], null)
helper.registerAllowedMethod('stash', [String.class], null)
jsr.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
testRepository: 'myTestRepo',
failOnError: true
)
// nested matchers do not work correctly
assertThat(seleniumParams.stashContent, hasItem(startsWith('testContent-')))
assertJobStatusSuccess()
}
}

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"))
}
}

View File

@ -0,0 +1,240 @@
#!groovy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class MailSendNotificationTest extends BasePiperTest {
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jlr)
.around(jscr)
.around(jsr)
@Before
void init() throws Exception {
// register Jenkins commands with mock values
helper.registerAllowedMethod("deleteDir", [], null)
helper.registerAllowedMethod("sshagent", [Map.class, Closure.class], null)
nullScript.commonPipelineEnvironment.configuration = nullScript.commonPipelineEnvironment.configuration ?: [:]
nullScript.commonPipelineEnvironment.configuration['general'] = nullScript.commonPipelineEnvironment.configuration['general'] ?: [:]
nullScript.commonPipelineEnvironment.configuration['steps'] = nullScript.commonPipelineEnvironment.configuration['steps'] ?: [:]
nullScript.commonPipelineEnvironment.configuration['steps']['mailSendNotification'] = nullScript.commonPipelineEnvironment.configuration['steps']['mailSendNotification'] ?: [:]
helper.registerAllowedMethod('requestor', [], { -> return [$class: 'RequesterRecipientProvider']})
}
@Test
void testGetDistinctRecipients() throws Exception {
// git log -10 --pretty=format:"%ae %ce"
def input = '''user1@domain.com noreply+github@domain.com
user1@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user2@domain.com user1@domain.com
user1@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com'''
def result = jsr.step.getDistinctRecipients(input)
// asserts
assertThat(result.split(' '), arrayWithSize(3))
assertThat(result, containsString('user1@domain.com'))
assertThat(result, containsString('user2@domain.com'))
assertThat(result, containsString('user3@domain.com'))
}
@Test
void testCulpritsFromGitCommit() throws Exception {
def gitCommand = "git log -2 --pretty=format:'%ae %ce'"
def expected = "user2@domain.com user3@domain.com"
jscr.setReturnValue("git log -2 --pretty=format:'%ae %ce'", 'user2@domain.com user3@domain.com')
def result = jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: 'f0973368a35a2b973612acb86f932c61f2635f6e'
],
'master',
2)
println("LOGS: ${jlr.log}")
println("RESULT: ${result}")
// asserts
assertThat(result, containsString('user2@domain.com'))
assertThat(result, containsString('user3@domain.com'))
}
@Test
void testCulpritsWithEmptyGitCommit() throws Exception {
jscr.setReturnValue('git log > /dev/null 2>&1',1)
jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: ''
],
'master',
2)
// asserts
assertThat(jlr.log, containsString('[mailSendNotification] No git context available to retrieve culprits'))
}
@Test
void testCulpritsWithoutGitCommit() throws Exception {
jscr.setReturnValue('git log > /dev/null 2>&1',1)
jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: null
],
'master',
2)
// asserts
assertThat(jlr.log, containsString('[mailSendNotification] No git context available to retrieve culprits'))
}
@Test
void testCulpritsWithoutBranch() throws Exception {
jscr.setReturnValue('git log > /dev/null 2>&1',1)
jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: ''
],
null,
2)
// asserts
assertThat(jlr.log, containsString('[mailSendNotification] No git context available to retrieve culprits'))
}
@Test
void testSendNotificationMail() throws Exception {
def emailParameters = [:]
def buildMock = [
fullProjectName: 'testProjectName',
displayName: 'testDisplayName',
result: 'FAILURE',
rawBuild: [
getLog: { cnt -> return ['Setting http proxy: proxy.wdf.domain.com:8080',
' > git fetch --no-tags --progress https://github.com/SAP/jenkins-library.git +refs/heads/*:refs/remotes/origin/*',
'Checking out Revision myUniqueCommitId (master)',
' > git config core.sparsecheckout # timeout=10',
' > git checkout -f myUniqueCommitId',
'Commit message: "Merge pull request #147 from marcusholl/pr/useGitRevParseForInsideGitRepoCheck"',
' > git rev-list --no-walk myUniqueCommitId # timeout=10',
'[Pipeline] node',
'Running on Jenkins in /var/jenkins_home/workspace/Test/UserId/ECHO',
'[Pipeline] {',
'[Pipeline] stage',
'[Pipeline] { (A)',
'[Pipeline] script',
'[Pipeline] {']
}
],
getPreviousBuild: {
return null
}
]
nullScript.currentBuild = buildMock
nullScript.commonPipelineEnvironment.configuration['steps']['mailSendNotification']['notificationRecipients'] = 'piper@domain.com'
helper.registerAllowedMethod('emailext', [Map.class], { map ->
emailParameters = map
return ''
})
jsr.step.mailSendNotification(
script: nullScript,
notifyCulprits: false,
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git'
)
// asserts
assertThat(emailParameters.to, is('piper@domain.com'))
assertThat(emailParameters.subject, is('FAILURE: Build testProjectName testDisplayName'))
assertThat(emailParameters.body, startsWith('<a href="http://build.url">http://build.url</a>\n<br>\nTo have a detailed look at the different pipeline stages: <a href="null">null</a>\n<br>\n<h3>Last lines of output</h3>'))
assertThat(emailParameters.body, containsString(' > git fetch --no-tags --progress https://github.com/SAP/jenkins-library.git +refs/heads/*:refs/remotes/origin/*'))
assertJobStatusSuccess()
}
@Test
void testSendNotificationMailWithGeneralConfig() throws Exception {
def credentials
nullScript.currentBuild = [
fullProjectName: 'testProjectName',
displayName: 'testDisplayName',
result: 'FAILURE',
rawBuild: [ getLog: { cnt -> return ['empty'] } ],
getChangeSets: { return null },
getPreviousBuild: { return null }
]
nullScript.commonPipelineEnvironment.configuration['general']['gitSshKeyCredentialsId'] = 'myCredentialsId'
helper.registerAllowedMethod('emailext', [Map.class], null)
helper.registerAllowedMethod("sshagent", [Map.class, Closure.class], { map, closure ->
credentials = map.credentials
return null
})
jscr.setReturnValue("git log -0 --pretty=format:'%ae %ce'", 'user2@domain.com user3@domain.com')
jsr.step.mailSendNotification(
script: nullScript,
gitCommitId: 'abcd1234',
//notifyCulprits: true,
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git'
)
// asserts
assertThat(credentials, hasItem('myCredentialsId'))
assertJobStatusSuccess()
}
@Test
void testSendNotificationMailWithEmptySshKey() throws Exception {
def credentials
nullScript.currentBuild = [
fullProjectName: 'testProjectName',
displayName: 'testDisplayName',
result: 'FAILURE',
rawBuild: [ getLog: { cnt -> return ['empty'] } ],
getChangeSets: { return null },
getPreviousBuild: { return null }
]
helper.registerAllowedMethod('emailext', [Map.class], null)
helper.registerAllowedMethod("sshagent", [Map.class, Closure.class], { map, closure ->
credentials = map.credentials
return null
})
jscr.setReturnValue("git log -0 --pretty=format:'%ae %ce'", 'user2@domain.com user3@domain.com')
jsr.step.mailSendNotification(
script: nullScript,
gitCommitId: 'abcd1234',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git'
)
// asserts
assertThat(credentials, hasItem(''))
assertJobStatusSuccess()
}
}

View File

@ -0,0 +1,126 @@
#!groovy
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.CoreMatchers.containsString
import static org.hamcrest.CoreMatchers.is
import static org.junit.Assert.assertThat
class PipelineRestartStepsTest extends BasePiperTest {
private JenkinsErrorRule jer = new JenkinsErrorRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsStepRule jsr = new JenkinsStepRule(this)
@Rule
public RuleChain chain = Rules.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jer)
.around(jlr)
.around(jsr)
@Test
void testError() throws Exception {
def mailBuildResult = ''
helper.registerAllowedMethod('mailSendNotification', [Map.class], { m ->
mailBuildResult = m.buildResult
return null
})
helper.registerAllowedMethod('timeout', [Map.class, Closure.class], { m, closure ->
assertThat(m.time, is(1))
assertThat(m.unit, is('SECONDS'))
return closure()
})
def iterations = 0
helper.registerAllowedMethod('input', [Map.class], { m ->
iterations ++
assertThat(m.message, is('Do you want to restart?'))
assertThat(m.ok, is('Restart'))
if (iterations > 1) {
throw new FlowInterruptedException()
} else {
return null
}
})
try {
jsr.step.pipelineRestartSteps ([
script: nullScript,
jenkinsUtilsStub: jenkinsUtils,
sendMail: true,
timeoutInSeconds: 1
]) {
throw new hudson.AbortException('I just created an error')
}
} catch(err) {
assertThat(jlr.log, containsString('ERROR occured: hudson.AbortException: I just created an error'))
assertThat(mailBuildResult, is('UNSTABLE'))
}
}
@Test
void testErrorNoMail() throws Exception {
def mailBuildResult = ''
helper.registerAllowedMethod('mailSendNotification', [Map.class], { m ->
mailBuildResult = m.buildResult
return null
})
helper.registerAllowedMethod('timeout', [Map.class, Closure.class], { m, closure ->
assertThat(m.time, is(1))
assertThat(m.unit, is('SECONDS'))
return closure()
})
def iterations = 0
helper.registerAllowedMethod('input', [Map.class], { m ->
iterations ++
assertThat(m.message, is('Do you want to restart?'))
assertThat(m.ok, is('Restart'))
if (iterations > 1) {
throw new FlowInterruptedException()
} else {
return null
}
})
try {
jsr.step.pipelineRestartSteps ([
script: nullScript,
jenkinsUtilsStub: jenkinsUtils,
sendMail: false,
timeoutInSeconds: 1
]) {
throw new hudson.AbortException('I just created an error')
}
} catch(err) {
assertThat(jlr.log, containsString('ERROR occured: hudson.AbortException: I just created an error'))
assertThat(mailBuildResult, is(''))
}
}
@Test
void testSuccess() throws Exception {
jsr.step.pipelineRestartSteps ([
script: nullScript,
jenkinsUtilsStub: jenkinsUtils,
sendMail: false,
timeoutInSeconds: 1
]) {
nullScript.echo 'This is a test'
}
assertThat(jlr.log, containsString('This is a test'))
}
}

View File

@ -0,0 +1,114 @@
import hudson.AbortException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class SeleniumExecuteTestsTest extends BasePiperTest {
private ExpectedException thrown = ExpectedException.none()
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
private JenkinsDockerExecuteRule jedr = new JenkinsDockerExecuteRule(this)
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(thrown)
.around(jedr)
.around(jsr) // needs to be activated after jedr, otherwise executeDocker is not mocked
boolean bodyExecuted = false
def gitMap
@Before
void init() throws Exception {
bodyExecuted = false
helper.registerAllowedMethod('stash', [String.class], null)
helper.registerAllowedMethod('git', [Map.class], {m ->
gitMap = m
})
}
@Test
void testExecuteSeleniumDefault() {
jsr.step.seleniumExecuteTests(
script: nullScript,
juStabUtils: utils
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(jedr.dockerParams.containerPortMappings, is(['selenium/standalone-chrome': [[containerPort: 4444, hostPort: 4444]]]))
assertThat(jedr.dockerParams.dockerEnvVars, is(null))
assertThat(jedr.dockerParams.dockerImage, is('node:8-stretch'))
assertThat(jedr.dockerParams.dockerName, is('npm'))
assertThat(jedr.dockerParams.dockerWorkspace, is('/home/node'))
assertThat(jedr.dockerParams.sidecarEnvVars, is(null))
assertThat(jedr.dockerParams.sidecarImage, is('selenium/standalone-chrome'))
assertThat(jedr.dockerParams.sidecarName, is('selenium'))
assertThat(jedr.dockerParams.sidecarVolumeBind, is(['/dev/shm': '/dev/shm']))
}
@Test
void testExecuteSeleniumCustomBuildTool() {
jsr.step.seleniumExecuteTests(
script: nullScript,
buildTool: 'maven',
juStabUtils: utils
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(jedr.dockerParams.dockerImage, is('maven:3.5-jdk-8'))
assertThat(jedr.dockerParams.dockerName, is('maven'))
assertThat(jedr.dockerParams.dockerWorkspace, is(''))
}
@Test
void testExecuteSeleniumError() {
thrown.expectMessage('Error occured')
jsr.step.seleniumExecuteTests(
script: nullScript,
juStabUtils: utils
) {
throw new AbortException('Error occured')
}
}
@Test
void testExecuteSeleniumIgnoreError() {
jsr.step.seleniumExecuteTests(
script: nullScript,
failOnError: false,
juStabUtils: utils
) {
bodyExecuted = true
throw new AbortException('Error occured')
}
assertThat(bodyExecuted, is(true))
}
@Test
void testExecuteSeleniumCustomRepo() {
jsr.step.seleniumExecuteTests(
script: nullScript,
gitBranch: 'test',
gitSshKeyCredentialsId: 'testCredentials',
juStabUtils: utils,
testRepository: 'git@test/test.git'
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(gitMap, hasEntry('branch', 'test'))
assertThat(gitMap, hasEntry('credentialsId', 'testCredentials'))
assertThat(gitMap, hasEntry('url', 'git@test/test.git'))
}
}

View File

@ -4,6 +4,8 @@ import org.junit.Test
import org.junit.rules.RuleChain
import org.yaml.snakeyaml.Yaml
import com.sap.piper.Utils
import util.BasePiperTest
import util.Rules
import util.JenkinsReadYamlRule
@ -15,6 +17,7 @@ import static org.junit.Assert.assertNotNull
class SetupCommonPipelineEnvironmentTest extends BasePiperTest {
def usedConfigFile
def swaOldConfigUsed
private JenkinsStepRule jsr = new JenkinsStepRule(this)
@ -35,18 +38,52 @@ class SetupCommonPipelineEnvironmentTest extends BasePiperTest {
usedConfigFile = parameters.file
return yamlParser.load(examplePipelineConfig)
})
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
helper.registerAllowedMethod("readProperties", [Map], { Map parameters ->
usedConfigFile = parameters.file
Properties props = new Properties()
props.setProperty('key', 'value')
return props
})
swaOldConfigUsed = null
}
@Test
void testIsConfigurationAvailable() throws Exception {
jsr.step.call(script: nullScript)
void testIsYamlConfigurationAvailable() throws Exception {
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
})
jsr.step.call(script: nullScript, utils: getSWAMockedUtils())
assertEquals(Boolean.FALSE.toString(), swaOldConfigUsed)
assertEquals('.pipeline/config.yml', usedConfigFile)
assertNotNull(nullScript.commonPipelineEnvironment.configuration)
assertEquals('develop', nullScript.commonPipelineEnvironment.configuration.general.productiveBranch)
assertEquals('my-maven-docker', nullScript.commonPipelineEnvironment.configuration.steps.mavenExecute.dockerImage)
}
@Test
void testIsPropertiesConfigurationAvailable() {
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.properties')
})
jsr.step.call(script: nullScript, utils: getSWAMockedUtils())
assertEquals(Boolean.TRUE.toString(), swaOldConfigUsed)
assertEquals('.pipeline/config.properties', usedConfigFile)
assertNotNull(nullScript.commonPipelineEnvironment.configProperties)
assertEquals('value', nullScript.commonPipelineEnvironment.configProperties['key'])
}
private getSWAMockedUtils() {
new Utils() {
void pushToSWA(Map payload, Map config) {
SetupCommonPipelineEnvironmentTest.this.swaOldConfigUsed = payload.stepParam5
}
}
}
}

View File

@ -122,7 +122,6 @@ public class TransportRequestCreateTest extends BasePiperTest {
result.developmentSystemId = developmentSystemId
result.cmEndpoint = cmEndpoint
result.credentialId = credentialId
result.clientOpts = clientOpts
return '001'
}

View File

@ -0,0 +1,30 @@
package com.sap.piper
import org.junit.Test
import static org.junit.Assert.*
class CfManifestUtilsTest {
@Test
void testManifestTransform() {
Map testFixture = [applications: [[buildpacks: ['sap_java_buildpack']]]]
Map expected = [applications: [[buildpack: 'sap_java_buildpack']]]
def actual = CfManifestUtils.transform(testFixture)
assertEquals(expected, actual)
}
@Test(expected = RuntimeException)
void testManifestTransformMultipleBuildpacks() {
Map testFixture = [applications: [[buildpacks: ['sap_java_buildpack', 'another_buildpack']]]]
CfManifestUtils.transform(testFixture)
}
@Test
void testManifestTransformShouldNotChange() {
Map testFixture = [applications: [[buildpack: 'sap_java_buildpack']]]
Map expected = [applications: [[buildpack: 'sap_java_buildpack']]]
def actual = CfManifestUtils.transform(testFixture)
assertEquals(expected, actual)
}
}

View File

@ -14,6 +14,18 @@ import org.junit.rules.ExpectedException
class ConfigurationHelperTest {
Script mockScript = new Script() {
def run() {
// it never runs
throw new UnsupportedOperationException()
}
def STEP_NAME = 'mock'
def echo(message) {
}
}
@Rule
public ExpectedException thrown = ExpectedException.none()
@ -37,6 +49,7 @@ class ConfigurationHelperTest {
assertThat(ConfigurationHelper.getConfigPropertyNested([a:[b: 'c']], 'a/c'), is((nullValue())))
}
@Test
void testGetPropertyNestedPathStartsWithTokenizer() {
assertThat(ConfigurationHelper.getConfigPropertyNested([k:'v'], '/k'), is(('v')))
}
@ -53,14 +66,14 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithDefaults() {
Map config = new ConfigurationHelper([property1: '27']).use()
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27']).use()
// asserts
Assert.assertThat(config, hasEntry('property1', '27'))
}
@Test
void testConfigurationLoaderWithCustomSettings() {
Map config = new ConfigurationHelper([property1: '27'])
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27'])
.mixin([property1: '41'])
.use()
// asserts
@ -70,7 +83,7 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithFilteredCustomSettings() {
Set filter = ['property2']
Map config = new ConfigurationHelper([property1: '27'])
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27'])
.mixin([property1: '41', property2: '28', property3: '29'], filter)
.use()
// asserts
@ -81,7 +94,7 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithBooleanValue() {
Map config = new ConfigurationHelper([property1: '27'])
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27'])
.mixin([property1: false])
.mixin([property2: false])
.use()
@ -92,7 +105,7 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithMixinDependent() {
Map config = new ConfigurationHelper([
Map config = ConfigurationHelper.newInstance(mockScript, [
type: 'maven',
maven: [dockerImage: 'mavenImage', dockerWorkspace: 'mavenWorkspace'],
npm: [dockerImage: 'npmImage', dockerWorkspace: 'npmWorkspace', executeDocker: true, executeDocker3: false],
@ -122,8 +135,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibility() {
def configuration = new ConfigurationHelper()
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript)
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(4))
@ -133,8 +146,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityFlat() {
def configuration = new ConfigurationHelper()
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, null, [new1: 'old1', new2: 'old2'])
def configuration = ConfigurationHelper.newInstance(mockScript)
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, [new1: 'old1', new2: 'old2'])
.use()
Assert.assertThat(configuration.size(), is(5))
@ -144,8 +157,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityDeep() {
def configuration = new ConfigurationHelper()
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, null, [deep:[deeper:[newStructure: [new1: 'old1', new2: 'old2']]]])
def configuration = ConfigurationHelper.newInstance(mockScript)
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, [deep:[deeper:[newStructure: [new1: 'old1', new2: 'old2']]]])
.use()
Assert.assertThat(configuration.size(), is(4))
@ -155,8 +168,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityNewAvailable() {
def configuration = new ConfigurationHelper([old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'])
.mixin([old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript, [old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'])
.mixin([old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(3))
@ -165,8 +178,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityOldNotSet() {
def configuration = new ConfigurationHelper([old1: null, test: 'testValue'])
.mixin([old1: null, test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript, [old1: null, test: 'testValue'])
.mixin([old1: null, test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(2))
@ -175,8 +188,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityNoneAvailable() {
def configuration = new ConfigurationHelper([old1: null, test: 'testValue'])
.mixin([test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript, [old1: null, test: 'testValue'])
.mixin([test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(2))
@ -189,7 +202,7 @@ class ConfigurationHelperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR myKey')
new ConfigurationHelper().withMandatoryProperty('myKey')
ConfigurationHelper.newInstance(mockScript).withMandatoryProperty('myKey')
}
@Test
@ -198,22 +211,22 @@ class ConfigurationHelperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('My error message')
new ConfigurationHelper().withMandatoryProperty('myKey', 'My error message')
ConfigurationHelper.newInstance(mockScript).withMandatoryProperty('myKey', 'My error message')
}
@Test
public void testWithMandoryParameterDefaultCustomFailureMessageProvidedSucceeds() {
new ConfigurationHelper([myKey: 'myValue']).withMandatoryProperty('myKey', 'My error message')
ConfigurationHelper.newInstance(mockScript, [myKey: 'myValue']).withMandatoryProperty('myKey', 'My error message')
}
@Test
public void testWithMandoryParameterDefaultCustomFailureMessageNotProvidedSucceeds() {
new ConfigurationHelper([myKey: 'myValue']).withMandatoryProperty('myKey')
ConfigurationHelper.newInstance(mockScript, [myKey: 'myValue']).withMandatoryProperty('myKey')
}
@Test
public void testWithMandoryWithFalseCondition() {
new ConfigurationHelper([verify: false])
ConfigurationHelper.newInstance(mockScript, [verify: false])
.withMandatoryProperty('missingKey', null, { c -> return c.get('verify') })
}
@ -222,20 +235,20 @@ class ConfigurationHelperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR missingKey')
new ConfigurationHelper([verify: true])
ConfigurationHelper.newInstance(mockScript, [verify: true])
.withMandatoryProperty('missingKey', null, { c -> return c.get('verify') })
}
@Test
public void testWithMandoryWithTrueConditionExistingValue() {
new ConfigurationHelper([existingKey: 'anyValue', verify: true])
ConfigurationHelper.newInstance(mockScript, [existingKey: 'anyValue', verify: true])
.withMandatoryProperty('existingKey', null, { c -> return c.get('verify') })
}
@Test
public void testTelemetryConfigurationAvailable() {
Set filter = ['test']
def configuration = new ConfigurationHelper([test: 'testValue'])
def configuration = ConfigurationHelper.newInstance(mockScript, [test: 'testValue'])
.mixin([collectTelemetryData: false], filter)
.use()
@ -257,7 +270,7 @@ class ConfigurationHelperTest {
assert bGString instanceof GString
assert cGString instanceof GString
def config = new ConfigurationHelper([a: aGString,
def config = ConfigurationHelper.newInstance(mockScript, [a: aGString,
nextLevel: [b: bGString]])
.mixin([c : cGString])
.use()
@ -269,4 +282,42 @@ class ConfigurationHelperTest {
assert config.c instanceof java.lang.String
assert config.nextLevel.b instanceof java.lang.String
}
@Test
public void testWithMandatoryParameterCollectFailuresAllParamtersArePresentResultsInNoExceptionThrown() {
ConfigurationHelper.newInstance(mockScript, [myKey1: 'a', myKey2: 'b'])
.collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
.use()
}
@Test
public void testWithMandatoryParameterCollectFailuresMultipleMissingParametersDoNotResultInFailuresDuringWithMandatoryProperties() {
ConfigurationHelper.newInstance(mockScript, [:]).collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
}
@Test
public void testWithMandatoryParameterCollectFailuresMultipleMissingParametersResultsInFailureDuringUse() {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR: myKey2, myKey3')
ConfigurationHelper.newInstance(mockScript, [myKey1:'a']).collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
.withMandatoryProperty('myKey3')
.use()
}
@Test
public void testWithMandatoryParameterCollectFailuresOneMissingParametersResultsInFailureDuringUse() {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR myKey2')
ConfigurationHelper.newInstance(mockScript, [myKey1:'a']).collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
.use()
}
}

View File

@ -1,20 +1,26 @@
package com.sap.piper
import static org.hamcrest.Matchers.equalTo
import static org.hamcrest.Matchers.hasEntry
import static org.hamcrest.Matchers.hasItem
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.notNullValue
import static org.hamcrest.Matchers.startsWith
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.JenkinsShellCallRule
import util.Rules
import static org.junit.Assert.assertEquals
import static org.hamcrest.Matchers.equalTo
import static org.junit.Assert.assertTrue
import static org.junit.Assert.assertFalse
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.notNullValue
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertNull
import static org.junit.Assert.assertThat
@ -26,11 +32,15 @@ class GitUtilsTest extends BasePiperTest {
@Autowired
GitUtils gitUtils
JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
ExpectedException thrown = ExpectedException.none()
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
private ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain ruleChain = Rules.getCommonRules(this).around(jscr).around(thrown)
public RuleChain ruleChain = Rules.getCommonRules(this)
.around(jlr)
.around(jscr)
.around(thrown)
@Before
void init() throws Exception {
@ -96,4 +106,24 @@ class GitUtilsTest extends BasePiperTest {
assertNotNull(log)
assertThat(log.size(),is(equalTo(0)))
}
@Test
void testHandleTestRepository() {
def result, gitMap, stashName, config = [
testRepository: 'repoUrl',
gitSshKeyCredentialsId: 'abc',
gitBranch: 'master'
]
helper.registerAllowedMethod('git', [Map.class], {m -> gitMap = m })
helper.registerAllowedMethod("stash", [String.class], { s -> stashName = s})
result = GitUtils.handleTestRepository(nullScript, config)
// asserts
assertThat(gitMap, hasEntry('url', config.testRepository))
assertThat(gitMap, hasEntry('credentialsId', config.gitSshKeyCredentialsId))
assertThat(gitMap, hasEntry('branch', config.gitBranch))
assertThat(stashName, startsWith('testContent-'))
assertThat(result, startsWith('testContent-'))
}
}

View File

@ -0,0 +1,45 @@
package com.sap.piper
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
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.JenkinsShellCallRule
import util.Rules
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class JenkinsUtilsTest extends BasePiperTest {
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(jscr)
.around(jlr)
@Test
void testNodeAvailable() {
def result = jenkinsUtils.nodeAvailable()
assertThat(jscr.shell, contains("echo 'Node is available!'"))
assertThat(result, is(true))
}
@Test
void testNoNodeAvailable() {
helper.registerAllowedMethod('sh', [String.class], {s ->
throw new MissingContextVariableException(String.class)
})
def result = jenkinsUtils.nodeAvailable()
assertThat(jlr.log, containsString('No node context available.'))
assertThat(result, is(false))
}
}

View File

@ -4,6 +4,7 @@ package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import com.sap.piper.GitUtils
import com.sap.piper.JenkinsUtils
import com.sap.piper.Utils
import org.junit.Before
import org.junit.runner.RunWith
@ -23,6 +24,9 @@ abstract class BasePiperTest extends BasePipelineTest {
@Autowired
Utils utils
@Autowired
JenkinsUtils jenkinsUtils
@Override
@Before
void setUp() throws Exception {

View File

@ -3,6 +3,7 @@
package util
import com.sap.piper.GitUtils
import com.sap.piper.JenkinsUtils
import com.sap.piper.Utils
import org.codehaus.groovy.runtime.InvokerHelper
import org.springframework.context.annotation.Bean
@ -36,4 +37,11 @@ class BasePiperTestContext {
LibraryLoadingTestExecutionListener.prepareObjectInterceptors(mockUtils)
return mockUtils
}
@Bean
JenkinsUtils mockJenkinsUtils() {
def mockJenkinsUtils = new JenkinsUtils()
LibraryLoadingTestExecutionListener.prepareObjectInterceptors(mockJenkinsUtils)
return mockJenkinsUtils
}
}

View File

@ -40,7 +40,7 @@ class JenkinsSetupRule implements TestRule {
testInstance.binding.setVariable('env', [
JOB_NAME : 'p',
BUILD_NUMBER: '1',
BUILD_URL : ''
BUILD_URL : 'http://build.url',
])
base.evaluate()

View File

@ -72,6 +72,7 @@ class JenkinsShellCallRule implements TestRule {
}
}
if(result instanceof Closure) result = result()
if (!result && m.returnStatus) result = 0
return result
}
})

View File

@ -25,7 +25,7 @@ import groovy.text.SimpleTemplateEngine
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus('gitCommitId')
def call(Map parameters = [:], Closure body = null) {
void call(Map parameters = [:], Closure body = null) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -41,13 +41,13 @@ def call(Map parameters = [:], Closure body = null) {
script = this
// load default & individual configuration
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixin(gitCommitId: gitUtils.getGitCommitIdOrNull())
.mixin(parameters, PARAMETER_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
.withMandatoryProperty('buildTool')
.dependingOn('buildTool').mixin('filePath')
.dependingOn('buildTool').mixin('versioningTemplate')
@ -77,15 +77,13 @@ def call(Map parameters = [:], Closure body = null) {
}
if (config.commitVersion) {
config = new ConfigurationHelper(config)
config = ConfigurationHelper.newInstance(this, config)
.addIfEmpty('gitSshUrl', isAppContainer(config)
?script.commonPipelineEnvironment.getAppContainerProperty('gitSshUrl')
:script.commonPipelineEnvironment.getGitSshUrl())
.withMandatoryProperty('gitSshUrl')
.use()
sh 'git add .'
def gitConfig = []
if(config.gitUserEMail) gitConfig.add("-c user.email=\"${config.gitUserEMail}\"")
@ -94,6 +92,7 @@ def call(Map parameters = [:], Closure body = null) {
try {
sh """#!/bin/bash
git add .
git ${gitConfig} commit -m 'update version ${newVersion}'
git tag ${config.tagPrefix}${newVersion}"""
config.gitCommitId = gitUtils.getGitCommitIdOrNull()

View File

@ -1,11 +1,12 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'batsExecuteTests'
@Field Set STEP_CONFIG_KEYS = [
'dockerImage', //
'dockerImage',
'dockerWorkspace',
'envVars',
'failOnError',
@ -20,14 +21,14 @@ import groovy.transform.Field
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def utils = parameters.juStabUtils ?: new Utils()
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
@ -39,17 +40,9 @@ def call(Map parameters = [:]) {
script.commonPipelineEnvironment.setInfluxStepData('bats', false)
if (config.testRepository) {
def gitParameters = [url: config.testRepository]
if (config.gitSshKeyCredentialsId?.length()>0) gitParameters.credentialsId = config.gitSshKeyCredentialsId
if (config.gitBranch?.length()>0) gitParameters.branch = config.gitBranch
git gitParameters
stash 'batsTests'
config.stashContent = ['batsTests']
} else {
config.stashContent = utils.unstashAll(config.stashContent)
}
config.stashContent = config.testRepository
?[GitUtils.handleTestRepository(this, config)]
:utils.unstashAll(config.stashContent)
//resolve commonPipelineEnvironment references in envVars
config.envVarList = []

View File

@ -22,7 +22,7 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati
@Field Set generalConfigurationKeys = stepConfigurationKeys
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -32,8 +32,8 @@ def call(parameters = [:]) {
ChangeManagement cm = parameters?.cmUtils ?: new ChangeManagement(script, gitUtils)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)
@ -111,14 +111,12 @@ def call(parameters = [:]) {
if(isInDevelopment) {
echo "[INFO] Change '${changeId}' is in status 'in development'."
return true
} else {
if(configuration.failIfStatusIsNotInDevelopment.toBoolean()) {
throw new AbortException("Change '${changeId}' is not in status 'in development'.")
} else {
echo "[WARNING] Change '${changeId}' is not in status 'in development'. Failing the pipeline has been explicitly disabled."
return false
}
}
}

View File

@ -21,7 +21,7 @@ import groovy.transform.Field
*
* @param others document all parameters
*/
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script
if (script == null)
@ -30,8 +30,8 @@ def call(Map parameters = [:]) {
prepare(parameters)
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -1,5 +1,6 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.CfManifestUtils
import groovy.transform.Field
@ -20,7 +21,7 @@ import groovy.transform.Field
@Field Map CONFIG_KEY_COMPATIBILITY = [cloudFoundry: [apiEndpoint: 'cfApiEndpoint', appName:'cfAppName', credentialsId: 'cfCredentialsId', manifest: 'cfManifest', org: 'cfOrg', space: 'cfSpace']]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -33,12 +34,12 @@ def call(Map parameters = [:]) {
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS, this, CONFIG_KEY_COMPATIBILITY)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
.dependingOn('deployTool').mixin('dockerImage')
.dependingOn('deployTool').mixin('dockerWorkspace')
.withMandatoryProperty('cloudFoundry/org')
@ -54,7 +55,7 @@ def call(Map parameters = [:]) {
if (config.deployTool == 'mtaDeployPlugin') {
// set default mtar path
config = new ConfigurationHelper(config)
config = ConfigurationHelper.newInstance(this, config)
.addIfEmpty('mtaPath', config.mtaPath?:findMtar())
.use()
@ -112,6 +113,7 @@ def deployCfNative (config) {
def deployCommand = 'push'
if (config.deployType == 'blue-green') {
deployCommand = 'blue-green-deploy'
handleLegacyCfManifest(config)
} else {
config.smokeTest = ''
}
@ -129,7 +131,7 @@ def deployCfNative (config) {
}
sh """#!/bin/bash
set +x
set +x
export HOME=${config.dockerWorkspace}
cf login -u \"${username}\" -p '${password}' -a ${config.cloudFoundry.apiEndpoint} -o \"${config.cloudFoundry.org}\" -s \"${config.cloudFoundry.space}\"
cf plugins
@ -166,3 +168,18 @@ def deployMta (config) {
sh "cf logout"
}
}
def handleLegacyCfManifest(config) {
def manifest = readYaml file: config.cloudFoundry.manifest
String originalManifest = manifest.toString()
manifest = CfManifestUtils.transform(manifest)
String transformedManifest = manifest.toString()
if (originalManifest != transformedManifest) {
echo """The file ${config.cloudFoundry.manifest} is not compatible with the Cloud Foundry blue-green deployment plugin. Re-writing inline.
See this issue if you are interested in the background: https://github.com/cloudfoundry/cli/issues/1445.\n
Original manifest file content: $originalManifest\n
Transformed manifest file content: $transformedManifest"""
sh "rm ${config.cloudFoundry.manifest}"
writeYaml file: config.cloudFoundry.manifest, data: manifest
}
}

View File

@ -1,12 +1,21 @@
class commonPipelineEnvironment implements Serializable {
private Map configProperties = [:]
Map configProperties = [:]
//stores version of the artifact which is build during pipeline run
def artifactVersion
//Stores the current buildResult
String buildResult = 'SUCCESS'
//stores the gitCommitId as well as additional git information for the build during pipeline run
private String gitCommitId
private String gitSshUrl
String gitCommitId
String gitSshUrl
String gitHttpsUrl
String gitBranch
//GiutHub specific information
String githubOrg
String githubRepo
//stores properties for a pipeline which build an artifact and then bundles it into a container
private Map appContainerProperties = [:]
@ -19,7 +28,7 @@ class commonPipelineEnvironment implements Serializable {
//influxCustomData represents measurement jenkins_custom_data in Influx. Metrics can be written into this map
private Map influxCustomData = [:]
private String mtarFilePath
String mtarFilePath
def reset() {
appContainerProperties = [:]
@ -30,6 +39,11 @@ class commonPipelineEnvironment implements Serializable {
gitCommitId = null
gitSshUrl = null
gitHttpsUrl = null
gitBranch = null
githubOrg = null
githubRepo = null
influxCustomData = [:]
influxCustomDataMap = [pipeline_data: [:], step_data: [:]]
@ -45,22 +59,6 @@ class commonPipelineEnvironment implements Serializable {
return appContainerProperties[property]
}
def setArtifactVersion(version) {
artifactVersion = version
}
def getArtifactVersion() {
return artifactVersion
}
def setConfigProperties(map) {
configProperties = map
}
def getConfigProperties() {
return configProperties
}
def setConfigProperty(property, value) {
configProperties[property] = value
}
@ -72,22 +70,6 @@ class commonPipelineEnvironment implements Serializable {
return configProperties[property]
}
def setGitCommitId(commitId) {
gitCommitId = commitId
}
def getGitCommitId() {
return gitCommitId
}
def setGitSshUrl(url) {
gitSshUrl = url
}
def getGitSshUrl() {
return gitSshUrl
}
def getInfluxCustomData() {
return influxCustomData
}
@ -103,15 +85,6 @@ class commonPipelineEnvironment implements Serializable {
return influxCustomDataMap.step_data[dataKey]
}
def getMtarFilePath() {
return mtarFilePath
}
void setMtarFilePath(mtarFilePath) {
this.mtarFilePath = mtarFilePath
}
def setPipelineMeasurement (measurementName, value) {
influxCustomDataMap.pipeline_data[measurementName] = value
}

View File

@ -9,11 +9,21 @@ import groovy.transform.Field
@Field Set GENERAL_CONFIG_KEYS = ['jenkinsKubernetes']
@Field Set PARAMETER_KEYS = ['dockerImage',
'dockerOptions',
'dockerWorkspace',
'dockerEnvVars',
'dockerVolumeBind']
@Field Set PARAMETER_KEYS = [
'containerPortMappings',
'dockerEnvVars',
'dockerImage',
'dockerName',
'dockerOptions',
'dockerWorkspace',
'dockerVolumeBind',
'sidecarName',
'sidecarEnvVars',
'sidecarImage',
'sidecarOptions',
'sidecarWorkspace',
'sidecarVolumeBind'
]
@Field Set STEP_CONFIG_KEYS = PARAMETER_KEYS
void call(Map parameters = [:], body) {
@ -21,8 +31,8 @@ void call(Map parameters = [:], body) {
final script = parameters.script
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
@ -31,19 +41,48 @@ void call(Map parameters = [:], body) {
if (isKubernetes() && config.dockerImage) {
if (env.POD_NAME && isContainerDefined(config)) {
container(getContainerDefined(config)) {
echo "Executing inside a Kubernetes Container"
echo "[INFO][${STEP_NAME}] Executing inside a Kubernetes Container."
body()
sh "chown -R 1000:1000 ."
}
} else {
dockerExecuteOnKubernetes(
script: script,
dockerImage: config.dockerImage,
dockerEnvVars: config.dockerEnvVars,
dockerWorkspace: config.dockerWorkspace
){
echo "Executing inside a Kubernetes Pod"
body()
if (!config.sidecarImage) {
dockerExecuteOnKubernetes(
script: script,
dockerImage: config.dockerImage,
dockerEnvVars: config.dockerEnvVars,
dockerWorkspace: config.dockerWorkspace
){
echo "[INFO][${STEP_NAME}] Executing inside a Kubernetes Pod"
body()
}
} else {
Map paramMap = [
script: script,
containerCommands: [:],
containerEnvVars: [:],
containerMap: [:],
containerName: config.dockerName,
containerPortMappings: [:],
containerWorkspaces: [:]
]
paramMap.containerCommands[config.sidecarImage] = ''
paramMap.containerEnvVars[config.dockerImage] = config.dockerEnvVars
paramMap.containerEnvVars[config.sidecarImage] = config.sidecarEnvVars
paramMap.containerMap[config.dockerImage] = config.dockerName
paramMap.containerMap[config.sidecarImage] = config.sidecarName
paramMap.containerPortMappings = config.containerPortMappings
paramMap.containerWorkspaces[config.dockerImage] = config.dockerWorkspace
paramMap.containerWorkspaces[config.sidecarImage] = ''
dockerExecuteOnKubernetes(paramMap){
echo "[INFO][${STEP_NAME}] Executing inside a Kubernetes Pod with sidecar container"
body()
}
}
}
} else {
@ -67,8 +106,21 @@ void call(Map parameters = [:], body) {
if (executeInsideDocker && config.dockerImage) {
def image = docker.image(config.dockerImage)
image.pull()
image.inside(getDockerOptions(config.dockerEnvVars, config.dockerVolumeBind, config.dockerOptions)) {
body()
if (!config.sidecarImage) {
image.inside(getDockerOptions(config.dockerEnvVars, config.dockerVolumeBind, config.dockerOptions)) {
body()
}
} else {
def sidecarImage = docker.image(config.sidecarImage)
sidecarImage.pull()
sidecarImage.withRun(getDockerOptions(config.sidecarEnvVars, config.sidecarVolumeBind, config.sidecarOptions)) { c ->
config.dockerOptions = config.dockerOptions?:[]
config.dockerOptions.add("--link ${c.id}:${config.sidecarName}")
image.inside(getDockerOptions(config.dockerEnvVars, config.dockerVolumeBind, config.dockerOptions)) {
echo "[INFO][${STEP_NAME}] Running with sidecar container."
body()
}
}
}
} else {
echo "[INFO][${STEP_NAME}] Running on local environment."

View File

@ -7,10 +7,17 @@ import hudson.AbortException
@Field def STEP_NAME = 'dockerExecuteOnKubernetes'
@Field def PLUGIN_ID_KUBERNETES = 'kubernetes'
@Field Set GENERAL_CONFIG_KEYS = ['jenkinsKubernetes']
@Field Set PARAMETER_KEYS = ['dockerImage',
'dockerWorkspace',
'dockerEnvVars',
'containerMap']
@Field Set PARAMETER_KEYS = [
'containerCommands', //specify 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', //specify environment variables per container. If not provided dockerEnvVars will be used
'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]]]
'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',
'dockerWorkspace',
'dockerEnvVars'
]
@Field Set STEP_CONFIG_KEYS = PARAMETER_KEYS.plus(['stashIncludes', 'stashExcludes'])
void call(Map parameters = [:], body) {
@ -22,34 +29,21 @@ void call(Map parameters = [:], body) {
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.addIfEmpty('uniqueId', UUID.randomUUID().toString())
Map config = [:]
Map config = configHelper.use()
if (parameters.containerMap) {
config = configHelper.use()
executeOnPodWithCustomContainerList(config: config) { body() }
} else {
config = configHelper
.withMandatoryProperty('dockerImage')
.use()
executeOnPodWithSingleContainer(config: config) { body() }
}
}
}
void executeOnPodWithCustomContainerList(Map parameters, body) {
def config = parameters.config
podTemplate(getOptions(config)) {
node(config.uniqueId) {
body()
if (!parameters.containerMap) {
configHelper.withMandatoryProperty('dockerImage')
config.containerName = 'container-exec'
config.containerMap = ["${config.get('dockerImage')}": config.containerName]
}
executeOnPod(config, body)
}
}
@ -59,40 +53,39 @@ def getOptions(config) {
containers: getContainerList(config)]
}
void executeOnPodWithSingleContainer(Map parameters, body) {
Map containerMap = [:]
def config = parameters.config
containerMap[config.get('dockerImage').toString()] = 'container-exec'
config.containerMap = containerMap
void executeOnPod(Map config, Closure body) {
/*
* There could be exceptions thrown by
- The podTemplate
- The container method
- The body
* We use nested exception handling in this case.
* In the first 2 cases, the workspace has not been modified. Hence, we can stash existing workspace as container and
* unstash in the finally block. In case of exception thrown by the body, we need to stash the workspace from the container
* in finally block
* In the first 2 cases, the 'container' stash is not created because the inner try/finally is not reached.
* However, the workspace has not been modified and don't need to be restored.
* In case third case, we need to create the 'container' stash to bring the modified content back to the host.
*/
try {
stashWorkspace(config, 'workspace')
if (config.containerName)
stashWorkspace(config, 'workspace')
podTemplate(getOptions(config)) {
node(config.uniqueId) {
container(name: 'container-exec') {
try {
unstashWorkspace(config, 'workspace')
body()
} finally {
stashWorkspace(config, 'container')
}
if (config.containerName) {
container(name: config.containerName){
try {
unstashWorkspace(config, 'workspace')
body()
} finally {
stashWorkspace(config, 'container')
}
}
} else {
body()
}
}
}
} catch (e) {
stashWorkspace(config, 'container')
throw e
} finally {
unstashWorkspace(config, 'container')
if (config.containerName)
unstashWorkspace(config, 'container')
}
}
@ -119,16 +112,35 @@ private void unstashWorkspace(config, prefix) {
}
private List getContainerList(config) {
def envVars = getContainerEnvs(config)
result = []
result.push(containerTemplate(name: 'jnlp',
image: config.jenkinsKubernetes.jnlpAgent))
result.push(containerTemplate(
name: 'jnlp',
image: config.jenkinsKubernetes.jnlpAgent
))
config.containerMap.each { imageName, containerName ->
result.push(containerTemplate(name: containerName.toLowerCase(),
def templateParameters = [
name: containerName.toLowerCase(),
image: imageName,
alwaysPullImage: true,
command: '/usr/bin/tail -f /dev/null',
envVars: envVars))
envVars: getContainerEnvs(config, imageName)
]
if (!config.containerCommands?.get(imageName)?.isEmpty()) {
templateParameters.command = config.containerCommands?.get(imageName)?: '/usr/bin/tail -f /dev/null'
}
if (config.containerPortMappings?.get(imageName)) {
def ports = []
def portCounter = 0
config.containerPortMappings.get(imageName).each {mapping ->
mapping.name = "${containerName}${portCounter}".toString()
ports.add(portMapping(mapping))
portCounter ++
}
templateParameters.ports = ports
}
result.push(containerTemplate(templateParameters))
}
return result
}
@ -139,10 +151,10 @@ private List getContainerList(config) {
* (Kubernetes-Plugin only!)
* @param config Map with configurations
*/
private List getContainerEnvs(config) {
private List getContainerEnvs(config, imageName) {
def containerEnv = []
def dockerEnvVars = config.dockerEnvVars ?: [:]
def dockerWorkspace = config.dockerWorkspace ?: ''
def dockerEnvVars = config.containerEnvVars?.get(imageName) ?: config.dockerEnvVars ?: [:]
def dockerWorkspace = config.containerWorkspaces?.get(imageName) != null ? config.containerWorkspaces?.get(imageName) : config.dockerWorkspace ?: ''
if (dockerEnvVars) {
for (String k : dockerEnvVars.keySet()) {

View File

@ -1,3 +1,7 @@
import groovy.transform.Field
@Field STEP_NAME = 'durationMeasure'
def call(Map parameters = [:], body) {
def script = parameters.script

View File

@ -0,0 +1,94 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GitUtils
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'gaugeExecuteTests'
@Field Set STEP_CONFIG_KEYS = [
'buildTool',
'dockerEnvVars',
'dockerImage',
'dockerName',
'dockerWorkspace',
'failOnError',
'gitBranch',
'gitSshKeyCredentialsId',
'installCommand',
'languageRunner',
'runCommand',
'stashContent',
'testOptions',
'testRepository',
'testServerUrl'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def utils = parameters.juStabUtils ?: new Utils()
script.commonPipelineEnvironment.setInfluxStepData('gauge', false)
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.dependingOn('buildTool').mixin('dockerImage')
.dependingOn('buildTool').mixin('dockerName')
.dependingOn('buildTool').mixin('dockerWorkspace')
.dependingOn('buildTool').mixin('languageRunner')
.dependingOn('buildTool').mixin('runCommand')
.dependingOn('buildTool').mixin('testOptions')
.use()
utils.pushToSWA([step: STEP_NAME, stepParam1: config.buildTool, stepParam2: config.dockerName], config)
if(!config.dockerEnvVars.TARGET_SERVER_URL && config.testServerUrl)
config.dockerEnvVars.TARGET_SERVER_URL = config.testServerUrl
if (config.testRepository) {
// handle separate test repository
config.stashContent = [GitUtils.handleTestRepository(this, config)]
} else {
config.stashContent = utils.unstashAll(config.stashContent)
}
seleniumExecuteTests (
script: script,
buildTool: config.buildTool,
dockerEnvVars: config.dockerEnvVars,
dockerImage: config.dockerImage,
dockerName: config.dockerName,
dockerWorkspace: config.dockerWorkspace,
stashContent: config.stashContent
) {
String gaugeScript = ''
if (config.installCommand) {
gaugeScript = '''export HOME=${HOME:-$(pwd)}
if [ "$HOME" = "/" ]; then export HOME=$(pwd); fi
export PATH=$HOME/bin/gauge:$PATH
mkdir -p $HOME/bin/gauge
''' + config.installCommand + '''
gauge telemetry off
gauge install ''' + config.languageRunner + '''
gauge install html-report
gauge install xml-report
'''
}
gaugeScript += config.runCommand
try {
sh "${gaugeScript} ${config.testOptions}"
script.commonPipelineEnvironment.setInfluxStepData('gauge', true)
} catch (err) {
echo "[${STEP_NAME}] One or more tests failed"
script.currentBuild.result = 'UNSTABLE'
if (config.failOnError) throw err
}
}
}
}

View File

@ -1,5 +1,9 @@
import groovy.transform.Field
def call(Map parameters = [:], body) {
@Field STEP_NAME = 'handlePipelineStepErrors'
void call(Map parameters = [:], body) {
def stepParameters = parameters.stepParameters //mandatory
def stepName = parameters.stepName //mandatory
@ -18,12 +22,12 @@ def call(Map parameters = [:], body) {
} catch (Throwable err) {
if (echoDetails)
echo """----------------------------------------------------------
--- ERROR OCCURED IN LIBRARY STEP: ${stepName}
--- ERROR OCCURRED IN LIBRARY STEP: ${stepName}
----------------------------------------------------------
FOLLOWING PARAMETERS WERE AVAILABLE TO THIS STEP:
***
${stepParameters}
${stepParameters?.toString()}
***
ERROR WAS:
@ -36,7 +40,7 @@ FURTHER INFORMATION:
* Source code of library step ${stepName}: https://github.com/SAP/jenkins-library/blob/master/vars/${stepName}.groovy
* Library documentation: https://sap.github.io/jenkins-library/
* Library repository: https://github.com/SAP/jenkins-library
----------------------------------------------------------"""
throw err
} finally {

View File

@ -0,0 +1,48 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
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.newInstance(this)
.loadStepDefaults()
.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()
new Utils().pushToSWA([step: STEP_NAME], config)
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()
}

View File

@ -17,15 +17,15 @@ import groovy.transform.Field
'artifactVersion'
])
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, allowBuildFailure: true) {
def script = parameters.script
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -0,0 +1,194 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'mailSendNotification'
@Field Set GENERAL_CONFIG_KEYS = ['gitSshKeyCredentialsId']
@Field Set STEP_CONFIG_KEYS = [
'projectName',
'buildResult',
'gitUrl',
'gitCommitId',
'gitSshKeyCredentialsId',
'wrapInNode',
'notifyCulprits',
'notificationAttachment',
'notificationRecipients',
'numLogLinesInBody'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, allowBuildFailure: true) {
def script = parameters.script?:[commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(
projectName: script.currentBuild.fullProjectName,
displayName: script.currentBuild.displayName,
buildResult: script.currentBuild.result,
gitUrl: script.commonPipelineEnvironment.getGitSshUrl(),
gitCommitId: script.commonPipelineEnvironment.getGitCommitId()
)
.mixin(parameters, PARAMETER_KEYS)
.use()
new Utils().pushToSWA([step: STEP_NAME], config)
//this takes care that terminated builds due to milestone-locking do not cause an error
if (script.commonPipelineEnvironment.getBuildResult() == 'ABORTED') return
def subject = "${config.buildResult}: Build ${config.projectName} ${config.displayName}"
def log = ''
def mailTemplate
if (config.buildResult == 'UNSTABLE' || config.buildResult == 'FAILURE'){
mailTemplate = 'com.sap.piper/templates/mailFailure.html'
log = script.currentBuild.rawBuild.getLog(config.numLogLinesInBody).join('\n')
}else if(hasRecovered(config.buildResult, script.currentBuild)){
mailTemplate = 'com.sap.piper/templates/mailRecover.html'
subject += ' is back to normal'
}
if(mailTemplate){
def mailContent = SimpleTemplateEngine.newInstance().createTemplate(libraryResource(mailTemplate)).make([env: env, log: log]).toString()
def recipientList = ''
if(config.notifyCulprits){
if (!config.gitUrl) {
echo "[${STEP_NAME}] no gitUrl available, -> exiting without sending mails"
return
}
recipientList += getCulpritCommitters(config, script.currentBuild)
}
if(config.notificationRecipients)
recipientList += " ${config.notificationRecipients}"
emailext(
mimeType: 'text/html',
subject: subject.trim(),
body: mailContent,
to: recipientList.trim(),
recipientProviders: [requestor()],
attachLog: config.notificationAttachment
)
}
}
}
def getNumberOfCommits(buildList){
def numCommits = 0
if(buildList != null)
for(actBuild in buildList) {
def changeLogSets = actBuild.getChangeSets()
if(changeLogSets != null)
for(changeLogSet in changeLogSets)
for(change in changeLogSet)
numCommits++
}
return numCommits
}
def getCulpritCommitters(config, currentBuild) {
def recipients
def buildList = []
def build = currentBuild
if (build != null) {
// At least add the current build
buildList.add(build)
// Now collect FAILED or ABORTED ones
build = build.getPreviousBuild()
while (build != null) {
if (build.getResult() != 'SUCCESS') {
buildList.add(build)
} else {
break
}
build = build.getPreviousBuild()
}
}
def numberOfCommits = getNumberOfCommits(buildList)
if(config.wrapInNode){
node(){
try{
recipients = getCulprits(config, env.BRANCH_NAME, numberOfCommits)
}finally{
deleteDir()
}
}
}else{
try{
recipients = getCulprits(config, env.BRANCH_NAME, numberOfCommits)
}finally{
deleteDir()
}
}
echo "[${STEP_NAME}] last ${numberOfCommits} commits revealed following responsibles ${recipients}"
return recipients
}
def getCulprits(config, branch, numberOfCommits) {
if (branch?.startsWith('PR-')) {
//special GitHub Pull Request handling
deleteDir()
sshagent(
credentials: [config.gitSshKeyCredentialsId],
ignoreMissing: true
) {
def pullRequestID = branch.replaceAll('PR-', '')
def localBranchName = "pr" + pullRequestID;
sh """git init
git fetch ${config.gitUrl} pull/${pullRequestID}/head:${localBranchName} > /dev/null 2>&1
git checkout -f ${localBranchName} > /dev/null 2>&1
"""
}
} else {
//standard git/GitHub handling
if (config.gitCommitId) {
deleteDir()
sshagent(
credentials: [config.gitSshKeyCredentialsId],
ignoreMissing: true
) {
sh """git clone ${config.gitUrl} .
git checkout ${config.gitCommitId} > /dev/null 2>&1"""
}
} else {
def retCode = sh(returnStatus: true, script: 'git log > /dev/null 2>&1')
if (retCode != 0) {
echo "[${STEP_NAME}] No git context available to retrieve culprits"
return ''
}
}
}
def recipients = sh(returnStdout: true, script: "git log -${numberOfCommits} --pretty=format:'%ae %ce'")
return getDistinctRecipients(recipients)
}
def getDistinctRecipients(recipients){
def result
def recipientAddresses = recipients.split()
def knownAddresses = new HashSet<String>()
if(recipientAddresses != null) {
for(address in recipientAddresses) {
address = address.trim()
if(address
&& address.contains("@")
&& !address.startsWith("noreply")
&& !knownAddresses.contains(address)) {
knownAddresses.add(address)
}
}
result = knownAddresses.join(" ")
}
return result
}
def hasRecovered(buildResult, currentBuild){
return buildResult == 'SUCCESS' && currentBuild.getPreviousBuild()?.result != 'SUCCESS'
}

View File

@ -21,13 +21,13 @@ import groovy.transform.Field
'logSuccessfulMavenTransfers'
])
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = parameters.script
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -20,13 +20,13 @@ import groovy.transform.Field
'dockerOptions'
])
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -29,7 +29,7 @@ import groovy.transform.Field
'warAction'
])
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
@ -70,10 +70,10 @@ def call(parameters = [:]) {
parameters.put('neoCredentialsId', credId)
}
// Backward compatibility end
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixin(stepCompatibilityConfiguration)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
@ -81,9 +81,9 @@ def call(parameters = [:]) {
.addIfEmpty('archivePath', script.commonPipelineEnvironment.getMtarFilePath())
.mixin(parameters, PARAMETER_KEYS)
.use()
utils.pushToSWA([
step: STEP_NAME,
step: STEP_NAME,
stepParam1: configuration.deployMode == 'mta'?'mta':'war', // ['mta', 'warParams', 'warPropertiesFile']
stepParam2: configuration.warAction == 'rolling-update'?'blue-green':'standard' // ['deploy', 'deploy-mta', 'rolling-update']
], configuration)

View File

@ -1,8 +1,8 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import groovy.transform.Field
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'newmanExecute'
@Field Set STEP_CONFIG_KEYS = [
@ -19,14 +19,14 @@ import groovy.text.SimpleTemplateEngine
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def utils = parameters?.juStabUtils ?: new Utils()
// load default & individual configuration
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
@ -35,16 +35,9 @@ def call(Map parameters = [:]) {
new Utils().pushToSWA([step: STEP_NAME], config)
if (config.testRepository) {
def gitParameters = [url: config.testRepository]
if (config.gitSshKeyCredentialsId) gitParameters.credentialsId = config.gitSshKeyCredentialsId
if (config.gitBranch) gitParameters.branch = config.gitBranch
git gitParameters
stash 'newmanContent'
config.stashContent = ['newmanContent']
} else {
config.stashContent = utils.unstashAll(config.stashContent)
}
config.stashContent = config.testRepository
?[GitUtils.handleTestRepository(this, config)]
:utils.unstashAll(config.stashContent)
List collectionList = findFiles(glob: config.newmanCollection)?.toList()
if (collectionList.isEmpty()) {

View File

@ -1,11 +1,17 @@
import com.sap.piper.Utils
import groovy.transform.Field
@Field STEP_NAME = 'pipelineExecute'
/**
* pipelineExecute
* Load and executes a pipeline from another git repository.
*
*/
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
node() {

View File

@ -0,0 +1,52 @@
import com.sap.piper.JenkinsUtils
import com.sap.piper.ConfigurationHelper
import groovy.transform.Field
@Field String STEP_NAME = 'pipelineRestartSteps'
@Field Set STEP_CONFIG_KEYS = [
'sendMail',
'timeoutInSeconds'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:], body) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def jenkinsUtils = parameters.jenkinsUtilsStub ?: new JenkinsUtils()
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.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)
.use()
def restart = true
while (restart) {
try {
body()
restart = false
} catch (Throwable err) {
echo "ERROR occured: ${err}"
if (config.sendMail)
if (jenkinsUtils.nodeAvailable()) {
mailSendNotification script: script, buildResult: 'UNSTABLE'
} else {
node {
mailSendNotification script: script, buildResult: 'UNSTABLE'
}
}
try {
timeout(time: config.timeoutInSeconds, unit: 'SECONDS') {
input message: 'Do you want to restart?', ok: 'Restart'
}
} catch(e) {
restart = false
throw err
}
}
}
}
}

View File

@ -1,4 +1,8 @@
def call(Map parameters = [:], body) {
import groovy.transform.Field
@Field STEP_NAME = 'pipelineStashFiles'
void call(Map parameters = [:], body) {
handlePipelineStepErrors (stepName: 'pipelineStashFiles', stepParameters: parameters) {
pipelineStashFilesBeforeBuild(parameters)
body() //execute build

View File

@ -6,7 +6,7 @@ import groovy.transform.Field
@Field Set STEP_CONFIG_KEYS = ['runCheckmarx', 'stashIncludes', 'stashExcludes']
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, stepNameDoc: 'stashFiles') {
def utils = parameters.juStabUtils
@ -20,8 +20,8 @@ def call(Map parameters = [:]) {
//additional includes via passing e.g. stashIncludes: [opa5: '**/*.include']
//additional excludes via passing e.g. stashExcludes: [opa5: '**/*.exclude']
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -6,7 +6,7 @@ import groovy.transform.Field
@Field Set STEP_CONFIG_KEYS = ['runOpaTests', 'stashIncludes', 'stashExcludes']
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, stepNameDoc: 'stashFiles') {
@ -22,8 +22,8 @@ def call(Map parameters = [:]) {
//additional includes via passing e.g. stashIncludes: [opa5: '**/*.include']
//additional excludes via passing e.g. stashExcludes: [opa5: '**/*.exclude']
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -1,7 +1,11 @@
import com.sap.piper.DefaultValueCache
import com.sap.piper.MapUtils
def call(Map parameters = [:]) {
import groovy.transform.Field
@Field STEP_NAME = 'prepareDefaultValues'
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: 'prepareDefaultValues', stepParameters: parameters) {
if(!DefaultValueCache.getInstance() || parameters.customDefaults) {
def defaultValues = [:]

View File

@ -0,0 +1,71 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import com.sap.piper.k8s.ContainerMap
import groovy.transform.Field
import groovy.text.SimpleTemplateEngine
@Field String STEP_NAME = 'seleniumExecuteTests'
@Field Set STEP_CONFIG_KEYS = [
'buildTool', //defines the tool which is used for executing the tests
'containerPortMappings', //port mappings required for containers. This will only take effect inside a Kubernetes pod, format [[containerPort: 1111, hostPort: 1111]]
'dockerEnvVars', //envVars to be set in the execution container if required
'dockerImage', //Docker image for code execution
'dockerName', //name of the Docker container. This will only take effect inside a Kubernetes pod.
'dockerWorkspace', //user home directory for Docker execution. This will only take effect inside a Kubernetes pod.
'failOnError',
'gitBranch', //only if testRepository is used: branch of testRepository. Default is master
'gitSshKeyCredentialsId', //only if testRepository is used: ssh credentials id in case a protected testRepository is used
'sidecarEnvVars', //envVars to be set in Selenium container if required
'sidecarImage', //image for Selenium execution which runs as sidecar to dockerImage
'sidecarName', //name of the Selenium container. If not on Kubernetes pod, this will define the name of the link to the Selenium container and is thus required for accessing the server, example http://selenium:4444 (default)
'sidecarVolumeBind', //volume bind. This will not take effect in Kubernetes pod.
'stashContent', //list of stash names which are required to be unstashed before test run
'testRepository' //if tests are in a separate repository, git url can be defined. For protected repositories the git ssh url is required
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:], Closure body) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def utils = parameters?.juStabUtils ?: new Utils()
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.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)
.dependingOn('buildTool').mixin('dockerImage')
.dependingOn('buildTool').mixin('dockerName')
.dependingOn('buildTool').mixin('dockerWorkspace')
.use()
utils.pushToSWA([step: STEP_NAME], config)
dockerExecute(
script: script,
containerPortMappings: config.containerPortMappings,
dockerEnvVars: config.dockerEnvVars,
dockerImage: config.dockerImage,
dockerName: config.dockerName,
dockerWorkspace: config.dockerWorkspace,
sidecarEnvVars: config.sidecarEnvVars,
sidecarImage: config.sidecarImage,
sidecarName: config.sidecarName,
sidecarVolumeBind: config.sidecarVolumeBind
) {
try {
config.stashContent = config.testRepository
?[GitUtils.handleTestRepository(this, config)]
:utils.unstashAll(config.stashContent)
body()
} catch (err) {
if (config.failOnError) {
throw err
}
}
}
}
}

View File

@ -5,7 +5,7 @@ import groovy.transform.Field
@Field String STEP_NAME = 'setupCommonPipelineEnvironment'
@Field Set GENERAL_CONFIG_KEYS = ['collectTelemetryData']
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -17,12 +17,14 @@ def call(Map parameters = [:]) {
loadConfigurationFromFile(script, configFile)
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.use()
new Utils().pushToSWA([step: STEP_NAME, stepParam4: parameters.customDefaults?'true':'false'], config)
(parameters.utils ?: new Utils())
.pushToSWA([step: STEP_NAME, stepParam4: parameters.customDefaults?'true':'false',
stepParam5: Boolean.toString( ! (script?.commonPipelineEnvironment?.getConfigProperties() ?: [:]).isEmpty())], config)
}
}

View File

@ -19,13 +19,13 @@ import groovy.transform.Field
])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def utils = parameters.juStabUtils ?: new Utils()
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -1,7 +1,6 @@
import com.cloudbees.groovy.cps.NonCPS
import com.sap.piper.ConfigurationHelper
import com.sap.piper.ConfigurationMerger
import com.sap.piper.MapUtils
import com.sap.piper.Utils
import groovy.transform.Field
@ -20,7 +19,7 @@ import groovy.transform.Field
* @param script global script environment of the Jenkinsfile run
* @param others document all parameters
*/
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script
if (script == null)
@ -28,8 +27,8 @@ def call(Map parameters = [:]) {
prepare(parameters)
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS)
@ -123,7 +122,7 @@ def publishJMeterReport(Map settings = [:]){
}
}
def touchFiles(){
void touchFiles(pattern){
echo "[${STEP_NAME}] update test results"
def patternArray = pattern.split(',')
for(def i = 0; i < patternArray.length; i++){

View File

@ -3,10 +3,13 @@ import com.sap.piper.Version
import com.sap.piper.tools.JavaArchiveDescriptor
import com.sap.piper.tools.ToolDescriptor
import groovy.transform.Field
import hudson.AbortException
@Field STEP_NAME = 'toolValidate'
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: 'toolValidate', stepParameters: parameters) {

View File

@ -1,9 +1,7 @@
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import groovy.transform.Field
import com.sap.piper.ConfigurationHelper
import com.sap.piper.ConfigurationMerger
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
@ -28,14 +26,16 @@ import hudson.AbortException
def call(parameters = [:]) {
def transportRequestId
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)
@ -97,8 +97,6 @@ def call(parameters = [:]) {
configuration = configHelper.use()
def transportRequestId
def creatingMessage = ["[INFO] Creating transport request"]
if(backendType == BackendType.SOLMAN) {
creatingMessage << " for change document '${configuration.changeDocumentId}' and development system '${configuration.developmentSystemId}'"
@ -131,6 +129,7 @@ def call(parameters = [:]) {
echo "[INFO] Transport Request '$transportRequestId' has been successfully created."
return transportRequestId
}
return transportRequestId
}

View File

@ -1,9 +1,7 @@
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import groovy.transform.Field
import com.sap.piper.ConfigurationHelper
import com.sap.piper.ConfigurationMerger
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
@ -25,7 +23,7 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati
@Field Set generalConfigurationKeys = stepConfigurationKeys
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -33,8 +31,8 @@ def call(parameters = [:]) {
ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)

View File

@ -1,9 +1,7 @@
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import groovy.transform.Field
import com.sap.piper.ConfigurationHelper
import com.sap.piper.ConfigurationMerger
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagementException
@ -27,7 +25,7 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati
'filePath',
'transportRequestId'])
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -35,8 +33,8 @@ def call(parameters = [:]) {
ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)