diff --git a/.gitignore b/.gitignore index e286faf6a..f26049779 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ target/ targets/ documentation/docs-gen -consumer-test/workspace +consumer-test/**/workspace diff --git a/.travis.yml b/.travis.yml index d5cae0e77..6a278ee59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ branches: only: - master + - /^it\/.*$/ language: groovy sudo: required services: @@ -35,8 +36,9 @@ jobs: cp -r documentation/docs documentation/docs-tmp documentation/bin/createDocu.sh vars documentation/docs-tmp/steps docker run --rm -it -v ${TRAVIS_BUILD_DIR}:/docs -w /docs/documentation squidfunk/mkdocs-material:3.0.4 build --clean --verbose --strict - - name: Consumer Tests for s4sdk pipeline - script: cd consumer-test && chmod +x runTests.sh && ./runTests.sh + - name: Consumer Tests + if: repo = "SAP/jenkins-library" && ( (type != pull_request && branch =~ /^master$|^it\/.*$/) || (type == pull_request && head_repo = "SAP/jenkins-library" && head_branch =~ /^it\/.*$/) ) + script: cd consumer-test && chmod +x integrationTestController.sh && ./integrationTestController.sh - stage: Docs name: Deploy diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 713d24ebf..000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,19 +0,0 @@ -node { - try { - lock(resource: "sap-jenkins-library/10", inversePrecedence: true) { - milestone 10 - deleteDir() - stage ('Checkout'){ - checkout scm - } - stage ('Test') { - sh "mvn clean test --batch-mode" - } - } - } catch (Throwable err) { - echo "Error occured: ${err}" - currentBuild.result = 'FAILURE' - mail subject: '[Build failed] SAP/jenkins-library', body: 'Fix the build.', to: 'marcus.holl@sap.com,oliver.nocon@sap.com' - throw err - } -} diff --git a/README.md b/README.md index 3167d9055..edcecd3df 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Register to our [google group][google-group] in order to get updates or for aski Read and understand our [contribution guidelines][piper-library-contribution] before opening a pull request. -# [License][piper-library-license] +# License Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted diff --git a/consumer-test/integrationTestController.sh b/consumer-test/integrationTestController.sh new file mode 100755 index 000000000..435284ae6 --- /dev/null +++ b/consumer-test/integrationTestController.sh @@ -0,0 +1,138 @@ +#!/bin/bash + +function fail() { + local message="$1" + local returnCode=${2:-1} + echo "[ERROR] ${message}" >&2 + exit "${returnCode}" +} + +function notify() { + + local state=${1} + local description=${2} + local hash=${3} + + echo "[INFO] Notifying about state \"${state}\" for commit \"${hash}\"." + + curl -X POST \ + --fail \ + --silent \ + --output /dev/null \ + --data "{\"state\": \"${state}\", \"target_url\": \"${TRAVIS_BUILD_WEB_URL}\", \"description\": \"${description}\", \"context\": \"integration-tests\"}" \ + --user "${INTEGRATION_TEST_VOTING_USER}:${INTEGRATION_TEST_VOTING_TOKEN}" \ + "https://api.github.com/repos/SAP/jenkins-library/statuses/${hash}" || fail "Cannot send notification. curl return code: $?" +} + +function cleanup() { + [ -z "${notificationThreadPid}" ] || kill -PIPE "${notificationThreadPid}" &>/dev/null +} + +trap cleanup EXIT + +# +# In case the build is performed for a pull request TRAVIS_COMMIT is a merge +# commit between the base branch and the PR branch HEAD. That commit is actually built. +# But for notifying about a build status we need the commit which is currenty +# the HEAD of the PR branch. +# +# In case the build is performed for a simple branch (not associated with a PR) +# In this case there is no merge commit between any base branch and HEAD of a PR branch. +# The commit which we need for notifying about a build status is in this case simply +# TRAVIS_COMMIT itself. +# +COMMIT_HASH_FOR_STATUS_NOTIFICATIONS="${TRAVIS_PULL_REQUEST_SHA}" +[ -z "${COMMIT_HASH_FOR_STATUS_NOTIFICATIONS}" ] && COMMIT_HASH_FOR_STATUS_NOTIFICATIONS="${TRAVIS_COMMIT}" + +notify "pending" "Integration tests in progress." "${COMMIT_HASH_FOR_STATUS_NOTIFICATIONS}" + +WORKSPACES_ROOT=workspaces +[ -e "${WORKSPACES_ROOT}" ] && rm -rf ${WORKSPACES_ROOT} + +TEST_CASES=$(find testCases -name '*.yml') + +# This auxiliar thread is needed in order to produce some output while the +# test are running. Otherwise the job will be canceled after 10 minutes without +# output. +while true; do sleep 10; echo "[INFO] Integration tests still running."; done & +notificationThreadPid=$! + +declare -a processes +i=0 +for f in ${TEST_CASES} +do + testCase=$(basename "${f%.*}") + area=$(dirname "${f#*/}") + echo "[INFO] Running test case \"${testCase}\" in area \"${area}\"." + TEST_CASE_ROOT="${WORKSPACES_ROOT}/${area}/${testCase}" + [ -e "${TEST_CASE_ROOT}" ] && rm -rf "${TEST_CASE_ROOT}" + mkdir -p "${TEST_CASE_ROOT}" || fail "Cannot create test case root directory for test case \"${testCase}\"." 1 + source ./runTest.sh "${testCase}" "${TEST_CASE_ROOT}" &> "${TEST_CASE_ROOT}/log.txt" & + pid=$! + processes[$i]="${area}/${testCase}:${pid}" + echo "[INFO] Test case \"${testCase}\" in area \"${area}\" launched. (PID: \"${pid}\")." + let i=i+1 +done + +[ "${i}" == 0 ] && fail "No tests has been executed." 1 + +# +# wait for the test cases and cat the log +for p in "${processes[@]}" +do + area=$(dirname "${p%:*}") + testCase=$(basename "${p%:*}") + processId="${p#*:}" + echo "[INFO] Waiting for test case \"${testCase}\" in area \"${area}\" (PID: \"${processId}\")." + wait "${processId}" + echo "[INFO] Test case \"${testCase}\" in area \"${area}\" finished (PID: \"${processId}\")." +done + +kill -PIPE "${notificationThreadPid}" &>/dev/null && notificationThreadPid="" + +# +# provide the logs +for p in "${processes[@]}" +do + area=$(dirname "${p%:*}") + testCase=$(basename "${p%:*}") + echo "[INFO] === START === Logs for test case \"${testCase}\" ===." + cat "${TEST_CASE_ROOT}/log.txt" + echo "[INFO] === END === Logs for test case \"${testCase}\" ===." +done + +# +# list test case status +echo "[INFO] Build status:" +failure="false" +for p in "${processes[@]}" +do + status="UNDEFINED" + area=$(dirname "${p%:*}") + testCase=$(basename "${p%:*}") + TEST_CASE_ROOT="${WORKSPACES_ROOT}/${area}/${testCase}" + if [ -f "${TEST_CASE_ROOT}/SUCCESS" ] + then + status="SUCCESS" + else + status="FAILURE" + failure="true" + fi + printf "[INFO] %-30s: %s\n" "${testCase}" "${status}" +done + +STATUS_DESCRIPTION="The integration tests failed." +STATUS_STATE="failure" + +if [ "${failure}" == "false" ] +then + STATUS_DESCRIPTION="The integration tests succeeded." + STATUS_STATE="success" +fi + +notify "${STATUS_STATE}" "${STATUS_DESCRIPTION}" "${COMMIT_HASH_FOR_STATUS_NOTIFICATIONS}" + +[ "${failure}" != "false" ] && fail "Integration tests failed." 1 + +echo "[INFO] Integration tests succeeded." +exit 0 diff --git a/consumer-test/runTest.sh b/consumer-test/runTest.sh new file mode 100755 index 000000000..db93f2479 --- /dev/null +++ b/consumer-test/runTest.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +TEST_CASE=$1 +TEST_CASE_ROOT=$2 +TEST_CASE_WORKSPACE="${TEST_CASE_ROOT}/workspace" + +LIBRARY_VERSION_UNDER_TEST=$(git log --format="%H" -n 1) +REPOSITORY_UNDER_TEST=${TRAVIS_REPO_SLUG:-SAP/jenkins-library} + +git clone -b "${TEST_CASE}" https://github.com/sap/cloud-s4-sdk-book "${TEST_CASE_WORKSPACE}" +cp -f jenkins.yml "${TEST_CASE_WORKSPACE}" +cd "${TEST_CASE_WORKSPACE}" || exit 1 + +# Configure path to library-repository under test in Jenkins config +sed -i -e "s:__REPO_SLUG__:${REPOSITORY_UNDER_TEST}:g" jenkins.yml + +# Force usage of library version under test by setting it in the Jenkinsfile which is then the first definition and thus has the highest precedence +echo "@Library(\"piper-library-os@$LIBRARY_VERSION_UNDER_TEST\") _" | cat - Jenkinsfile > temp && mv temp Jenkinsfile + +# Commit the changed version because artifactSetVersion expects the git repo not to be dirty +git commit --all --author="piper-testing-bot " --message="Set piper lib version for test" + +docker run -v /var/run/docker.sock:/var/run/docker.sock -v "${PWD}":/workspace -v /tmp -e CASC_JENKINS_CONFIG=/workspace/jenkins.yml \ + -e CX_INFRA_IT_CF_USERNAME -e CX_INFRA_IT_CF_PASSWORD -e BRANCH_NAME="${TEST_CASE}" ppiper/jenkinsfile-runner + +RC=$? + +cd - &> /dev/null || { echo "[ERROR] change directory back into integration test root folder failed."; exit 1; } + +[ "${RC}" == 0 ] && touch "${TEST_CASE_ROOT}/SUCCESS" diff --git a/consumer-test/runTests.sh b/consumer-test/runTests.sh deleted file mode 100755 index 9a3095f20..000000000 --- a/consumer-test/runTests.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -e - -LIBRARY_VERSION_UNDER_TEST=$(git log --format="%H" -n 1) -REPOSITORY_UNDER_TEST=${TRAVIS_REPO_SLUG:-SAP/jenkins-library} - -rm -rf workspace -git clone -b consumer-test https://github.com/sap/cloud-s4-sdk-book workspace -cp -f jenkins.yml workspace -cd workspace -sed -i -e "s:__REPO_SLUG__:${REPOSITORY_UNDER_TEST}:g" jenkins.yml -echo "@Library(\"piper-library-os@$LIBRARY_VERSION_UNDER_TEST\") _" | cat - Jenkinsfile > temp && mv temp Jenkinsfile -git commit --all --author="piper-testing-bot " --message="Set piper lib version for test" - -docker run -v /var/run/docker.sock:/var/run/docker.sock -v "${PWD}":/workspace -v /tmp -e CASC_JENKINS_CONFIG=/workspace/jenkins.yml -e CX_INFRA_IT_CF_USERNAME -e CX_INFRA_IT_CF_PASSWORD -e BRANCH_NAME=consumer-test ppiper/jenkinsfile-runner diff --git a/consumer-test/testCases/s4sdk/consumer-test-neo.yml b/consumer-test/testCases/s4sdk/consumer-test-neo.yml new file mode 100644 index 000000000..23b65d3b1 --- /dev/null +++ b/consumer-test/testCases/s4sdk/consumer-test-neo.yml @@ -0,0 +1,2 @@ +# Empty for the moment. +# Might contain test configuration in the future. diff --git a/consumer-test/testCases/s4sdk/consumer-test.yml b/consumer-test/testCases/s4sdk/consumer-test.yml new file mode 100644 index 000000000..23b65d3b1 --- /dev/null +++ b/consumer-test/testCases/s4sdk/consumer-test.yml @@ -0,0 +1,2 @@ +# Empty for the moment. +# Might contain test configuration in the future. diff --git a/documentation/docs/steps/checkChangeInDevelopment.md b/documentation/docs/steps/checkChangeInDevelopment.md index ecf035b39..b164dce27 100644 --- a/documentation/docs/steps/checkChangeInDevelopment.md +++ b/documentation/docs/steps/checkChangeInDevelopment.md @@ -2,7 +2,7 @@ ## Description -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Prerequisites @@ -10,11 +10,11 @@ Content here is generated from corresponnding step, see `vars`. ## Parameters -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Step configuration -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Exceptions diff --git a/documentation/docs/steps/dockerExecute.md b/documentation/docs/steps/dockerExecute.md index 5b4eaa1e9..bb327eb1e 100644 --- a/documentation/docs/steps/dockerExecute.md +++ b/documentation/docs/steps/dockerExecute.md @@ -2,11 +2,11 @@ ## Description -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Parameters -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Kubernetes support @@ -14,7 +14,7 @@ If the Jenkins is setup on a Kubernetes cluster, then you can execute the closur ## Step configuration -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Side effects diff --git a/documentation/docs/steps/dockerExecuteOnKubernetes.md b/documentation/docs/steps/dockerExecuteOnKubernetes.md index 22ddbbbfb..013ce042a 100644 --- a/documentation/docs/steps/dockerExecuteOnKubernetes.md +++ b/documentation/docs/steps/dockerExecuteOnKubernetes.md @@ -2,7 +2,7 @@ ## Description -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Prerequisites @@ -13,11 +13,11 @@ Content here is generated from corresponnding step, see `vars`. ## Parameters -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Step configuration -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Side effects diff --git a/documentation/docs/steps/karmaExecuteTests.md b/documentation/docs/steps/karmaExecuteTests.md index 11015922a..ba7b16a55 100644 --- a/documentation/docs/steps/karmaExecuteTests.md +++ b/documentation/docs/steps/karmaExecuteTests.md @@ -2,82 +2,20 @@ ## Description -In this step the ([Karma test runner](http://karma-runner.github.io)) is executed. - -The step is using the `seleniumExecuteTest` step to spins up two containers in a Docker network: - -- a Selenium/Chrome container (`selenium/standalone-chrome`) -- a NodeJS container (`node:8-stretch`) - -In the Docker network, the containers can be referenced by the values provided in `dockerName` and `sidecarName`, the default values are `karma` and `selenium`. These values must be used in the `hostname` properties of the test configuration ([Karma](https://karma-runner.github.io/1.0/config/configuration-file.html) and [WebDriver](https://github.com/karma-runner/karma-webdriver-launcher#usage)). - -!!! note - In a Kubernetes environment, the containers both need to be referenced with `localhost`. +Content here is generated from corresponnding step, see `vars`. ## Prerequisites -- **running Karma tests** - have a NPM module with running tests executed with Karma -- **configured WebDriver** - have the [`karma-webdriver-launcher`](https://github.com/karma-runner/karma-webdriver-launcher) package installed and a custom, WebDriver-based browser configured in Karma +* **running Karma tests** - have a NPM module with running tests executed with Karma +* **configured WebDriver** - have the [`karma-webdriver-launcher`](https://github.com/karma-runner/karma-webdriver-launcher) package installed and a custom, WebDriver-based browser configured in Karma ## Parameters -| parameter | mandatory | default | possible values | -| ----------|-----------|---------|-----------------| -|script|yes||| -|containerPortMappings|no|`[node:8-stretch: [[containerPort: 9876, hostPort: 9876]]]`|| -|dockerEnvVars|no|`[ NO_PROXY: 'localhost,karma,$NO_PROXY', no_proxy: 'localhost,karma,$no_proxy']`|| -|dockerImage|no|`node:8-stretch`|| -|dockerName|no|`karma`|| -|dockerWorkspace|no|`/home/node`|| -|failOnError|no||| -|installCommand|no|`npm install --quiet`|| -|modules|no|`['.']`|| -|runCommand|no|`npm run karma`|| -|sidecarEnvVars|no|`[ NO_PROXY: 'localhost,selenium,$NO_PROXY', no_proxy: 'localhost,selenium,$no_proxy']`|| -|sidecarImage|no||| -|sidecarName|no||| -|sidecarVolumeBind|no||| -|stashContent|no|`['buildDescriptor', 'tests']`|| - -- `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. -- `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` - see step [seleniumExecuteTests](seleniumExecuteTests.md) -- `installCommand` - the command that is executed to install dependencies -- `modules` - define the paths of the modules to execute tests on -- `runCommand` - the command that is executed to start the tests -- `sidecarEnvVars` - see step [dockerExecute](dockerExecute.md) -- `sidecarImage` - see step [dockerExecute](dockerExecute.md) -- `sidecarName` - see step [dockerExecute](dockerExecute.md) -- `sidecarVolumeBind` - see step [dockerExecute](dockerExecute.md) -- `stashContent` - pass specific stashed that should be considered for the tests +Content here is generated from corresponnding step, see `vars`. ## Step configuration -We recommend to define values of step parameters via [config.yml file](../configuration.md). - -In following sections the configuration is possible: - -| parameter | general | step | stage | -| ----------|---------|------|-------| -|script|||| -|containerPortMappings|X|X|X| -|dockerEnvVars|X|X|X| -|dockerImage|X|X|X| -|dockerName|X|X|X| -|dockerWorkspace|X|X|X| -|failOnError|X|X|X| -|installCommand|X|X|X| -|modules|X|X|X| -|runCommand|X|X|X| -|sidecarEnvVars|X|X|X| -|sidecarImage|X|X|X| -|sidecarName|X|X|X| -|sidecarVolumeBind|X|X|X| -|stashContent|X|X|X| +Content here is generated from corresponnding step, see `vars`. ## Side effects diff --git a/documentation/docs/steps/npmExecute.md b/documentation/docs/steps/npmExecute.md new file mode 100644 index 000000000..619cb9cb1 --- /dev/null +++ b/documentation/docs/steps/npmExecute.md @@ -0,0 +1,23 @@ +# npmExecute + +## Description + +Content here is generated from corresponding step, see `vars`. + +## Parameters + +Content here is generated from corresponding step, see `vars`. + +## Step configuration + +Content here is generated from corresponding step, see `vars`. + +## Exceptions + +None + +## Examples + +```groovy +npmExecute script: this, dockerImage: 'node:8-stretch', npmCommand: 'run build' +``` diff --git a/documentation/docs/steps/uiVeri5ExecuteTests.md b/documentation/docs/steps/uiVeri5ExecuteTests.md index 09816bffa..25fe24fa8 100644 --- a/documentation/docs/steps/uiVeri5ExecuteTests.md +++ b/documentation/docs/steps/uiVeri5ExecuteTests.md @@ -2,18 +2,21 @@ ## Description -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Prerequisites ## Parameters -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Step configuration -Content here is generated from corresponnding step, see `vars`. +Content here is generated from corresponding step, see `vars`. ## Exceptions +If you see an error like `fatal: Not a git repository (or any parent up to mount point /home/jenkins)` it is likely that your test description cannot be found.
+Please make sure to point parameter `testOptions` to your `conf.js` file like `testOptions: './path/to/my/tests/conf.js'` + ## Examples diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index f766e9799..84af48355 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -24,6 +24,7 @@ nav: - mtaBuild: steps/mtaBuild.md - neoDeploy: steps/neoDeploy.md - newmanExecute: steps/newmanExecute.md + - npmExecute: steps/npmExecute.md - pipelineExecute: steps/pipelineExecute.md - pipelineRestartSteps: steps/pipelineRestartSteps.md - pipelineStashFiles: steps/pipelineStashFiles.md diff --git a/resources/default_pipeline_environment.yml b/resources/default_pipeline_environment.yml index 030d791b0..ca57ffeff 100644 --- a/resources/default_pipeline_environment.yml +++ b/resources/default_pipeline_environment.yml @@ -16,6 +16,12 @@ general: from: 'origin/master' to: 'HEAD' format: '%b' + rfc: + docker: + image: 'rfc' + options: [] + envVars: {} + pullImage: true githubApiUrl: 'https://api.github.com' githubServerUrl: 'https://github.com' gitSshKeyCredentialsId: '' #needed to allow sshagent to run with local ssh key @@ -252,6 +258,8 @@ 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' + npmExecute: + dockerImage: 'node:8-stretch' pipelineRestartSteps: sendMail: true timeoutInSeconds: 900 @@ -361,8 +369,14 @@ steps: failIfStatusIsNotInDevelopment: true transportRequestCreate: developmentSystemId: null + verbose: false transportRequestUploadFile: + acceptUnixStyleLineEndings: true + codePage: 'UTF-8' + failOnWarning: true + verbose: false transportRequestRelease: + verbose: false uiVeri5ExecuteTests: failOnError: false dockerEnvVars: {} diff --git a/src/com/sap/piper/Utils.groovy b/src/com/sap/piper/Utils.groovy index 71b5161d1..8de84be15 100644 --- a/src/com/sap/piper/Utils.groovy +++ b/src/com/sap/piper/Utils.groovy @@ -2,6 +2,7 @@ package com.sap.piper import com.cloudbees.groovy.cps.NonCPS import com.sap.piper.analytics.Telemetry +import groovy.text.SimpleTemplateEngine import java.nio.charset.StandardCharsets import java.security.MessageDigest @@ -98,3 +99,10 @@ void pushToSWA(Map parameters, Map config) { // some error occured in telemetry reporting. This should not break anything though. } } + +@NonCPS +static String fillTemplate(String templateText, Map binding){ + def engine = new SimpleTemplateEngine() + String result = engine.createTemplate(templateText).make(binding) + return result +} diff --git a/src/com/sap/piper/cm/BackendType.groovy b/src/com/sap/piper/cm/BackendType.groovy index d3a9dacae..1ab4ccb3b 100644 --- a/src/com/sap/piper/cm/BackendType.groovy +++ b/src/com/sap/piper/cm/BackendType.groovy @@ -1,5 +1,5 @@ package com.sap.piper.cm; public enum BackendType { - SOLMAN, CTS, NONE + SOLMAN, CTS, RFC, NONE } diff --git a/src/com/sap/piper/cm/ChangeManagement.groovy b/src/com/sap/piper/cm/ChangeManagement.groovy index fa3c2332d..97c41c396 100644 --- a/src/com/sap/piper/cm/ChangeManagement.groovy +++ b/src/com/sap/piper/cm/ChangeManagement.groovy @@ -2,6 +2,7 @@ package com.sap.piper.cm import com.sap.piper.GitUtils +import groovy.json.JsonSlurper import hudson.AbortException @@ -63,7 +64,7 @@ public class ChangeManagement implements Serializable { } boolean isChangeInDevelopment(String changeId, String endpoint, String credentialsId, String clientOpts = '') { - int rc = executeWithCredentials(BackendType.SOLMAN, endpoint, credentialsId, 'is-change-in-development', ['-cID', "'${changeId}'", '--return-code'], + int rc = executeWithCredentials(BackendType.SOLMAN, [:], endpoint, credentialsId, 'is-change-in-development', ['-cID', "'${changeId}'", '--return-code'], false, clientOpts) as int @@ -78,7 +79,7 @@ public class ChangeManagement implements Serializable { String createTransportRequestCTS(String transportType, String targetSystemId, String description, String endpoint, String credentialsId, String clientOpts = '') { try { - def transportRequest = executeWithCredentials(BackendType.CTS, endpoint, credentialsId, 'create-transport', + def transportRequest = executeWithCredentials(BackendType.CTS, [:], endpoint, credentialsId, 'create-transport', ['-tt', transportType, '-ts', targetSystemId, '-d', "\"${description}\""], true, clientOpts) @@ -91,7 +92,7 @@ public class ChangeManagement implements Serializable { String createTransportRequestSOLMAN(String changeId, String developmentSystemId, String endpoint, String credentialsId, String clientOpts = '') { try { - def transportRequest = executeWithCredentials(BackendType.SOLMAN, endpoint, credentialsId, 'create-transport', ['-cID', changeId, '-dID', developmentSystemId], + def transportRequest = executeWithCredentials(BackendType.SOLMAN, [:], endpoint, credentialsId, 'create-transport', ['-cID', changeId, '-dID', developmentSystemId], true, clientOpts) return (transportRequest as String)?.trim() @@ -100,86 +101,311 @@ public class ChangeManagement implements Serializable { } } - void uploadFileToTransportRequest(BackendType type, String changeId, String transportRequestId, String applicationId, String filePath, String endpoint, String credentialsId, String cmclientOpts = '') { + String createTransportRequestRFC( + Map docker, + String endpoint, + String developmentInstance, + String developmentClient, + String credentialsId, + String description, + boolean verbose) { - def args = null + def command = 'cts createTransportRequest' + def args = [ + TRANSPORT_DESCRIPTION: description, + ABAP_DEVELOPMENT_INSTANCE: developmentInstance, + ABAP_DEVELOPMENT_CLIENT: developmentClient, + VERBOSE: verbose, + ] - if(type == BackendType.SOLMAN) { - args = ['-cID', changeId, - '-tID', transportRequestId, - applicationId, "\"$filePath\""] - } else if (type == BackendType.CTS) { - args = ['-tID', transportRequestId, - "\"$filePath\""] - } else { - throw new IllegalArgumentException("Invalid backend type: ${type}") + try { + + def transportRequestId = executeWithCredentials( + BackendType.RFC, + docker, + endpoint, + credentialsId, + command, + args, + true) + + return new JsonSlurper().parseText(transportRequestId).REQUESTID + + } catch(AbortException ex) { + throw new ChangeManagementException( + "Cannot create transport request: ${ex.getMessage()}", ex) } - - int rc = executeWithCredentials(type, - endpoint, - credentialsId, - 'upload-file-to-transport', - args, - false, - cmclientOpts) as int - - if(rc == 0) { - return - } else { - throw new ChangeManagementException("Cannot upload file '$filePath' for change document '$changeId' with transport request '$transportRequestId'. Return code from cmclient: $rc.") - } - } - def executeWithCredentials(BackendType type, String endpoint, String credentialsId, String command, List args, boolean returnStdout = false, String clientOpts = '') { + void uploadFileToTransportRequestSOLMAN( + String changeId, + String transportRequestId, + String applicationId, + String filePath, + String endpoint, + String credentialsId, + String cmclientOpts = '') { + + def args = [ + '-cID', changeId, + '-tID', transportRequestId, + applicationId, "\"$filePath\"" + ] + + int rc = executeWithCredentials( + BackendType.SOLMAN, + [:], + endpoint, + credentialsId, + 'upload-file-to-transport', + args, + false, + cmclientOpts) as int + + if(rc != 0) { + throw new ChangeManagementException( + "Cannot upload file into transport request. Return code from cm client: $rc.") + } + } + + void uploadFileToTransportRequestCTS( + String transportRequestId, + String filePath, + String endpoint, + String credentialsId, + String cmclientOpts = '') { + + def args = [ + '-tID', transportRequestId, + "\"$filePath\"" + ] + + int rc = executeWithCredentials( + BackendType.CTS, + [:], + endpoint, + credentialsId, + 'upload-file-to-transport', + args, + false, + cmclientOpts) as int + + if(rc != 0) { + throw new ChangeManagementException( + "Cannot upload file into transport request. Return code from cm client: $rc.") + } + } + + void uploadFileToTransportRequestRFC( + Map docker, + String transportRequestId, + String applicationName, + String filePath, + String endpoint, + String credentialsId, + String developmentInstance, + String developmentClient, + String applicationDescription, + String abapPackage, + String codePage, + boolean acceptUnixStyleEndOfLine, + boolean failOnWarning, + boolean verbose) { + + def args = [ + ABAP_DEVELOPMENT_INSTANCE: developmentInstance, + ABAP_DEVELOPMENT_CLIENT: developmentClient, + ABAP_APPLICATION_NAME: applicationName, + ABAP_APPLICATION_DESC: applicationDescription, + ABAP_PACKAGE: abapPackage, + ZIP_FILE_URL: filePath, + CODE_PAGE: codePage, + ABAP_ACCEPT_UNIX_STYLE_EOL: acceptUnixStyleEndOfLine ? 'X' : '-', + FAIL_UPLOAD_ON_WARNING: Boolean.toString(failOnWarning), + VERBOSE: Boolean.toString(verbose), + ] + + int rc = executeWithCredentials( + BackendType.RFC, + docker, + endpoint, + credentialsId, + "cts uploadToABAP:${transportRequestId}", + args, + false) as int + + if(rc != 0) { + throw new ChangeManagementException( + "Cannot upload file into transport request. Return code from rfc client: $rc.") + } + } + + def executeWithCredentials( + BackendType type, + Map docker, + String endpoint, + String credentialsId, + String command, + def args, + boolean returnStdout = false, + String clientOpts = '') { + + def script = this.script script.withCredentials([script.usernamePassword( credentialsId: credentialsId, passwordVariable: 'password', usernameVariable: 'username')]) { - def cmScript = getCMCommandLine(type, endpoint, script.username, script.password, - command, args, - clientOpts) Map shArgs = [:] + if(returnStdout) shArgs.put('returnStdout', true) else shArgs.put('returnStatus', true) - shArgs.put('script', cmScript) + def result = 1 - // user and password are masked by withCredentials - script.echo """[INFO] Executing command line: "${cmScript}".""" - return script.sh(shArgs) + switch(type) { + + case BackendType.RFC: + + if(! (args in Map)) { + throw new IllegalArgumentException("args expected as Map for backend types ${[BackendType.RFC]}") + } + + shArgs.script = command + + args = args.plus([ + ABAP_DEVELOPMENT_SERVER: endpoint, + ABAP_DEVELOPMENT_USER: script.username, + ABAP_DEVELOPMENT_PASSWORD: script.password, + ]) + + // user and password are masked by withCredentials + script.echo """[INFO] Executing command line: "${shArgs.script}".""" + + script.dockerExecute( + script: script, + dockerImage: docker.image, + dockerOptions: docker.options, + dockerEnvVars: (docker.envVars?:[:]).plus(args), + dockerPullImage: docker.pullImage) { + + result = script.sh(shArgs) + + } + + break + + case BackendType.SOLMAN: + case BackendType.CTS: + + if(! (args in Collection)) + throw new IllegalArgumentException("args expected as Collection for backend types ${[BackendType.SOLMAN, BackendType.CTS]}") + + shArgs.script = getCMCommandLine(type, endpoint, script.username, script.password, + command, args, + clientOpts) + + // user and password are masked by withCredentials + script.echo """[INFO] Executing command line: "${shArgs.script}".""" + + result = script.sh(shArgs) + + break + } + + return result } } - void releaseTransportRequest(BackendType type,String changeId, String transportRequestId, String endpoint, String credentialsId, String clientOpts = '') { + void releaseTransportRequestSOLMAN( + String changeId, + String transportRequestId, + String endpoint, + String credentialsId, + String clientOpts = '') { - def cmd - List args = [] + def cmd = 'release-transport' + def args = [ + '-cID', + changeId, + '-tID', + transportRequestId, + ] - if(type == BackendType.SOLMAN) { - cmd = 'release-transport' - args << '-cID' - args << changeId - } else if(type == BackendType.CTS) { - cmd = 'export-transport' - } else { - throw new IllegalStateException("Invalid backend type: '${type}'") - } + int rc = executeWithCredentials( + BackendType.SOLMAN, + [:], + endpoint, + credentialsId, + cmd, + args, + false, + clientOpts) as int - args << '-tID' - args << transportRequestId - - int rc = executeWithCredentials(type, endpoint, credentialsId, cmd, args, false, clientOpts) as int - if(rc == 0) { - return - } else { + if(rc != 0) { throw new ChangeManagementException("Cannot release Transport Request '$transportRequestId'. Return code from cmclient: $rc.") } } + void releaseTransportRequestCTS( + String transportRequestId, + String endpoint, + String credentialsId, + String clientOpts = '') { + + def cmd = 'export-transport' + def args = [ + '-tID', + transportRequestId, + ] + + int rc = executeWithCredentials( + BackendType.CTS, + [:], + endpoint, + credentialsId, + cmd, + args, + false) as int + + if(rc != 0) { + throw new ChangeManagementException("Cannot release Transport Request '$transportRequestId'. Return code from cmclient: $rc.") + } + } + + void releaseTransportRequestRFC( + Map docker, + String transportRequestId, + String endpoint, + String developmentInstance, + String developmentClient, + String credentialsId, + boolean verbose) { + + def cmd = "cts releaseTransport:${transportRequestId}" + def args = [ + ABAP_DEVELOPMENT_INSTANCE: developmentInstance, + ABAP_DEVELOPMENT_CLIENT: developmentClient, + VERBOSE: verbose, + ] + + int rc = executeWithCredentials( + BackendType.RFC, + docker, + endpoint, + credentialsId, + cmd, + args, + false) as int + + if(rc != 0) { + throw new ChangeManagementException("Cannot release Transport Request '$transportRequestId'. Return code from rfcclient: $rc.") + } + + } + String getCMCommandLine(BackendType type, String endpoint, String username, diff --git a/src/com/sap/piper/testutils/JenkinsController.groovy b/src/com/sap/piper/testutils/JenkinsController.groovy new file mode 100644 index 000000000..1e4d0476d --- /dev/null +++ b/src/com/sap/piper/testutils/JenkinsController.groovy @@ -0,0 +1,122 @@ +package com.sap.piper.jenkins + +import com.cloudbees.groovy.cps.NonCPS + +class JenkinsController implements Serializable { + def script + String jenkinsUrl + def timeout + + JenkinsController(script, String jenkinsUrl = "http://localhost:8080", timeout = 3600) { + this.script = script + this.jenkinsUrl = jenkinsUrl + this.timeout = timeout + } + + def waitForJenkinsStarted() { + def timeout = 120 + def timePerLoop = 5 + + for (int i = 0; i < timeout; i += timePerLoop) { + script.sleep timePerLoop + try { + if (retrieveJenkinsStatus() == 'NORMAL') { + return true + } + } catch (Exception e) { + script.echo "Could not retrieve status for Jenkins at ${jenkinsUrl}/api/json. Message: ${e.getMessage()}. Retrying..." + e.printStackTrace() + continue + } + return false + } + script.error("Timeout: Jenkins did not start within the expected time frame.") + } + + private retrieveJenkinsStatus() { + def apiUrl = "${jenkinsUrl}/api/json" + script.echo "Checking Jenkins Status" + def response = getTextFromUrl(apiUrl) + def result = script.readJSON text: response + return result.mode + } + + //Trigger scanning of the multi branch builds + def buildJob(String jobName) { + script.sh "curl -s -X POST ${jenkinsUrl}/job/${URLEncoder.encode(jobName, 'UTF-8')}/build" + } + + def waitForSuccess(String jobName, String branch) { + if (this.waitForJobStatus(jobName, branch, 'SUCCESS')) { + this.printConsoleText(jobName, branch) + script.echo "Build was successful" + } else { + this.printConsoleText(jobName, branch) + script.error("Build of ${jobName} ${branch} was not successfull") + } + } + + def getBuildUrl(String jobName, String branch) { + return "${jenkinsUrl}/job/${URLEncoder.encode(jobName, 'UTF-8')}/job/${URLEncoder.encode(branch, 'UTF-8')}/lastBuild/" + } + + def waitForJobStatus(String jobName, String branch, String status) { + def buildUrl = getBuildUrl(jobName, branch) + def timePerLoop = 10 + + for (int i = 0; i < timeout; i += timePerLoop) { + script.sleep timePerLoop + try { + script.echo "Checking Build Status of ${jobName} ${branch}" + def buildInformation = retrieveBuildInformation(jobName, branch) + + if (buildInformation.building) { + script.echo "Build is still in progress" + continue + } + if (buildInformation.result == status) { + return true + } + } catch (Exception e) { + script.echo "Could not retrieve status for ${buildUrl}. Message: ${e.getMessage()}. Retrying..." + continue + } + return false + } + script.error("Timeout: Build of job ${jobName}, branch ${branch} did not finish in the expected time frame.") + } + + def getConsoleText(String jobName, String branch) { + def consoleUrl = this.getBuildUrl(jobName, branch) + "/consoleText" + return getTextFromUrl(consoleUrl) + } + + def printConsoleText(String jobName, String branch) { + String consoleOutput = getConsoleText(jobName, branch) + + script.echo '***********************************************' + script.echo '** Begin Output of Example Application Build **' + script.echo '***********************************************' + + script.echo consoleOutput + + script.echo '*********************************************' + script.echo '** End Output of Example Application Build **' + script.echo '*********************************************' + } + + def retrieveBuildInformation(String jobName, String branch) { + def buildUrl = getBuildUrl(jobName, branch) + def url = "${buildUrl}/api/json" + script.echo "Checking Build Status of ${jobName} ${branch}" + script.echo "${jenkinsUrl}/job/${URLEncoder.encode(jobName, 'UTF-8')}/job/${URLEncoder.encode(branch, 'UTF-8')}/" + def response = getTextFromUrl(url) + def result = script.readJSON text: response + return result + } + + @NonCPS + private static String getTextFromUrl(url) { + return new URL(url).getText() + } +} diff --git a/src/com/sap/piper/tools/neo/NeoCommandHelper.groovy b/src/com/sap/piper/tools/neo/NeoCommandHelper.groovy index c58687cac..58ebcc7fb 100644 --- a/src/com/sap/piper/tools/neo/NeoCommandHelper.groovy +++ b/src/com/sap/piper/tools/neo/NeoCommandHelper.groovy @@ -9,25 +9,23 @@ class NeoCommandHelper { private Script step private DeployMode deployMode private Map deploymentConfiguration - private String pathToNeoExecutable private String user private String password private String source //Warning: Commands generated with this class can contain passwords and should only be used within the step withCredentials - NeoCommandHelper(Script step, DeployMode deployMode, Map deploymentConfiguration, String pathToNeoExecutable, + NeoCommandHelper(Script step, DeployMode deployMode, Map deploymentConfiguration, String user, String password, String source) { this.step = step this.deployMode = deployMode this.deploymentConfiguration = deploymentConfiguration - this.pathToNeoExecutable = pathToNeoExecutable this.user = user this.password = password this.source = source } private String prolog() { - return "\"${pathToNeoExecutable}\"" + return 'neo.sh' } String statusCommand() { diff --git a/test/groovy/FioriOnCloudPlatformPipelineTest.groovy b/test/groovy/FioriOnCloudPlatformPipelineTest.groovy index 09b81a195..8c695b2be 100644 --- a/test/groovy/FioriOnCloudPlatformPipelineTest.groovy +++ b/test/groovy/FioriOnCloudPlatformPipelineTest.groovy @@ -115,9 +115,11 @@ class FioriOnCloudPlatformPipelineTest extends BasePiperTest { .commonPipelineEnvironment .configuration = [steps: [neoDeploy: - [ host: 'hana.example.com', - account: 'myTestAccount', - ] + [neo: + [ host: 'hana.example.com', + account: 'myTestAccount', + ] + ] ] ] @@ -139,7 +141,7 @@ class FioriOnCloudPlatformPipelineTest extends BasePiperTest { // the neo deploy call: Assert.assertThat(shellRule.shell, new CommandLineMatcher() - .hasProlog("\"/opt/sap/neo/tools/neo.sh\" deploy-mta") + .hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('host', 'hana\\.example\\.com') .hasSingleQuotedOption('account', 'myTestAccount') .hasSingleQuotedOption('password', 'terceSpot') diff --git a/test/groovy/NeoDeployTest.groovy b/test/groovy/NeoDeployTest.groovy index a97d5ee6e..d4d93c7a5 100644 --- a/test/groovy/NeoDeployTest.groovy +++ b/test/groovy/NeoDeployTest.groovy @@ -84,109 +84,10 @@ class NeoDeployTest extends BasePiperTest { helper.registerAllowedMethod('dockerExecute', [Map, Closure], null) helper.registerAllowedMethod('fileExists', [String], { s -> return new File(workspacePath, s).exists() }) helper.registerAllowedMethod('pwd', [], { return workspacePath }) - mockShellCommands() nullScript.commonPipelineEnvironment.configuration = [steps: [neoDeploy: [neo: [host: 'test.deploy.host.com', account: 'trialuser123']]]] } - @Test - void straightForwardTestCompatibilityConfiguration(){ - shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.* status .*', 'Status: STARTED') - - nullScript.commonPipelineEnvironment.configuration = [ - steps: [ - neoDeploy: [ - host: 'test.deploy.host.com', - account: 'trialuser123', - neoCredentialsId: 'myCredentialsId' - ]]] - - stepRule.step.neoDeploy(script: nullScript, - archivePath: warArchiveName, - deployMode: 'warParams', - applicationName: 'testApp', - runtime: 'neo-javaee6-wp', - runtimeVersion: '2.125', - warAction: 'rolling-update', - vmSize: 'lite') - - Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" rolling-update") - .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') - .hasSingleQuotedOption('account', 'trialuser123') - .hasSingleQuotedOption('application', 'testApp') - .hasSingleQuotedOption('runtime', 'neo-javaee6-wp') - .hasSingleQuotedOption('runtime-version', '2\\.125') - .hasSingleQuotedOption('size', 'lite') - .hasSingleQuotedOption('user', 'anonymous') - .hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*') - .hasSingleQuotedOption('source', '.*\\.war')) - } - - @Test - void straightForwardTestConfigViaConfigProperties() { - - boolean buildStatusHasBeenSet = false - boolean notifyOldConfigFrameworkUsed = false - - nullScript.commonPipelineEnvironment.setConfigProperty('DEPLOY_HOST', 'test.deploy.host.com') - nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'trialuser123') - nullScript.commonPipelineEnvironment.configuration = [:] - - nullScript.currentBuild = [setResult: { buildStatusHasBeenSet = true }] - - def utils = new Utils() { - void pushToSWA(Map parameters, Map config) { - notifyOldConfigFrameworkUsed = parameters.stepParam4 - } - } - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName, - neo: [credentialsId: 'myCredentialsId'], - utils: utils - ) - - Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") - .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') - .hasSingleQuotedOption('account', 'trialuser123') - .hasOption('synchronous', '') - .hasSingleQuotedOption('user', 'anonymous') - .hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*') - .hasSingleQuotedOption('source', '.*')) - - assert !buildStatusHasBeenSet - assert notifyOldConfigFrameworkUsed - } - - @Test - void testConfigViaConfigPropertiesSetsBuildToUnstable() { - - def buildStatus = 'SUCCESS' - - nullScript.commonPipelineEnvironment.setConfigProperty('DEPLOY_HOST', 'test.deploy.host.com') - nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'trialuser123') - nullScript.commonPipelineEnvironment.configuration = [:] - - nullScript.currentBuild = [setResult: { r -> buildStatus = r }] - - System.setProperty('com.sap.piper.featureFlag.buildUnstableWhenOldConfigFrameworkIsUsedByNeoDeploy', - Boolean.TRUE.toString()) - - try { - stepRule.step.neoDeploy(script: nullScript, - source: archiveName, - neo:[credentialsId: 'myCredentialsId'], - utils: utils - ) - } finally { - System.clearProperty('com.sap.piper.featureFlag.buildUnstableWhenOldConfigFrameworkIsUsedByNeoDeploy') - } - - assert buildStatus == 'UNSTABLE' - } - @Test void straightForwardTestConfigViaConfiguration() { @@ -205,7 +106,7 @@ class NeoDeployTest extends BasePiperTest { ) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") + new CommandLineMatcher().hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') .hasSingleQuotedOption('account', 'trialuser123') .hasOption('synchronous', '') @@ -231,7 +132,7 @@ class NeoDeployTest extends BasePiperTest { ) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") + new CommandLineMatcher().hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('host', 'configuration-frwk\\.deploy\\.host\\.com') .hasSingleQuotedOption('account', 'configurationFrwkUser123') .hasOption('synchronous', '') @@ -246,7 +147,7 @@ class NeoDeployTest extends BasePiperTest { stepRule.step.neoDeploy(script: nullScript) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") + new CommandLineMatcher().hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('source', 'archive.mtar')) } @@ -257,7 +158,7 @@ class NeoDeployTest extends BasePiperTest { source: "archive.mtar") Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") + new CommandLineMatcher().hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('source', 'archive.mtar')) } @@ -282,7 +183,7 @@ class NeoDeployTest extends BasePiperTest { ) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") + new CommandLineMatcher().hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') .hasSingleQuotedOption('account', 'trialuser123') .hasOption('synchronous', '') @@ -292,69 +193,6 @@ class NeoDeployTest extends BasePiperTest { ) } - - @Test - void neoHomeNotSetTest() { - - mockHomeVariablesNotSet() - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName - ) - - assert shellRule.shell.find { c -> c.contains('"neo.sh" deploy-mta') } - assert loggingRule.log.contains('SAP Cloud Platform Console Client is on PATH.') - assert loggingRule.log.contains("Using SAP Cloud Platform Console Client 'neo.sh'.") - } - - - @Test - void neoHomeAsParameterTest() { - - mockHomeVariablesNotSet() - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName, - neo:[credentialsId: 'myCredentialsId'], - neoHome: '/param/neo' - ) - - assert shellRule.shell.find { c -> c = "\"/param/neo/tools/neo.sh\" deploy-mta" } - assert loggingRule.log.contains("SAP Cloud Platform Console Client home '/param/neo' retrieved from configuration.") - assert loggingRule.log.contains("Using SAP Cloud Platform Console Client '/param/neo/tools/neo.sh'.") - } - - - @Test - void neoHomeFromEnvironmentTest() { - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName - ) - - assert shellRule.shell.find { c -> c.contains("\"/opt/neo/tools/neo.sh\" deploy-mta") } - assert loggingRule.log.contains("SAP Cloud Platform Console Client home '/opt/neo' retrieved from environment.") - assert loggingRule.log.contains("Using SAP Cloud Platform Console Client '/opt/neo/tools/neo.sh'.") - } - - - @Test - void neoHomeFromCustomStepConfigurationTest() { - - mockHomeVariablesNotSet() - - nullScript.commonPipelineEnvironment.configuration = [steps: [neoDeploy: [neo: [host: 'test.deploy.host.com', account: 'trialuser123'], neoHome: '/config/neo']]] - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName - ) - - assert shellRule.shell.find { c -> c = "\"/config/neo/tools/neo.sh\" deploy-mta" } - assert loggingRule.log.contains("SAP Cloud Platform Console Client home '/config/neo' retrieved from configuration.") - assert loggingRule.log.contains("Using SAP Cloud Platform Console Client '/config/neo/tools/neo.sh'.") - } - - @Test void archiveNotProvidedTest() { @@ -392,7 +230,7 @@ class NeoDeployTest extends BasePiperTest { stepRule.step.neoDeploy(script: nullScript, source: archiveName, deployMode: 'mta') Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta") + new CommandLineMatcher().hasProlog("neo.sh deploy-mta") .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') .hasSingleQuotedOption('account', 'trialuser123') .hasOption('synchronous', '') @@ -417,7 +255,7 @@ class NeoDeployTest extends BasePiperTest { source: warArchiveName) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy") + new CommandLineMatcher().hasProlog("neo.sh deploy") .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') .hasSingleQuotedOption('account', 'trialuser123') .hasSingleQuotedOption('application', 'testApp') @@ -448,7 +286,7 @@ class NeoDeployTest extends BasePiperTest { ) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" rolling-update") + new CommandLineMatcher().hasProlog("neo.sh rolling-update") .hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com') .hasSingleQuotedOption('account', 'trialuser123') .hasSingleQuotedOption('application', 'testApp') @@ -478,7 +316,7 @@ class NeoDeployTest extends BasePiperTest { Assert.assertThat(shellRule.shell, new CommandLineMatcher() - .hasProlog("\"/opt/neo/tools/neo.sh\" deploy") + .hasProlog("neo.sh deploy") .hasSingleQuotedOption('application', 'testApp')) } @@ -541,7 +379,7 @@ class NeoDeployTest extends BasePiperTest { ) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy") + new CommandLineMatcher().hasProlog("neo.sh deploy") .hasArgument("config.properties") .hasSingleQuotedOption('user', 'defaultUser') .hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*') @@ -566,7 +404,7 @@ class NeoDeployTest extends BasePiperTest { ]) Assert.assertThat(shellRule.shell, - new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" rolling-update") + new CommandLineMatcher().hasProlog("neo.sh rolling-update") .hasArgument('config.properties') .hasSingleQuotedOption('user', 'defaultUser') .hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*') @@ -654,57 +492,4 @@ class NeoDeployTest extends BasePiperTest { size: 'lite' ]) } - - @Test - void deployHostProvidedAsDeprecatedParameterTest() { - - nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'configPropsUser123') - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName, - deployHost: "my.deploy.host.com" - ) - - assert loggingRule.log.contains("[WARNING][neoDeploy] Deprecated parameter 'deployHost' is used. This will not work anymore in future versions. Use parameter 'host' instead.") - } - - @Test - void deployAccountProvidedAsDeprecatedParameterTest() { - - nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'configPropsUser123') - - stepRule.step.neoDeploy(script: nullScript, - source: archiveName, - neo: [ - host: "my.deploy.host.com", - ], - deployAccount: "myAccount" - ) - - assert loggingRule.log.contains("Deprecated parameter 'deployAccount' is used. This will not work anymore in future versions. Use parameter 'account' instead.") - } - - private mockShellCommands() { - String javaVersion = '''openjdk version \"1.8.0_121\" - OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-1~bpo8+1-b13) - OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)''' - shellRule.setReturnValue(Type.REGEX, '.*java -version.*', javaVersion) - - String neoVersion = '''SAP Cloud Platform Console Client - SDK version : 3.39.10 - Runtime : neo-java-web''' - shellRule.setReturnValue(Type.REGEX, '.*neo.sh version.*', neoVersion) - - shellRule.setReturnValue(Type.REGEX, '.*JAVA_HOME.*', '/opt/java') - shellRule.setReturnValue(Type.REGEX, '.*NEO_HOME.*', '/opt/neo') - shellRule.setReturnValue(Type.REGEX, '.*which java.*', 0) - shellRule.setReturnValue(Type.REGEX, '.*which neo.*', 0) - } - - private mockHomeVariablesNotSet() { - shellRule.setReturnValue(Type.REGEX, '.*JAVA_HOME.*', '') - shellRule.setReturnValue(Type.REGEX, '.*NEO_HOME.*', '') - shellRule.setReturnValue(Type.REGEX, '.*which java.*', 0) - shellRule.setReturnValue(Type.REGEX, '.*which neo.*', 0) - } } diff --git a/test/groovy/NpmExecuteTest.groovy b/test/groovy/NpmExecuteTest.groovy new file mode 100644 index 000000000..610f23ad3 --- /dev/null +++ b/test/groovy/NpmExecuteTest.groovy @@ -0,0 +1,56 @@ +import static org.junit.Assert.assertEquals +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.BasePiperTest +import util.JenkinsDockerExecuteRule +import util.JenkinsReadYamlRule +import util.JenkinsShellCallRule +import util.JenkinsStepRule +import util.Rules + +class NpmExecuteTest extends BasePiperTest { + + private ExpectedException thrown = new ExpectedException().none() + private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this) + private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this) + private JenkinsStepRule stepRule = new JenkinsStepRule(this) + private JenkinsReadYamlRule yamlRule = new JenkinsReadYamlRule(this) + + @Rule + public RuleChain ruleChain = Rules + .getCommonRules(this) + .around(thrown) + .around(yamlRule) + .around(dockerExecuteRule) + .around(shellRule) + .around(stepRule) + + @Before + void init() { + helper.registerAllowedMethod 'fileExists', [String], { s -> s == 'package.json' } + } + + @Test + void testNpmExecute() { + stepRule.step.npmExecute(script: nullScript, dockerImage: 'node:8-stretch') + assertEquals 'node:8-stretch', dockerExecuteRule.dockerParams.dockerImage + } + + @Test + void testNpmExecuteWithClosure() { + stepRule.step.npmExecute(script: nullScript, dockerImage: 'node:8-stretch', npmCommand: 'run build') { } + assert shellRule.shell.find { c -> c.contains('npm run build') } + } + + @Test + void testNoPackageJson() { + helper.registerAllowedMethod 'fileExists', [String], { false } + thrown.expect AbortException + thrown.expectMessage '[npmExecute] package.json is not found.' + stepRule.step.npmExecute(script: nullScript, dockerImage: 'node:8-stretch', npmCommand: 'run build') + } +} diff --git a/test/groovy/TransportRequestCreateTest.groovy b/test/groovy/TransportRequestCreateTest.groovy index ad4b863e4..fad0ed08a 100644 --- a/test/groovy/TransportRequestCreateTest.groovy +++ b/test/groovy/TransportRequestCreateTest.groovy @@ -1,3 +1,10 @@ +import static org.hamcrest.Matchers.allOf +import static org.hamcrest.Matchers.containsString + +import java.util.Map + +import org.hamcrest.Matchers +import org.hamcrest.core.StringContains import org.junit.Before import org.junit.Rule import org.junit.Test @@ -54,7 +61,7 @@ public class TransportRequestCreateTest extends BasePiperTest { } @Test - public void changeIdNotProvidedTest() { + public void changeIdNotProvidedSOLANTest() { thrown.expect(IllegalArgumentException) thrown.expectMessage("Change document id not provided (parameter: 'changeDocumentId' or via commit history).") @@ -73,7 +80,7 @@ public class TransportRequestCreateTest extends BasePiperTest { } @Test - public void developmentSystemIdNotProvidedTest() { + public void developmentSystemIdNotProvidedSOLMANTest() { thrown.expect(IllegalArgumentException) thrown.expectMessage("ERROR - NO VALUE AVAILABLE FOR developmentSystemId") @@ -82,7 +89,7 @@ public class TransportRequestCreateTest extends BasePiperTest { } @Test - public void createTransportRequestFailureTest() { + public void createTransportRequestFailureSOLMANTest() { ChangeManagement cm = new ChangeManagement(nullScript) { @@ -154,8 +161,7 @@ public class TransportRequestCreateTest extends BasePiperTest { String description, String endpoint, String credentialsId, - String clientOpts -) { + String clientOpts) { result.transportType = transportType result.targetSystemId = targetSystemId result.description = description @@ -186,6 +192,120 @@ public class TransportRequestCreateTest extends BasePiperTest { assert loggingRule.log.contains("[INFO] Transport Request '001' has been successfully created.") } + @Test + public void createTransportRequestSuccessRFCTest() { + + def result = [:] + + ChangeManagement cm = new ChangeManagement(nullScript) { + + String createTransportRequestRFC( + Map docker, + String endpoint, + String developmentInstance, + String developmentClient, + String credentialsId, + String description, + boolean verbose) { + + result.docker = docker + result.endpoint = endpoint + result.developmentClient = developmentClient + result.developmentInstance= developmentInstance + result.credentialsId = credentialsId + result.description = description + result.verbose = verbose + + return '001' + } + } + + stepRule.step.transportRequestCreate( + script: nullScript, + changeManagement: [ + type: 'RFC', + rfc: [ + developmentInstance: '01', + developmentClient: '001', + ], + endpoint: 'https://example.org/rfc', + ], + developmentSystemId: '001', + description: '', + cmUtils: cm, + verbose: true) + + assert nullScript.commonPipelineEnvironment.getTransportRequestId() == '001' + assert result == [ + docker: [ + image: 'rfc', + options: [], + envVars: [:], + pullImage: true + ], + endpoint: 'https://example.org/rfc', + developmentClient: '001', + developmentInstance: '01', + credentialsId: 'CM', + description: '', + verbose: true + ] + + assert loggingRule.log.contains("[INFO] Creating transport request.") + assert loggingRule.log.contains("[INFO] Transport Request '001' has been successfully created.") + } + + @Test + public void createTransportRequestFailureRFCTest() { + + thrown.expect(AbortException) + thrown.expectMessage('upload failed') + + ChangeManagement cm = new ChangeManagement(nullScript) { + + String createTransportRequestRFC( + Map docker, + String endpoint, + String developmentClient, + String developmentInstance, + String credentialsId, + String description, + boolean verbose) { + + throw new ChangeManagementException('upload failed') + } + } + + stepRule.step.transportRequestCreate( + script: nullScript, + changeManagement: [ + type: 'RFC', + rfc: [ + developmentInstance: '01', + developmentClient: '001', + ], + endpoint: 'https://example.org/rfc', + ], + developmentSystemId: '001', + description: '', + cmUtils: cm) + } + + @Test + public void createTransportRequestSanityChecksRFCTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage(allOf( + containsString('changeManagement/rfc/developmentInstance'), + containsString('changeManagement/rfc/developmentClient'), + )) + stepRule.step.transportRequestCreate( + script: nullScript, + changeManagement: [ + type: 'RFC', + ]) + } + @Test public void cmIntegrationSwichtedOffTest() { diff --git a/test/groovy/TransportRequestReleaseTest.groovy b/test/groovy/TransportRequestReleaseTest.groovy index 960cf2dff..b28895926 100644 --- a/test/groovy/TransportRequestReleaseTest.groovy +++ b/test/groovy/TransportRequestReleaseTest.groovy @@ -1,3 +1,7 @@ +import static org.hamcrest.Matchers.allOf +import static org.hamcrest.Matchers.containsString + +import org.hamcrest.Matchers import org.junit.Before import org.junit.Rule import org.junit.Test @@ -10,6 +14,7 @@ import com.sap.piper.cm.ChangeManagementException import util.BasePiperTest import util.JenkinsCredentialsRule +import util.JenkinsDockerExecuteRule import util.JenkinsStepRule import util.JenkinsLoggingRule import util.JenkinsReadYamlRule @@ -48,7 +53,7 @@ public class TransportRequestReleaseTest extends BasePiperTest { } @Test - public void changeIdNotProvidedTest() { + public void changeDocumentIdNotProvidedSOLMANTest() { ChangeManagement cm = new ChangeManagement(nullScript) { String getChangeDocumentId(String from, @@ -84,14 +89,14 @@ public class TransportRequestReleaseTest extends BasePiperTest { } @Test - public void releaseTransportRequestFailureTest() { + public void releaseTransportRequestFailsSOLMANTest() { thrown.expect(AbortException) thrown.expectMessage("Something went wrong") ChangeManagement cm = new ChangeManagement(nullScript) { - void releaseTransportRequest(BackendType type, + void releaseTransportRequestSOLMAN( String changeId, String transportRequestId, String endpoint, @@ -106,7 +111,257 @@ public class TransportRequestReleaseTest extends BasePiperTest { } @Test - public void releaseTransportRequestSuccessTest() { + public void releaseTransportRequestFailsCTSTest() { + + thrown.expect(AbortException) + thrown.expectMessage("Something went wrong") + + nullScript + .commonPipelineEnvironment + .configuration + .general + .changeManagement + .type = 'CTS' + + ChangeManagement cm = new ChangeManagement(nullScript) { + + void releaseTransportRequestCTS( + String transportRequestId, + String endpoint, + String credentialsId, + String clientOpts) { + + throw new ChangeManagementException('Something went wrong') + } + } + + stepRule.step.transportRequestRelease( + script: nullScript, + transportRequestId: '001', + cmUtils: cm) + } + + @Test + public void releaseTransportRequestSuccessRFCTest() { + + def receivedParameters + + nullScript + .commonPipelineEnvironment + .configuration + .general + .changeManagement = + [ + credentialsId: 'CM', + type: 'RFC', + endpoint: 'https://example.org/rfc', + rfc: [ + dockerImage: 'rfc', + dockerOptions: [], + ], + ] + + ChangeManagement cm = new ChangeManagement(nullScript) { + void releaseTransportRequestRFC( + Map docker, + String transportRequestId, + String endpoint, + String developmentInstance, + String developmentClient, + String credentialsId, + boolean verbose) { + + receivedParameters = [ + docker: docker, + transportRequestId: transportRequestId, + endpoint: endpoint, + developmentInstance: developmentInstance, + developmentClient: developmentClient, + credentialsId: credentialsId, + verbose: verbose, + ] + } + } + + stepRule.step.transportRequestRelease( + script: nullScript, + transportRequestId: '002', + changeManagement: [ + rfc: [ + developmentClient: '003', + developmentInstance: '002', + ] + ], + verbose: true, + cmUtils: cm) + + assert receivedParameters == [ + docker: [ + image: 'rfc', + options: [], + envVars: [:], + pullImage: true, + ], + transportRequestId: '002', + endpoint: 'https://example.org/rfc', + developmentInstance: '002', + developmentClient: '003', + credentialsId: 'CM', + 'verbose': true, + ] + } + + @Test + public void releaseTransportRequestSuccessCTSTest() { + + def receivedParameters + + nullScript + .commonPipelineEnvironment + .configuration + .general + .changeManagement = + [ + credentialsId: 'CM', + type: 'CTS', + endpoint: 'https://example.org/cts' + ] + + ChangeManagement cm = new ChangeManagement(nullScript) { + void releaseTransportRequestCTS( + String transportRequestId, + String endpoint, + String credentialsId, + String clientOpts = '') { + + receivedParameters = [ + transportRequestId: transportRequestId, + endpoint: endpoint, + credentialsId: credentialsId, + clientOpts: clientOpts + ] + } + } + + stepRule.step.transportRequestRelease( + script: nullScript, + transportRequestId: '002', + cmUtils: cm) + + assert receivedParameters == [ + transportRequestId: '002', + endpoint: 'https://example.org/cts', + credentialsId: 'CM', + clientOpts: '' + ] + } + + @Test + public void releaseTransportRequestFailsRFCTest() { + + thrown.expect(AbortException) + thrown.expectMessage('Failed releasing transport request.') + + nullScript + .commonPipelineEnvironment + .configuration + .general + .changeManagement = + [ + credentialsId: 'CM', + type: 'RFC', + endpoint: 'https://example.org/rfc', + rfc: [dockerImage: 'rfc'] + ] + + ChangeManagement cm = new ChangeManagement(nullScript) { + void releaseTransportRequestRFC( + Map docker, + String transportRequestId, + String endpoint, + String developmentInstance, + String developmentClient, + String credentialsId, + boolean verbose) { + + throw new ChangeManagementException('Failed releasing transport request.') + } + } + + stepRule.step.transportRequestRelease( + script: nullScript, + transportRequestId: '002', + changeManagement: [ + rfc: [ + developmentClient: '003', + developmentInstance: '002' + ] + ], + cmUtils: cm) + + } + + @Test + public void releaseTransportRequestSanityChecksSOLMANTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage(allOf( + containsString('ERROR - NO VALUE AVAILABLE FOR'), + containsString('changeManagement/endpoint'))) + + // changeDocumentId and transportRequestId are not checked + // by the sanity checks here since they are looked up from + // commit history in case they are not provided. + + nullScript + .commonPipelineEnvironment + .configuration = null + + stepRule.step.transportRequestRelease( + script: nullScript, + changeManagement: [type: 'SOLMAN'] + ) + } + + @Test + public void releaseTransportRequestSanityChecksCTSTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage(allOf( + containsString('ERROR - NO VALUE AVAILABLE FOR'), + containsString('changeManagement/endpoint'))) + + nullScript + .commonPipelineEnvironment + .configuration = null + + stepRule.step.transportRequestRelease( + script: nullScript, + changeManagement: [type: 'CTS'] + ) + } + + @Test + public void releaseTransportRequestSanityChecksRFCTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage(allOf( + containsString('ERROR - NO VALUE AVAILABLE FOR:'), + containsString('changeManagement/endpoint'), + containsString('developmentClient'))) + + nullScript + .commonPipelineEnvironment + .configuration = null + + stepRule.step.transportRequestRelease( + script: nullScript, + changeManagement: [type: 'RFC'], + transportRequestId: '002') + } + + @Test + public void releaseTransportRequestSuccessSOLMANTest() { // Here we test only the case where the transportRequestId is // provided via parameters. The other cases are tested by @@ -118,14 +373,13 @@ public class TransportRequestReleaseTest extends BasePiperTest { Map receivedParams = [:] ChangeManagement cm = new ChangeManagement(nullScript) { - void releaseTransportRequest(BackendType type, + void releaseTransportRequestSOLMAN( String changeId, String transportRequestId, String endpoint, String credentialsId, String clientOpts) { - receivedParams.type = type receivedParams.changeId = changeId receivedParams.transportRequestId = transportRequestId receivedParams.endpoint = endpoint @@ -136,7 +390,7 @@ public class TransportRequestReleaseTest extends BasePiperTest { stepRule.step.transportRequestRelease(script: nullScript, changeDocumentId: '001', transportRequestId: '002', cmUtils: cm) - assert receivedParams == [type: BackendType.SOLMAN, + assert receivedParams == [ changeId: '001', transportRequestId: '002', endpoint: 'https://example.org/cm', diff --git a/test/groovy/TransportRequestUploadFileTest.groovy b/test/groovy/TransportRequestUploadFileTest.groovy index f3fb7aa62..e7ade0d5e 100644 --- a/test/groovy/TransportRequestUploadFileTest.groovy +++ b/test/groovy/TransportRequestUploadFileTest.groovy @@ -1,11 +1,17 @@ +import static org.hamcrest.Matchers.allOf +import static org.hamcrest.Matchers.containsString + +import java.util.List import java.util.Map +import org.hamcrest.Matchers import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.ExpectedException import org.junit.rules.RuleChain +import com.sap.piper.JenkinsUtils import com.sap.piper.cm.BackendType import com.sap.piper.cm.ChangeManagement import com.sap.piper.cm.ChangeManagementException @@ -119,10 +125,10 @@ public class TransportRequestUploadFileTest extends BasePiperTest { } @Test - public void uploadFileToTransportRequestFailureTest() { + public void uploadFileToTransportRequestSOLMANFailureTest() { ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, + void uploadFileToTransportRequestSOLMAN( String changeId, String transportRequestId, String applicationId, @@ -152,19 +158,14 @@ public class TransportRequestUploadFileTest extends BasePiperTest { loggingRule.expect("[INFO] File '/path' has been successfully uploaded to transport request '002'.") ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, - String changeId, + void uploadFileToTransportRequestCTS( String transportRequestId, - String applicationId, String filePath, String endpoint, String credentialsId, String cmclientOpts) { - cmUtilReceivedParams.type = type - cmUtilReceivedParams.changeId = changeId cmUtilReceivedParams.transportRequestId = transportRequestId - cmUtilReceivedParams.applicationId = applicationId cmUtilReceivedParams.filePath = filePath cmUtilReceivedParams.endpoint = endpoint cmUtilReceivedParams.credentialsId = credentialsId @@ -180,10 +181,7 @@ public class TransportRequestUploadFileTest extends BasePiperTest { assert cmUtilReceivedParams == [ - type: BackendType.CTS, - changeId: null, transportRequestId: '002', - applicationId: null, filePath: '/path', endpoint: 'https://example.org/cm', credentialsId: 'CM', @@ -191,6 +189,166 @@ public class TransportRequestUploadFileTest extends BasePiperTest { ] } + @Test + public void uploadFileToTransportRequestRFCSanityChecksTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage(allOf( + containsString('NO VALUE AVAILABLE FOR'), + containsString('applicationUrl'), + containsString('developmentInstance'), + containsString('developmentClient'), + containsString('applicationDescription'), + containsString('abapPackage'), + containsString('applicationName'))) + + stepRule.step.transportRequestUploadFile(script: nullScript, + transportRequestId: '123456', //no sanity check, can be read from git history + changeManagement: [type: 'RFC'], + ) + } + + @Test + public void uploadFileToTransportRequestRFCSuccessTest() { + + def cmUtilsReceivedParams + + nullScript.commonPipelineEnvironment.configuration = + [general: + [changeManagement: + [ + endpoint: 'https://example.org/rfc' + ] + ] + ] + + def cm = new ChangeManagement(nullScript) { + + void uploadFileToTransportRequestRFC( + Map docker, + String transportRequestId, + String applicationId, + String applicationURL, + String endpoint, + String credentialsId, + String developmentInstance, + String developmentClient, + String applicationDescription, + String abapPackage, + String codePage, + boolean acceptUnixStyleLineEndings, + boolean failUploadOnWarning, + boolean verbose) { + + cmUtilsReceivedParams = [ + docker: docker, + transportRequestId: transportRequestId, + applicationName: applicationId, + applicationURL: applicationURL, + endpoint: endpoint, + credentialsId: credentialsId, + developmentInstance: developmentInstance, + developmentClient: developmentClient, + applicationDescription: applicationDescription, + abapPackage: abapPackage, + codePage: codePage, + acceptUnixStyleLineEndings: acceptUnixStyleLineEndings, + failUploadOnWarning: failUploadOnWarning, + ] + } + } + + stepRule.step.transportRequestUploadFile(script: nullScript, + applicationUrl: 'http://example.org/blobstore/xyz.zip', + codePage: 'UTF-9', + acceptUnixStyleLineEndings: true, + transportRequestId: '123456', + changeManagement: [ + type: 'RFC', + rfc: [ + developmentClient: '002', + developmentInstance: '001' + ] + ], + applicationName: '42', + applicationDescription: 'Lorem ipsum', + abapPackage: 'XYZ', + cmUtils: cm,) + + assert cmUtilsReceivedParams == + [ + docker: [ + image: 'rfc', + options: [], + envVars: [:], + pullImage: true + ], + transportRequestId: '123456', + applicationName: '42', + applicationURL: 'http://example.org/blobstore/xyz.zip', + endpoint: 'https://example.org/rfc', + credentialsId: 'CM', + developmentInstance: '001', + developmentClient: '002', + applicationDescription: 'Lorem ipsum', + abapPackage:'XYZ', + codePage: 'UTF-9', + acceptUnixStyleLineEndings: true, + failUploadOnWarning: true, + ] + } + + @Test + public void uploadFileToTransportRequestRFCUploadFailsTest() { + + thrown.expect(AbortException) + thrown.expectMessage('upload failed') + + def cm = new ChangeManagement(nullScript) { + + void uploadFileToTransportRequestRFC( + Map docker, + String transportRequestId, + String applicationId, + String applicationURL, + String endpoint, + String credentialsId, + String developmentInstance, + String developmentClient, + String applicationDescription, + String abapPackage, + String codePage, + boolean acceptUnixStyleLineEndings, + boolean failOnUploadWarning, + boolean verbose) { + throw new ChangeManagementException('upload failed') + } + } + + stepRule.step.transportRequestUploadFile(script: nullScript, + applicationUrl: 'http://example.org/blobstore/xyz.zip', + codePage: 'UTF-9', + acceptUnixStyleLineEndings: true, + transportRequestId: '123456', + changeManagement: [ + type: 'RFC', + rfc: [ + docker: [ + image: 'rfc', + options: [], + envVars: [:], + pullImage: false, + ], + developmentClient: '002', + developmentInstance: '001', + ] + ], + applicationName: '42', + applicationDescription: 'Lorem ipsum', + abapPackage: 'XYZ', + cmUtils: cm,) + } + @Test public void uploadFileToTransportRequestSOLMANSuccessTest() { @@ -202,7 +360,7 @@ public class TransportRequestUploadFileTest extends BasePiperTest { loggingRule.expect("[INFO] File '/path' has been successfully uploaded to transport request '002' of change document '001'.") ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, + void uploadFileToTransportRequestSOLMAN( String changeId, String transportRequestId, String applicationId, @@ -211,7 +369,6 @@ public class TransportRequestUploadFileTest extends BasePiperTest { String credentialsId, String cmclientOpts) { - cmUtilReceivedParams.type = type cmUtilReceivedParams.changeId = changeId cmUtilReceivedParams.transportRequestId = transportRequestId cmUtilReceivedParams.applicationId = applicationId @@ -231,7 +388,6 @@ public class TransportRequestUploadFileTest extends BasePiperTest { assert cmUtilReceivedParams == [ - type: BackendType.SOLMAN, changeId: '001', transportRequestId: '002', applicationId: 'app', @@ -243,14 +399,14 @@ public class TransportRequestUploadFileTest extends BasePiperTest { } @Test - public void uploadFileToTransportRequestSuccessApplicationIdFromConfigurationTest() { + public void uploadFileToTransportRequestSOLMANSuccessApplicationIdFromConfigurationTest() { nullScript.commonPipelineEnvironment.configuration.put(['steps', [transportRequestUploadFile: [applicationId: 'AppIdfromConfig']]]) ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, + void uploadFileToTransportRequestSOLMAN( String changeId, String transportRequestId, String applicationId, @@ -274,13 +430,13 @@ public class TransportRequestUploadFileTest extends BasePiperTest { } @Test - public void uploadFileToTransportRequestFilePathFromParameters() { + public void uploadFileToTransportRequestSOLMANFilePathFromParameters() { // this one is not used when file path is provided via signature nullScript.commonPipelineEnvironment.setMtarFilePath('/path2') ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, + void uploadFileToTransportRequestSOLMAN( String changeId, String transportRequestId, String applicationId, @@ -304,13 +460,13 @@ public class TransportRequestUploadFileTest extends BasePiperTest { } @Test - public void uploadFileToTransportRequestFilePathFromCommonPipelineEnvironment() { + public void uploadFileToTransportRequestSOLMANFilePathFromCommonPipelineEnvironment() { // this one is used since there is nothing in the signature nullScript.commonPipelineEnvironment.setMtarFilePath('/path2') ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, + void uploadFileToTransportRequestSOLMAN( String changeId, String transportRequestId, String applicationId, @@ -333,13 +489,13 @@ public class TransportRequestUploadFileTest extends BasePiperTest { } @Test - public void uploadFileToTransportRequestUploadFailureTest() { + public void uploadFileToTransportRequestSOLMANUploadFailureTest() { thrown.expect(AbortException) thrown.expectMessage('Upload failure.') ChangeManagement cm = new ChangeManagement(nullScript) { - void uploadFileToTransportRequest(BackendType type, + void uploadFileToTransportRequestSOLMAN( String changeId, String transportRequestId, String applicationId, @@ -362,7 +518,7 @@ public class TransportRequestUploadFileTest extends BasePiperTest { @Test public void invalidBackendTypeTest() { thrown.expect(AbortException) - thrown.expectMessage('Invalid backend type: \'DUMMY\'. Valid values: [SOLMAN, CTS, NONE]. ' + + thrown.expectMessage('Invalid backend type: \'DUMMY\'. Valid values: [SOLMAN, CTS, RFC, NONE]. ' + 'Configuration: \'changeManagement/type\'.') stepRule.step.transportRequestUploadFile(script: nullScript, diff --git a/test/groovy/com/sap/piper/cm/ChangeManagementTest.groovy b/test/groovy/com/sap/piper/cm/ChangeManagementTest.groovy index cf32ab220..cad42129d 100644 --- a/test/groovy/com/sap/piper/cm/ChangeManagementTest.groovy +++ b/test/groovy/com/sap/piper/cm/ChangeManagementTest.groovy @@ -1,12 +1,13 @@ package com.sap.piper.cm - import static org.hamcrest.Matchers.allOf +import static org.hamcrest.Matchers.contains import static org.hamcrest.Matchers.containsString import static org.hamcrest.Matchers.equalTo import static org.hamcrest.Matchers.hasItem import static org.hamcrest.Matchers.is import static org.hamcrest.Matchers.not import static org.junit.Assert.assertThat +import static org.junit.Assert.assertEquals import org.hamcrest.Matchers import org.junit.Assert @@ -22,6 +23,7 @@ import util.JenkinsLoggingRule import util.JenkinsScriptLoaderRule import util.JenkinsShellCallRule import util.JenkinsCredentialsRule +import util.JenkinsDockerExecuteRule import util.Rules import hudson.AbortException @@ -32,6 +34,7 @@ public class ChangeManagementTest extends BasePiperTest { private JenkinsShellCallRule script = new JenkinsShellCallRule(this) private JenkinsLoggingRule logging = new JenkinsLoggingRule(this) + private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this) @Rule public RuleChain rules = Rules.getCommonRules(this) @@ -39,6 +42,7 @@ public class ChangeManagementTest extends BasePiperTest { .around(script) .around(logging) .around(new JenkinsCredentialsRule(this).withCredentials('me','user','password')) + .around(dockerExecuteRule) @Test public void testRetrieveChangeDocumentIdOutsideGitWorkTreeTest() { @@ -167,6 +171,57 @@ public void testGetCommandLineWithCMClientOpts() { } + @Test + public void testCreateTransportRequestRFCSucceeds() { + + script.setReturnValue('cts createTransportRequest', '{"REQUESTID":"XYZK9000004"}') + + def transportRequestId = new ChangeManagement(nullScript).createTransportRequestRFC( + [image: 'rfc', options: []], + 'https://example.org/rfc', // endpoint + '01', // instance + '001', // client + 'me', // credentialsId + 'Lorem ipsum', // description + true // verbose + ) + + assert dockerExecuteRule.dockerParams.dockerImage == 'rfc' + + assert dockerExecuteRule.dockerParams.dockerEnvVars == [ + TRANSPORT_DESCRIPTION: 'Lorem ipsum', + ABAP_DEVELOPMENT_INSTANCE: '01', + ABAP_DEVELOPMENT_CLIENT: '001', + ABAP_DEVELOPMENT_SERVER: 'https://example.org/rfc', + ABAP_DEVELOPMENT_USER: 'user', + ABAP_DEVELOPMENT_PASSWORD: 'password', + VERBOSE: true + ] + + assert transportRequestId == 'XYZK9000004' + + } + + @Test + public void testCreateTransportRequestRFCFails() { + + thrown.expect(ChangeManagementException) + thrown.expectMessage('Cannot create transport request: script returned exit code 3') + + script.setReturnValue('cts createTransportRequest', + { throw new AbortException('script returned exit code 3')}) + + def transportRequestId = new ChangeManagement(nullScript).createTransportRequestRFC( + [image: 'rfc', options: []], + 'https://example.org/rfc', // endpoint + '001', // client + '01', // instance + 'me', // credentialsId + 'Lorem ipsum', // description + true, //verbose + ) + } + @Test public void testCreateTransportRequestCTSSucceeds() { @@ -185,32 +240,13 @@ public void testGetCommandLineWithCMClientOpts() { } - @Test - public void testCreateTransportRequestFails() { - - script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*upload-file-to-transport.*', 1) - - thrown.expect(ChangeManagementException) - thrown.expectMessage('Cannot upload file \'/path\' for change document \'001\''+ - ' with transport request \'002\'. Return code from cmclient: 1.') - - new ChangeManagement(nullScript).uploadFileToTransportRequest(BackendType.SOLMAN, - '001', - '002', - 'XXX', - '/path', - 'https://example.org/cm', - 'me') - } - @Test public void testUploadFileToTransportSucceedsSOLMAN() { // the regex provided below is an implicit check that the command line is fine. script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'upload-file-to-transport.*-cID 001 -tID 002 XXX "/path"', 0) - new ChangeManagement(nullScript).uploadFileToTransportRequest( - BackendType.SOLMAN, + new ChangeManagement(nullScript).uploadFileToTransportRequestSOLMAN( '001', '002', 'XXX', @@ -228,11 +264,8 @@ public void testGetCommandLineWithCMClientOpts() { // the regex provided below is an implicit check that the command line is fine. script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t CTS upload-file-to-transport -tID 002 "/path"', 0) - new ChangeManagement(nullScript).uploadFileToTransportRequest( - BackendType.CTS, - null, + new ChangeManagement(nullScript).uploadFileToTransportRequestCTS( '002', - null, '/path', 'https://example.org/cm', 'me') @@ -242,15 +275,85 @@ public void testGetCommandLineWithCMClientOpts() { } @Test - public void testUploadFileToTransportFails() { + public void testUploadFileToTransportSucceedsRFC() { + + new ChangeManagement(nullScript).uploadFileToTransportRequestRFC( + [image:'rfc', options: [], pullImage: true], + '002', //transportRequestId + '001', // applicationId + 'https://example.org/mypath/deployArtifact.zip', + 'https://example.org/rfc', + 'me', + '00', //developmentInstance + '001', // developmentClient + 'Lorem ipsum', // applicationDescription + 'XYZ', // abapPackage + 'UTF-9', //codePage + true, // accept unix style EOL + true, // failUploadOnWarning + false, // verbose + ) + + + assert dockerExecuteRule.dockerParams.dockerImage == 'rfc' + assert dockerExecuteRule.dockerParams.dockerPullImage == true + + assert dockerExecuteRule.dockerParams.dockerEnvVars == + [ + ABAP_DEVELOPMENT_INSTANCE: '00', + ABAP_DEVELOPMENT_CLIENT: '001', + ABAP_APPLICATION_NAME: '001', + ABAP_APPLICATION_DESC: 'Lorem ipsum', + ABAP_PACKAGE: 'XYZ', + ZIP_FILE_URL: 'https://example.org/mypath/deployArtifact.zip', + ABAP_DEVELOPMENT_SERVER: 'https://example.org/rfc', + ABAP_DEVELOPMENT_USER: 'user', + ABAP_DEVELOPMENT_PASSWORD: 'password', + CODE_PAGE: 'UTF-9', + ABAP_ACCEPT_UNIX_STYLE_EOL: 'X', + FAIL_UPLOAD_ON_WARNING: 'true', + VERBOSE: 'false' + ] + + assertThat(script.shell, contains('cts uploadToABAP:002')) + } + + @Test + public void testUploadFileToTransportFailsRFC() { thrown.expect(ChangeManagementException) - thrown.expectMessage("Cannot upload file '/path' for change document '001' with transport request '002'. " + - "Return code from cmclient: 1.") + thrown.expectMessage('Cannot upload file into transport request. Return code from rfc client: 1.') + + script.setReturnValue('cts uploadToABAP:002', 1) + + new ChangeManagement(nullScript).uploadFileToTransportRequestRFC( + [:], + '002', //transportRequestId + '001', // applicationId + 'https://example.org/mypath/deployArtifact.zip', + 'https://example.org/rfc', + 'me', + '00', //developmentInstance + '001', // developmentClient + 'Lorem ipsum', // applicationDescription + 'XYZ', // abapPackage + 'UTF-9', // codePage + true, // accept unix style EOL + true, // failUploadOnWarning + false, // verbose + ) + } + + @Test + public void testUploadFileToTransportFailsSOLMAN() { + + thrown.expect(ChangeManagementException) + thrown.expectMessage("Cannot upload file into transport request. " + + "Return code from cm client: 1.") script.setReturnValue(JenkinsShellCallRule.Type.REGEX,, 'upload-file-to-transport', 1) - new ChangeManagement(nullScript).uploadFileToTransportRequest(BackendType.SOLMAN, + new ChangeManagement(nullScript).uploadFileToTransportRequestSOLMAN( '001', '002', 'XXX', @@ -265,8 +368,7 @@ public void testGetCommandLineWithCMClientOpts() { // the regex provided below is an implicit check that the command line is fine. script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t SOLMAN release-transport.*-cID 001.*-tID 002', 0) - new ChangeManagement(nullScript).releaseTransportRequest( - BackendType.SOLMAN, + new ChangeManagement(nullScript).releaseTransportRequestSOLMAN( '001', '002', 'https://example.org', @@ -283,9 +385,7 @@ public void testGetCommandLineWithCMClientOpts() { // the regex provided below is an implicit check that the command line is fine. script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t CTS export-transport.*-tID 002', 0) - new ChangeManagement(nullScript).releaseTransportRequest( - BackendType.CTS, - null, + new ChangeManagement(nullScript).releaseTransportRequestCTS( '002', 'https://example.org', 'me', @@ -296,7 +396,31 @@ public void testGetCommandLineWithCMClientOpts() { } @Test - public void testReleaseTransportRequestFails() { + public void testReleaseTransportRequestSucceedsRFC() { + + new ChangeManagement(nullScript).releaseTransportRequestRFC( + [:], + '002', + 'https://example.org', + '002', + '001', + 'me', + true) + + assert dockerExecuteRule.dockerParams.dockerEnvVars == [ + ABAP_DEVELOPMENT_SERVER: 'https://example.org', + ABAP_DEVELOPMENT_USER: 'user', + ABAP_DEVELOPMENT_PASSWORD: 'password', + ABAP_DEVELOPMENT_CLIENT: '001', + ABAP_DEVELOPMENT_INSTANCE: '002', + VERBOSE: true, + ] + + assertThat(script.shell, hasItem('cts releaseTransport:002')) + } + + @Test + public void testReleaseTransportRequestFailsSOLMAN() { thrown.expect(ChangeManagementException) thrown.expectMessage("Cannot release Transport Request '002'. Return code from cmclient: 1.") @@ -304,16 +428,13 @@ public void testGetCommandLineWithCMClientOpts() { // the regex provided below is an implicit check that the command line is fine. script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'release-transport.*-cID 001.*-tID 002', 1) - new ChangeManagement(nullScript).releaseTransportRequest( - BackendType.SOLMAN, + new ChangeManagement(nullScript).releaseTransportRequestSOLMAN( '001', '002', 'https://example.org', - 'me', - 'openSesame') + 'me') } - private GitUtils gitUtilsMock(boolean insideWorkTree, String[] changeIds) { return new GitUtils() { public boolean insideWorkTree() { diff --git a/test/groovy/com/sap/piper/tools/neo/NeoCommandHelperTest.groovy b/test/groovy/com/sap/piper/tools/neo/NeoCommandHelperTest.groovy index ad54ebc05..d272c24cd 100644 --- a/test/groovy/com/sap/piper/tools/neo/NeoCommandHelperTest.groovy +++ b/test/groovy/com/sap/piper/tools/neo/NeoCommandHelperTest.groovy @@ -34,7 +34,6 @@ class NeoCommandHelperTest extends BasePiperTest { String source = (deployMode == DeployMode.MTA) ? 'file.mta' : 'file.war' String username = 'username' String password = 'password' - String neoExecutable = '/path/tools/neo.sh'; nullScript.STEP_NAME="neoDeploy" @@ -42,7 +41,6 @@ class NeoCommandHelperTest extends BasePiperTest { nullScript, deployMode, deploymentConfiguration, - neoExecutable, username, password, source @@ -52,7 +50,7 @@ class NeoCommandHelperTest extends BasePiperTest { @Test void testStatusCommand() { String actual = getTestFixture(DeployMode.WAR_PARAMS).statusCommand() - String expected = "\"/path/tools/neo.sh\" status --host 'host_value' --account 'account_value' " + + String expected = "neo.sh status --host 'host_value' --account 'account_value' " + "--application 'application_value' --user 'username' --password 'password'" Assert.assertEquals(expected, actual) } @@ -60,14 +58,14 @@ class NeoCommandHelperTest extends BasePiperTest { @Test void testStatusCommandForProperties() { String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).statusCommand() - String expected = "\"/path/tools/neo.sh\" status file.properties --user 'username' --password 'password'" + String expected = "neo.sh status file.properties --user 'username' --password 'password'" Assert.assertEquals(expected, actual) } @Test void testRollingUpdateCommand() { String actual = getTestFixture(DeployMode.WAR_PARAMS).rollingUpdateCommand() - String basicCommand = "\"/path/tools/neo.sh\" rolling-update --host 'host_value' --account 'account_value' " + + String basicCommand = "neo.sh rolling-update --host 'host_value' --account 'account_value' " + "--application 'application_value' --user 'username' --password 'password' --source 'file.war'" Assert.assertTrue(actual.contains(basicCommand)) @@ -81,14 +79,14 @@ class NeoCommandHelperTest extends BasePiperTest { @Test void testRollingUpdateCommandForProperties() { String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).rollingUpdateCommand() - String expected = "\"/path/tools/neo.sh\" rolling-update file.properties --user 'username' --password 'password' --source 'file.war' " + String expected = "neo.sh rolling-update file.properties --user 'username' --password 'password' --source 'file.war' " Assert.assertEquals(expected, actual) } @Test void testDeployCommand() { String actual = getTestFixture(DeployMode.WAR_PARAMS).deployCommand() - String basicCommand = "\"/path/tools/neo.sh\" deploy --host 'host_value' --account 'account_value' " + + String basicCommand = "neo.sh deploy --host 'host_value' --account 'account_value' " + "--application 'application_value' --user 'username' --password 'password' --source 'file.war'" Assert.assertTrue(actual.contains(basicCommand)) @@ -102,14 +100,14 @@ class NeoCommandHelperTest extends BasePiperTest { @Test void testDeployCommandForProperties() { String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).deployCommand() - String expected = "\"/path/tools/neo.sh\" deploy file.properties --user 'username' --password 'password' --source 'file.war' " + String expected = "neo.sh deploy file.properties --user 'username' --password 'password' --source 'file.war' " Assert.assertEquals(expected, actual) } @Test void testRestartCommand() { String actual = getTestFixture(DeployMode.WAR_PARAMS).restartCommand() - String expected = "\"/path/tools/neo.sh\" restart --synchronous --host 'host_value' --account 'account_value' " + + String expected = "neo.sh restart --synchronous --host 'host_value' --account 'account_value' " + "--application 'application_value' --user 'username' --password 'password'" Assert.assertEquals(expected, actual) } @@ -117,14 +115,14 @@ class NeoCommandHelperTest extends BasePiperTest { @Test void testRestartCommandForProperties() { String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).restartCommand() - String expected = "\"/path/tools/neo.sh\" restart --synchronous file.properties --user 'username' --password 'password'" + String expected = "neo.sh restart --synchronous file.properties --user 'username' --password 'password'" Assert.assertEquals(expected, actual) } @Test void deployMta() { String actual = getTestFixture(DeployMode.MTA).deployMta() - String expected = "\"/path/tools/neo.sh\" deploy-mta --synchronous --host 'host_value' --account 'account_value' " + + String expected = "neo.sh deploy-mta --synchronous --host 'host_value' --account 'account_value' " + "--user 'username' --password 'password' --source 'file.mta'" Assert.assertEquals(expected, actual) } diff --git a/vars/influxWriteData.groovy b/vars/influxWriteData.groovy index 79a083756..2f51c74e2 100644 --- a/vars/influxWriteData.groovy +++ b/vars/influxWriteData.groovy @@ -35,7 +35,10 @@ void call(Map parameters = [:]) { .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS) .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS) .mixin([ - artifactVersion: script.commonPipelineEnvironment.getArtifactVersion() + artifactVersion: script.commonPipelineEnvironment.getArtifactVersion(), + influxPrefix: script.commonPipelineEnvironment.getGithubOrg() && script.commonPipelineEnvironment.getGithubRepo() + ? "${script.commonPipelineEnvironment.getGithubOrg()}_${script.commonPipelineEnvironment.getGithubRepo()}" + : null ]) .mixin(parameters, PARAMETER_KEYS) .addIfNull('customData', script.commonPipelineEnvironment.getInfluxCustomData()) diff --git a/vars/karmaExecuteTests.groovy b/vars/karmaExecuteTests.groovy index e12b00f47..19432aa5d 100644 --- a/vars/karmaExecuteTests.groovy +++ b/vars/karmaExecuteTests.groovy @@ -1,6 +1,7 @@ import static com.sap.piper.Prerequisites.checkScript import com.sap.piper.ConfigurationHelper +import com.sap.piper.GenerateDocumentation import com.sap.piper.GitUtils import com.sap.piper.Utils @@ -9,34 +10,67 @@ import groovy.transform.Field @Field String STEP_NAME = getClass().getName() @Field Set GENERAL_CONFIG_KEYS = [ - /** port mappings required for containers. This will only take effect inside a Kubernetes pod, format [[containerPort: 1111, hostPort: 1111]] */ + /** + * Map which defines per docker image the port mappings, e.g. `containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]]`. + */ 'containerPortMappings', - /** envVars to be set in the execution container if required */ + /** A map of environment variables to set in the container, e.g. [http_proxy:'proxy:8080']. */ 'dockerEnvVars', - /** Docker image for code execution */ + /** The 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. */ 'dockerImage', - /** name of the Docker container. If not on Kubernetes pod, this will define the network-alias to the NPM container and is thus required for accessing the server, example http://karma:9876 (default). */ + /** + * Kubernetes only: + * Name of the container launching `dockerImage`. + * SideCar only: + * Name of the container in local network. + */ 'dockerName', - /** user home directory for Docker execution. This will only take effect inside a Kubernetes pod. */ + /** + * Kubernetes only: + * Specifies a dedicated user home directory for the container which will be passed as value for environment variable `HOME`. + */ 'dockerWorkspace', + /** + * With `failOnError` the behavior in case tests fail can be defined. + * @possibleValues `true`, `false` + */ 'failOnError', + /** The command that is executed to install the test tool. */ 'installCommand', + /** Define the paths of the modules to execute tests on. */ 'modules', + /** The command that is executed to start the tests. */ 'runCommand', - /** envVars to be set in Selenium container if required */ + /** A map of environment variables to set in the sidecar container, similar to `dockerEnvVars`. */ 'sidecarEnvVars', - /** image for Selenium execution which runs as sidecar to dockerImage */ + /** The name of the docker image of the sidecar container. If empty, no sidecar container is started. */ 'sidecarImage', - /** name of the Selenium container. If not on Kubernetes pod, this will define the network-alias to the Selenium container and is thus required for accessing the server, example http://selenium:4444 (default) */ + /** + * as `dockerName` for the sidecar container + */ 'sidecarName', - /** volume bind. This will not take effect in Kubernetes pod. */ + /** Volumes that should be mounted into the sidecar container. */ 'sidecarVolumeBind', - /** list of stash names which are required to be unstashed before test run */ + /** If specific stashes should be considered for the tests, their names need to be passed via the parameter `stashContent`. */ 'stashContent' ] @Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS +/** + * In this step the ([Karma test runner](http://karma-runner.github.io)) is executed. + * + * The step is using the `seleniumExecuteTest` step to spin up two containers in a Docker network: + * + * * a Selenium/Chrome container (`selenium/standalone-chrome`) + * * a NodeJS container (`node:8-stretch`) + * + * In the Docker network, the containers can be referenced by the values provided in `dockerName` and `sidecarName`, the default values are `karma` and `selenium`. These values must be used in the `hostname` properties of the test configuration ([Karma](https://karma-runner.github.io/1.0/config/configuration-file.html) and [WebDriver](https://github.com/karma-runner/karma-webdriver-launcher#usage)). + * + * !!! note + * In a Kubernetes environment, the containers both need to be referenced with `localhost`. + */ +@GenerateDocumentation void call(Map parameters = [:]) { handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) { final script = checkScript(this, parameters) ?: this diff --git a/vars/neoDeploy.groovy b/vars/neoDeploy.groovy index a239452dd..e0b9c8e82 100644 --- a/vars/neoDeploy.groovy +++ b/vars/neoDeploy.groovy @@ -1,6 +1,5 @@ import com.sap.piper.ConfigurationHelper import com.sap.piper.Utils -import com.sap.piper.tools.ToolDescriptor import com.sap.piper.tools.neo.DeployMode import com.sap.piper.tools.neo.NeoCommandHelper import com.sap.piper.tools.neo.WarAction @@ -25,21 +24,6 @@ import static com.sap.piper.Prerequisites.checkScript 'warAction' ]) -@Field Map CONFIG_KEY_COMPATIBILITY = [ - neo : [ - host : 'host', - account : 'account', - application : 'applicationName', - credentialsId : 'neoCredentialsId', - propertiesFile: 'propertiesFile', - runtime : 'runtime', - runtimeVersion: 'runtimeVersion', - size : 'vmSize' - - ], - source: 'archivePath' -] - void call(parameters = [:]) { handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) { @@ -49,19 +33,17 @@ void call(parameters = [:]) { prepareDefaultValues script: script - Map stepCompatibilityConfiguration = handleCompatibility(script, parameters) - // load default & individual configuration Map configuration = ConfigurationHelper.newInstance(this) .loadStepDefaults() .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS) - .mixin(stepCompatibilityConfiguration) - .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY) - .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY) + .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS) + .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS) .addIfEmpty('source', script.commonPipelineEnvironment.getMtarFilePath()) - .mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY) + .mixin(parameters, PARAMETER_KEYS) .withMandatoryProperty('neo') .withMandatoryProperty('source') + .withMandatoryProperty('neo/credentialsId') .withPropertyInValues('deployMode', DeployMode.stringValues()) .use() @@ -73,52 +55,37 @@ void call(parameters = [:]) { stepParam2: configuration.warAction == 'rolling-update'?'blue-green':'standard', // ['deploy', 'deploy-mta', 'rolling-update'] stepParamKey3: 'scriptMissing', stepParam3: parameters?.script == null, - stepParamKey4: 'legacyConfig', - stepParam4: !stepCompatibilityConfiguration.isEmpty(), ], configuration) - ToolDescriptor neo = new ToolDescriptor('SAP Cloud Platform Console Client', 'NEO_HOME', 'neoHome', '/tools/', 'neo.sh', null, 'version') - ToolDescriptor java = new ToolDescriptor('Java', 'JAVA_HOME', '', '/bin/', 'java', '1.8.0', '-version 2>&1') - if (configuration.neo.credentialsId) { - withCredentials([usernamePassword( - credentialsId: configuration.neo.credentialsId, - passwordVariable: 'NEO_PASSWORD', - usernameVariable: 'NEO_USERNAME')]) { + withCredentials([usernamePassword( + credentialsId: configuration.neo.credentialsId, + passwordVariable: 'NEO_PASSWORD', + usernameVariable: 'NEO_USERNAME')]) { - assertPasswordRules(NEO_PASSWORD) + assertPasswordRules(NEO_PASSWORD) - dockerExecute( - script: script, - dockerImage: configuration.dockerImage, - dockerEnvVars: configuration.dockerEnvVars, - dockerOptions: configuration.dockerOptions - ) { + dockerExecute( + script: script, + dockerImage: configuration.dockerImage, + dockerEnvVars: configuration.dockerEnvVars, + dockerOptions: configuration.dockerOptions + ) { + DeployMode deployMode = DeployMode.fromString(configuration.deployMode) - neo.verify(this, configuration) - java.verify(this, configuration) + NeoCommandHelper neoCommandHelper = new NeoCommandHelper( + this, + deployMode, + configuration.neo, + NEO_USERNAME, + NEO_PASSWORD, + configuration.source + ) - String neoExecutable = neo.getToolExecutable(script, configuration) - - DeployMode deployMode = DeployMode.fromString(configuration.deployMode) - - NeoCommandHelper neoCommandHelper = new NeoCommandHelper( - this, - deployMode, - configuration.neo, - neoExecutable, - NEO_USERNAME, - NEO_PASSWORD, - configuration.source - ) - - lock("$STEP_NAME :${neoCommandHelper.resourceLock()}") { - deploy(script, utils, configuration, neoCommandHelper, configuration.dockerImage, deployMode) - } + lock("$STEP_NAME :${neoCommandHelper.resourceLock()}") { + deploy(script, utils, configuration, neoCommandHelper, configuration.dockerImage, deployMode) } } - } else { - error("[neoDeploy] No credentials defined for the deployment. Please specify the value for credentialsId for neo.") } } } @@ -168,61 +135,6 @@ private boolean isAppRunning(NeoCommandHelper commandHelper) { return status.contains('Status: STARTED') } -private handleCompatibility(script, parameters) { - final Map neoCompatibilityConfiguration = [:] - - // Backward compatibility: ensure old configuration is taken into account - // The old configuration in not stage / step specific - - def defaultDeployHost = script.commonPipelineEnvironment.getConfigProperty('DEPLOY_HOST') - if (defaultDeployHost) { - echo "[WARNING][${STEP_NAME}] A deprecated configuration framework is used for configuring parameter 'DEPLOY_HOST'. This configuration framework will be removed in future versions." - neoCompatibilityConfiguration.put('host', defaultDeployHost) - } - - def defaultDeployAccount = script.commonPipelineEnvironment.getConfigProperty('CI_DEPLOY_ACCOUNT') - if (defaultDeployAccount) { - echo "[WARNING][${STEP_NAME}] A deprecated configuration framework is used for configuring parameter 'DEPLOY_ACCOUNT'. This configuration framekwork will be removed in future versions." - neoCompatibilityConfiguration.put('account', defaultDeployAccount) - } - - if (parameters.deployHost && !parameters.host) { - echo "[WARNING][${STEP_NAME}] Deprecated parameter 'deployHost' is used. This will not work anymore in future versions. Use parameter 'host' instead." - parameters.put('host', parameters.deployHost) - } - - if (parameters.deployAccount && !parameters.account) { - echo "[WARNING][${STEP_NAME}] Deprecated parameter 'deployAccount' is used. This will not work anymore in future versions. Use parameter 'account' instead." - parameters.put('account', parameters.deployAccount) - } - - def credId = script.commonPipelineEnvironment.getConfigProperty('neoCredentialsId') - if (credId && !parameters.neoCredentialsId) { - echo "[WARNING][${STEP_NAME}] Deprecated parameter 'neoCredentialsId' from old configuration framework is used. This will not work anymore in future versions." - parameters.put('neoCredentialsId', credId) - } - - if (!neoCompatibilityConfiguration.isEmpty()) { - echo "[WARNING][$STEP_NAME] You are using a deprecated configuration framework. This will be removed in " + - 'futureVersions.\nAdd snippet below to \'./pipeline/config.yml\' and remove ' + - 'file \'.pipeline/configuration.properties\'.\n' + - """|steps: - | neoDeploy: - | neo: - | host: ${neoCompatibilityConfiguration.get('host', '')} - | account: ${neoCompatibilityConfiguration.get('account', '')} - """.stripMargin() - - if (Boolean.getBoolean('com.sap.piper.featureFlag.buildUnstableWhenOldConfigFrameworkIsUsedByNeoDeploy')) { - script.currentBuild.setResult('UNSTABLE') - echo "[WARNING][$STEP_NAME] Build has been set to unstable since old config framework is used." - } - return [neo: neoCompatibilityConfiguration] - } - - return [:] -} - private assertPasswordRules(String password) { if (password.startsWith("@")) { error("Your password for the deployment to SAP Cloud Platform contains characters which are not " + diff --git a/vars/npmExecute.groovy b/vars/npmExecute.groovy new file mode 100644 index 000000000..06e980737 --- /dev/null +++ b/vars/npmExecute.groovy @@ -0,0 +1,73 @@ +import static com.sap.piper.Prerequisites.checkScript +import com.sap.piper.GenerateDocumentation +import com.sap.piper.ConfigurationHelper +import com.sap.piper.Utils +import groovy.transform.Field + +@Field def STEP_NAME = getClass().getName() +@Field Set GENERAL_CONFIG_KEYS = [] +@Field Set STEP_CONFIG_KEYS = [ + /** + * Name of the docker image that should be used, in which node should be installed and configured. Default value is 'node:8-stretch'. + */ + 'dockerImage', + /** + * URL of default NPM registry + */ + 'defaultNpmRegistry', + /** + * Which NPM command should be executed. + */ + 'npmCommand'] +@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS + [ + /** + * Docker options to be set when starting the container. + */ + 'dockerOptions'] +/** + * Executes NPM commands inside a docker container. + * Docker image, docker options and npm commands can be specified or configured. + */ +@GenerateDocumentation +void call(Map parameters = [:], body = null) { + handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) { + + final script = checkScript(this, parameters) ?: this + + // load default & individual configuration + 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) + .mixin(parameters, PARAMETER_KEYS) + .use() + + new Utils().pushToSWA([ + step: STEP_NAME, + stepParamKey1: 'scriptMissing', + stepParam1: parameters?.script == null + ], configuration) + + try { + if (!fileExists('package.json')) { + error "[${STEP_NAME}] package.json is not found." + } + dockerExecute(script: script, dockerImage: configuration.dockerImage, dockerOptions: configuration.dockerOptions) { + if (configuration.defaultNpmRegistry) { + sh "npm config set registry ${configuration.defaultNpmRegistry}" + } + if (configuration.npmCommand) { + sh "npm ${configuration.npmCommand}" + } + if (body) { + body() + } + } + } catch (Exception e) { + println "Error while executing npm. Here are the logs:" + sh "cat ~/.npm/_logs/*" + throw e + } + } +} diff --git a/vars/prepareDefaultValues.groovy b/vars/prepareDefaultValues.groovy index c1e80d323..79f296a7b 100644 --- a/vars/prepareDefaultValues.groovy +++ b/vars/prepareDefaultValues.groovy @@ -6,7 +6,7 @@ import groovy.transform.Field @Field STEP_NAME = getClass().getName() void call(Map parameters = [:]) { - handlePipelineStepErrors (stepName: 'prepareDefaultValues', stepParameters: parameters) { + handlePipelineStepErrors (stepName: 'prepareDefaultValues', stepParameters: parameters, echoDetails: false) { if(!DefaultValueCache.getInstance() || parameters.customDefaults) { def defaultValues = [:] def configFileList = ['default_pipeline_environment.yml'] diff --git a/vars/toolValidate.groovy b/vars/toolValidate.groovy index 7168809d6..339c6bb9c 100644 --- a/vars/toolValidate.groovy +++ b/vars/toolValidate.groovy @@ -14,7 +14,7 @@ void call(Map parameters = [:]) { handlePipelineStepErrors (stepName: 'toolValidate', stepParameters: parameters) { - echo '[WARNING][toolValidate] This step is deprecated, and it will be removed in future versions. Validation is automatically done inside the steps.' + echo '[WARNING][toolValidate] This step is deprecated, and it will be removed in future versions.' def tool = parameters.tool def home = parameters.home diff --git a/vars/transportRequestCreate.groovy b/vars/transportRequestCreate.groovy index 1dcb94c4a..04dd373f8 100644 --- a/vars/transportRequestCreate.groovy +++ b/vars/transportRequestCreate.groovy @@ -23,6 +23,7 @@ import hudson.AbortException 'developmentSystemId', // SOLMAN 'targetSystem', // CTS 'transportType', // CTS + 'verbose', // RFC ] @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus(['changeDocumentId']) @@ -38,6 +39,7 @@ void call(parameters = [:]) { ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script) ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this) + .collectValidationFailures() .loadStepDefaults() .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS) .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS) @@ -60,6 +62,9 @@ void call(parameters = [:]) { .withMandatoryProperty('transportType', null, { backendType == BackendType.CTS}) .withMandatoryProperty('targetSystem', null, { backendType == BackendType.CTS}) .withMandatoryProperty('description', null, { backendType == BackendType.CTS}) + .withMandatoryProperty('changeManagement/rfc/developmentInstance', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('changeManagement/rfc/developmentClient', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('verbose', null, {backendType == BackendType.RFC}) def changeDocumentId = null @@ -88,7 +93,8 @@ void call(parameters = [:]) { creatingMessage << '.' echo creatingMessage.join() - try { + + try { if(backendType == BackendType.SOLMAN) { transportRequestId = cm.createTransportRequestSOLMAN( configuration.changeDocumentId, @@ -104,6 +110,15 @@ void call(parameters = [:]) { configuration.changeManagement.endpoint, configuration.changeManagement.credentialsId, configuration.changeManagement.clientOpts) + } else if (backendType == BackendType.RFC) { + transportRequestId = cm.createTransportRequestRFC( + configuration.changeManagement.rfc.docker, + configuration.changeManagement.endpoint, + configuration.changeManagement.rfc.developmentInstance, + configuration.changeManagement.rfc.developmentClient, + configuration.changeManagement.credentialsId, + configuration.description, + configuration.verbose) } else { throw new IllegalArgumentException("Invalid backend type: '${backendType}'.") } diff --git a/vars/transportRequestRelease.groovy b/vars/transportRequestRelease.groovy index 334251021..9d87b185d 100644 --- a/vars/transportRequestRelease.groovy +++ b/vars/transportRequestRelease.groovy @@ -25,6 +25,7 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([ 'changeDocumentId', 'transportRequestId', + 'verbose', ]) void call(parameters = [:]) { @@ -49,12 +50,16 @@ void call(parameters = [:]) { if(backendType == BackendType.NONE) return configHelper + .collectValidationFailures() .withMandatoryProperty('changeManagement/clientOpts') .withMandatoryProperty('changeManagement/credentialsId') .withMandatoryProperty('changeManagement/endpoint') .withMandatoryProperty('changeManagement/git/to') .withMandatoryProperty('changeManagement/git/from') .withMandatoryProperty('changeManagement/git/format') + .withMandatoryProperty('changeManagement/rfc/developmentInstance', null, { backendType == BackendType.RFC}) + .withMandatoryProperty('changeManagement/rfc/developmentClient', null, { backendType == BackendType.RFC}) + .withMandatoryProperty('verbose', null, { backendType == BackendType.RFC}) configuration = configHelper.use() @@ -89,13 +94,44 @@ void call(parameters = [:]) { echo closingMessage.join() try { - cm.releaseTransportRequest(backendType, - configuration.changeDocumentId, - configuration.transportRequestId, - configuration.changeManagement.endpoint, - configuration.changeManagement.credentialsId, - configuration.changeManagement.clientOpts) + switch(backendType) { + + case BackendType.SOLMAN: + + cm.releaseTransportRequestSOLMAN( + configuration.changeDocumentId, + configuration.transportRequestId, + configuration.changeManagement.endpoint, + configuration.changeManagement.credentialsId, + configuration.changeManagement.clientOpts) + break + + case BackendType.CTS: + + cm.releaseTransportRequestCTS( + configuration.transportRequestId, + configuration.changeManagement.endpoint, + configuration.changeManagement.credentialsId, + configuration.changeManagement.clientOpts) + break + + case BackendType.RFC: + + cm.releaseTransportRequestRFC( + configuration.changeManagement.rfc.docker, + configuration.transportRequestId, + configuration.changeManagement.endpoint, + configuration.changeManagement.rfc.developmentInstance, + configuration.changeManagement.rfc.developmentClient, + configuration.changeManagement.credentialsId, + configuration.verbose) + break + + default: + + throw new IllegalArgumentException("Invalid backend type: '${backendType}'.") + } } catch(ChangeManagementException ex) { throw new AbortException(ex.getMessage()) } diff --git a/vars/transportRequestUploadFile.groovy b/vars/transportRequestUploadFile.groovy index ceae10712..ec853b70b 100644 --- a/vars/transportRequestUploadFile.groovy +++ b/vars/transportRequestUploadFile.groovy @@ -21,12 +21,19 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati ] @Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([ - 'applicationId' + 'applicationName', // RFC + 'applicationId', // SOLMAN + 'applicationDescription', + 'filePath', // SOLMAN, CTS + 'applicationUrl', // RFC + 'abapPackage', + 'codePage', //RFC + 'acceptUnixStyleLineEndings', // RFC + 'verbose', // RFC ]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([ 'changeDocumentId', - 'filePath', 'transportRequestId']) void call(parameters = [:]) { @@ -51,6 +58,7 @@ void call(parameters = [:]) { if(backendType == BackendType.NONE) return configHelper + .collectValidationFailures() .withMandatoryProperty('changeManagement/changeDocumentLabel') .withMandatoryProperty('changeManagement/clientOpts') .withMandatoryProperty('changeManagement/credentialsId') @@ -59,7 +67,22 @@ void call(parameters = [:]) { .withMandatoryProperty('changeManagement/git/from') .withMandatoryProperty('changeManagement/git/to') .withMandatoryProperty('changeManagement/git/format') - .withMandatoryProperty('filePath') + .withMandatoryProperty('filePath', null, { backendType in [BackendType.SOLMAN, BackendType.CTS] }) + .withMandatoryProperty('applicationUrl', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('codePage', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('acceptUnixStyleLineEndings', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('changeManagement/rfc/developmentInstance', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('changeManagement/rfc/developmentClient', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('changeManagement/rfc/docker/image', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('changeManagement/rfc/docker/options', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('changeManagement/rfc/docker/envVars', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('changeManagement/rfc/docker/pullImage', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('applicationDescription', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('abapPackage', null, { backendType == BackendType.RFC }) + .withMandatoryProperty('applicationId', null, {backendType == BackendType.SOLMAN}) + .withMandatoryProperty('applicationName', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('failOnWarning', null, {backendType == BackendType.RFC}) + .withMandatoryProperty('verbose', null, {backendType == BackendType.RFC}) new Utils().pushToSWA([ step: STEP_NAME, @@ -85,14 +108,15 @@ void call(parameters = [:]) { configHelper .withMandatoryProperty('changeDocumentId', "Change document id not provided (parameter: \'changeDocumentId\' or via commit history).") - .withMandatoryProperty('applicationId') } configuration = configHelper .withMandatoryProperty('transportRequestId', "Transport request id not provided (parameter: \'transportRequestId\' or via commit history).") .use() - def uploadingMessage = ["[INFO] Uploading file '${configuration.filePath}' to transport request '${configuration.transportRequestId}'"] + def uploadingMessage = ['[INFO] Uploading file ' + + "'${backendType == BackendType.RFC ? configuration.applicationUrl : configuration.filePath}' " + + "to transport request '${configuration.transportRequestId}'"] if(backendType == BackendType.SOLMAN) uploadingMessage << " of change document '${configuration.changeDocumentId}'" uploadingMessage << '.' @@ -101,22 +125,55 @@ void call(parameters = [:]) { try { + switch(backendType) { - cm.uploadFileToTransportRequest(backendType, - configuration.changeDocumentId, - configuration.transportRequestId, - configuration.applicationId, - configuration.filePath, - configuration.changeManagement.endpoint, - configuration.changeManagement.credentialsId, - configuration.changeManagement.clientOpts) + case BackendType.SOLMAN: + cm.uploadFileToTransportRequestSOLMAN( + configuration.changeDocumentId, + configuration.transportRequestId, + configuration.applicationId, + configuration.filePath, + configuration.changeManagement.endpoint, + configuration.changeManagement.credentialsId, + configuration.changeManagement.clientOpts) + break + case BackendType.CTS: + cm.uploadFileToTransportRequestCTS( + configuration.transportRequestId, + configuration.filePath, + configuration.changeManagement.endpoint, + configuration.changeManagement.credentialsId, + configuration.changeManagement.clientOpts) + break + case BackendType.RFC: + + cm.uploadFileToTransportRequestRFC( + configuration.changeManagement.rfc.docker ?: [], + configuration.transportRequestId, + configuration.applicationName, + configuration.applicationUrl, + configuration.changeManagement.endpoint, + configuration.changeManagement.credentialsId, + configuration.changeManagement.rfc.developmentInstance, + configuration.changeManagement.rfc.developmentClient, + configuration.applicationDescription, + configuration.abapPackage, + configuration.codePage, + configuration.acceptUnixStyleLineEndings, + configuration.failOnWarning, + configuration.verbose + ) + + break + + } } catch(ChangeManagementException ex) { throw new AbortException(ex.getMessage()) } - def uploadedMessage = ["[INFO] File '${configuration.filePath}' has been successfully uploaded to transport request '${configuration.transportRequestId}'"] + def uploadedMessage = ["[INFO] File '${backendType == BackendType.RFC ? configuration.applicationUrl : configuration.filePath}' has been successfully uploaded to transport request '${configuration.transportRequestId}'"] if(backendType == BackendType.SOLMAN) uploadedMessage << " of change document '${configuration.changeDocumentId}'" uploadedMessage << '.' diff --git a/vars/uiVeri5ExecuteTests.groovy b/vars/uiVeri5ExecuteTests.groovy index 68a30a91f..e82eb9ee3 100644 --- a/vars/uiVeri5ExecuteTests.groovy +++ b/vars/uiVeri5ExecuteTests.groovy @@ -74,7 +74,12 @@ import static com.sap.piper.Prerequisites.checkScript /** * With `testRepository` the tests can be loaded from another reposirory. */ - 'testRepository' + 'testRepository', + /** + * The `testServerUrl` is passed as environment variable `TARGET_SERVER_URL` to the test execution. + * The tests should read the host information from this environment variable in order to be infrastructure agnostic. + */ + 'testServerUrl' ]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS @@ -123,6 +128,7 @@ void call(Map parameters = [:]) { config.stashContent = config.testRepository ? [GitUtils.handleTestRepository(this, config)] : utils.unstashAll(config.stashContent) config.installCommand = SimpleTemplateEngine.newInstance().createTemplate(config.installCommand).make([config: config]).toString() config.runCommand = SimpleTemplateEngine.newInstance().createTemplate(config.runCommand).make([config: config]).toString() + config.dockerEnvVars.TARGET_SERVER_URL = config.dockerEnvVars.TARGET_SERVER_URL ?: config.testServerUrl seleniumExecuteTests( script: script,