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:
commit
a2393c3f92
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.idea/
|
||||
bin/
|
||||
.settings
|
||||
logs
|
||||
reports
|
||||
|
BIN
documentation/docs/images/k8s_env.png
Normal file
BIN
documentation/docs/images/k8s_env.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
@ -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'
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
@ -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
|
||||
'''
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -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_
|
||||
|
||||

|
||||
|
||||
## 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"
|
||||
}
|
||||
```
|
||||
|
102
documentation/docs/steps/gaugeExecuteTests.md
Normal file
102
documentation/docs/steps/gaugeExecuteTests.md
Normal 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|
|
62
documentation/docs/steps/healthExecuteCheck.md
Normal file
62
documentation/docs/steps/healthExecuteCheck.md
Normal 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|
|
96
documentation/docs/steps/mailSendNotification.md
Normal file
96
documentation/docs/steps/mailSendNotification.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
75
documentation/docs/steps/pipelineRestartSteps.md
Normal file
75
documentation/docs/steps/pipelineRestartSteps.md
Normal 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
|
||||
|
143
documentation/docs/steps/seleniumExecuteTests.md
Normal file
143
documentation/docs/steps/seleniumExecuteTests.md
Normal 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
|
@ -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
|
||||
|
2
pom.xml
2
pom.xml
@ -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>
|
||||
|
6
resources/com.sap.piper/templates/mailFailure.html
Normal file
6
resources/com.sap.piper/templates/mailFailure.html
Normal 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>
|
3
resources/com.sap.piper/templates/mailRecover.html
Normal file
3
resources/com.sap.piper/templates/mailRecover.html
Normal 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>
|
@ -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'
|
||||
|
26
src/com/sap/piper/CfManifestUtils.groovy
Normal file
26
src/com/sap/piper/CfManifestUtils.groovy
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.sap.piper
|
||||
|
||||
import com.cloudbees.groovy.cps.NonCPS
|
||||
import com.sap.piper.MapUtils
|
||||
|
||||
class ConfigurationMerger {
|
||||
@NonCPS
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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.')
|
||||
|
||||
|
107
test/groovy/CommonStepsTest.groovy
Normal file
107
test/groovy/CommonStepsTest.groovy
Normal 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
|
||||
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
137
test/groovy/GaugeExecuteTestsTest.groovy
Normal file
137
test/groovy/GaugeExecuteTestsTest.groovy
Normal 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()
|
||||
}
|
||||
}
|
88
test/groovy/HealthExecuteCheckTest.groovy
Normal file
88
test/groovy/HealthExecuteCheckTest.groovy
Normal 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"))
|
||||
}
|
||||
|
||||
}
|
240
test/groovy/MailSendNotificationTest.groovy
Normal file
240
test/groovy/MailSendNotificationTest.groovy
Normal 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()
|
||||
}
|
||||
}
|
126
test/groovy/PipelineRestartStepsTest.groovy
Normal file
126
test/groovy/PipelineRestartStepsTest.groovy
Normal 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'))
|
||||
}
|
||||
}
|
114
test/groovy/SeleniumExecuteTestsTest.groovy
Normal file
114
test/groovy/SeleniumExecuteTestsTest.groovy
Normal 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'))
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,6 @@ public class TransportRequestCreateTest extends BasePiperTest {
|
||||
result.developmentSystemId = developmentSystemId
|
||||
result.cmEndpoint = cmEndpoint
|
||||
result.credentialId = credentialId
|
||||
|
||||
result.clientOpts = clientOpts
|
||||
return '001'
|
||||
}
|
||||
|
30
test/groovy/com/sap/piper/CfManifestUtilsTest.groovy
Normal file
30
test/groovy/com/sap/piper/CfManifestUtilsTest.groovy
Normal 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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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-'))
|
||||
}
|
||||
}
|
||||
|
45
test/groovy/com/sap/piper/JenkinsUtilsTest.groovy
Normal file
45
test/groovy/com/sap/piper/JenkinsUtilsTest.groovy
Normal 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))
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -72,6 +72,7 @@ class JenkinsShellCallRule implements TestRule {
|
||||
}
|
||||
}
|
||||
if(result instanceof Closure) result = result()
|
||||
if (!result && m.returnStatus) result = 0
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
@ -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()
|
||||
|
@ -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 = []
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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."
|
||||
|
@ -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()) {
|
||||
|
@ -1,3 +1,7 @@
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field STEP_NAME = 'durationMeasure'
|
||||
|
||||
def call(Map parameters = [:], body) {
|
||||
|
||||
def script = parameters.script
|
||||
|
94
vars/gaugeExecuteTests.groovy
Normal file
94
vars/gaugeExecuteTests.groovy
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
48
vars/healthExecuteCheck.groovy
Normal file
48
vars/healthExecuteCheck.groovy
Normal 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()
|
||||
}
|
@ -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)
|
||||
|
194
vars/mailSendNotification.groovy
Normal file
194
vars/mailSendNotification.groovy
Normal 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'
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()) {
|
||||
|
@ -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() {
|
||||
|
||||
|
52
vars/pipelineRestartSteps.groovy
Normal file
52
vars/pipelineRestartSteps.groovy
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 = [:]
|
||||
|
71
vars/seleniumExecuteTests.groovy
Normal file
71
vars/seleniumExecuteTests.groovy
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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++){
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user