1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/vars/xsDeploy.groovy

206 lines
7.7 KiB
Groovy
Raw Normal View History

2019-12-13 17:05:55 +02:00
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.ConfigurationHelper
import com.sap.piper.DefaultValueCache
import com.sap.piper.JenkinsUtils
2019-12-13 17:05:55 +02:00
import com.sap.piper.PiperGoUtils
import com.sap.piper.GenerateDocumentation
import com.sap.piper.Utils
import groovy.transform.Field
2019-12-13 17:05:55 +02:00
@Field String METADATA_FILE = 'metadata/xsDeploy.yaml'
@Field String PIPER_DEFAULTS = 'default_pipeline_environment.yml'
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FOLDER = '.pipeline' // metadata file contains already the "metadata" folder level, hence we end up in a folder ".pipeline/metadata"
@Field String ADDITIONAL_CONFIGS_FOLDER='.pipeline/additionalConfigs'
enum DeployMode {
DEPLOY,
BG_DEPLOY,
NONE
String toString() {
name().toLowerCase(Locale.ENGLISH).replaceAll('_', '-')
}
}
enum Action {
RESUME,
ABORT,
RETRY,
NONE
String toString() {
name().toLowerCase(Locale.ENGLISH)
}
}
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
2019-12-13 17:05:55 +02:00
final script = checkScript(this, parameters) ?: null
2019-12-13 17:05:55 +02:00
if(! script) {
error "Reference to surrounding pipeline script not provided (script: this)."
}
2019-12-13 17:05:55 +02:00
def utils = parameters.juStabUtils ?: new Utils()
def piperGoUtils = parameters.piperGoUtils ?: new PiperGoUtils(utils)
//
// The parameters map in provided from outside. That map might be used elsewhere in the pipeline
// hence we should not modify it here. So we create a new map based on the parameters map.
parameters = [:] << parameters
// hard to predict how these parameters looks like in its serialized form. Anyhow it is better
2019-12-13 17:05:55 +02:00
// not to have these parameters forwarded somehow to the go layer.
parameters.remove('juStabUtils')
parameters.remove('piperGoUtils')
parameters.remove('script')
piperGoUtils.unstashPiperBin()
//
// Printing the piper-go version. Should not be done here, but somewhere during materializing
// the piper binary. As long as we don't have it elsewhere we should keep it here.
2019-12-13 17:05:55 +02:00
def piperGoVersion = sh(returnStdout: true, script: "./piper version")
echo "PiperGoVersion: ${piperGoVersion}"
//
// since there is no valid config provided (... null) telemetry is disabled (same for other go releated steps at the moment).
utils.pushToSWA([
step: STEP_NAME,
2019-12-13 17:05:55 +02:00
], null)
String configFiles = prepareConfigurations([PIPER_DEFAULTS].plus(script.commonPipelineEnvironment.getCustomDefaults()), ADDITIONAL_CONFIGS_FOLDER)
writeFile(file: "${METADATA_FOLDER}/${METADATA_FILE}", text: libraryResource(METADATA_FILE))
2019-12-13 17:05:55 +02:00
withEnv([
"PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(parameters)}",
]) {
2019-12-13 17:05:55 +02:00
//
// context config gives us e.g. the docker image name. --> How does this work for customer maintained images?
// There is a name provided in the metadata file. But we do not provide a docker image for that.
// The user has to build that for her/his own. How do we expect to configure this?
String projectConfigScript = "./piper getConfig --stepMetadata '${METADATA_FOLDER}/${METADATA_FILE}' --defaultConfig ${configFiles}"
String contextConfigScript = projectConfigScript + " --contextConfig"
Map projectConfig = readJSON (text: sh(returnStdout: true, script: projectConfigScript))
Map contextConfig = readJSON (text: sh(returnStdout: true, script: contextConfigScript))
Map dockerOptions = getDockerOptions(script.commonPipelineEnvironment)
def dockerImage = dockerOptions.dockerImage
def dockerPullImage = dockerOptions.dockerPullImage
if(dockerImage == null || dockerImage.toString().trim().isEmpty()) dockerImage = contextConfig.dockerImage
if(dockerPullImage == null || dockerPullImage.toString().trim().isEmpty()) dockerPullImage = contextConfig.dockerPullImage
2019-12-13 17:05:55 +02:00
Action action = projectConfig.action
DeployMode mode = projectConfig.mode
if(parameters.verbose) {
2019-12-20 15:22:39 +02:00
echo "[INFO] ContextConfig: ${contextConfig}"
echo "[INFO] ProjectConfig: ${projectConfig}"
2019-12-13 17:05:55 +02:00
}
2020-01-15 16:08:43 +02:00
def mtaFilePath = script.commonPipelineEnvironment.mtarFilePath
def operationId = parameters.operationId
if(! operationId && mode == DeployMode.BG_DEPLOY && action != Action.NONE) {
2019-12-13 17:05:55 +02:00
operationId = script.commonPipelineEnvironment.xsDeploymentId
if (! operationId) {
throw new IllegalArgumentException('No operationId provided. Was there a deployment before?')
}
}
2019-12-13 17:05:55 +02:00
def xsDeployStdout
2019-12-20 15:22:39 +02:00
lock(getLockIdentifier(projectConfig)) {
2019-12-13 17:05:55 +02:00
withCredentials([usernamePassword(
2019-12-20 15:22:39 +02:00
credentialsId: contextConfig.credentialsId,
2019-12-13 17:05:55 +02:00
passwordVariable: 'PASSWORD',
usernameVariable: 'USERNAME')]) {
dockerExecute([script: this].plus([dockerImage: dockerImage, dockerPullImage: dockerPullImage])) {
2019-12-13 17:05:55 +02:00
xsDeployStdout = sh returnStdout: true, script: """#!/bin/bash
2020-01-15 16:08:43 +02:00
./piper xsDeploy --defaultConfig ${configFiles} --user \${USERNAME} --password \${PASSWORD} ${mtaFilePath ? '--mtaPath ' + mtaFilePath : ''} ${operationId ? '--operationId ' + operationId : ''}
2019-12-13 17:05:55 +02:00
"""
}
}
}
2019-12-13 17:05:55 +02:00
if(mode == DeployMode.BG_DEPLOY && action == Action.NONE) {
script.commonPipelineEnvironment.xsDeploymentId = readJSON(text: xsDeployStdout).operationId
if (!script.commonPipelineEnvironment.xsDeploymentId) {
error "No Operation id returned from xs deploy step. This is required for mode '${mode}' and action '${action}'."
}
echo "[INFO] OperationId for subsequent resume or abort: '${script.commonPipelineEnvironment.xsDeploymentId}'."
}
}
}
}
String getLockIdentifier(Map config) {
"$STEP_NAME:${config.apiUrl}:${config.org}:${config.space}"
}
/*
* The returned string can be used directly in the command line for retrieving the configuration via go
*/
String prepareConfigurations(List configs, String configCacheFolder) {
for(def customDefault : configs) {
writeFile(file: "${ADDITIONAL_CONFIGS_FOLDER}/${customDefault}", text: libraryResource(customDefault))
}
joinAndQuote(configs.reverse(), configCacheFolder)
}
/*
* prefix is supposed to be provided without trailing slash
*/
String joinAndQuote(List l, String prefix = '') {
_l = []
if(prefix == null) {
prefix = ''
}
if(prefix.endsWith('/') || prefix.endsWith('\\'))
throw new IllegalArgumentException("Provide prefix (${prefix}) without trailing slash")
for(def e : l) {
2020-01-07 13:40:58 +02:00
def _e = ''
if(prefix.length() > 0) {
_e += prefix
_e += '/'
}
_e += e
_l << '"' + _e + '"'
}
_l.join(' ')
}
//
// ugly backward compatibility handling
// retrieves docker options from project config or from landscape config layer(s)
//
Map getDockerOptions(def cpe) {
Set configKeys = ['docker']
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(cpe, configKeys)
.mixinStepConfig(cpe, configKeys)
.mixinStageConfig(cpe, env.STAGE_NAME, configKeys)
.use()
[dockerImage: config?.docker.dockerImage, dockerPullImage: config?.docker.dockerPullImage]
}