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

196 lines
7.3 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.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 ${parameters.verbose ? '--verbose' :''} 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))
2019-12-13 17:05:55 +02:00
if(parameters.verbose) {
echo "[INFO] Context-Config: ${contextConfig}"
echo "[INFO] Project-Config: ${projectConfig}"
}
2019-12-13 17:05:55 +02:00
Action action = projectConfig.action
DeployMode mode = projectConfig.mode
// That config map here is only used in the groovy layer. Nothing is handed over to go.
Map config = contextConfig <<
[
apiUrl: projectConfig.apiUrl, // required on groovy level for acquire the lock
org: projectConfig.org, // required on groovy level for acquire the lock
space: projectConfig.space, // required on groovy level for acquire the lock
docker: [
dockerImage: contextConfig.dockerImage,
dockerPullImage: false
2019-12-13 17:05:55 +02:00
]
]
if(parameters.verbose) {
echo "[INFO] Merged-Config: ${config}"
}
2019-12-13 17:05:55 +02:00
def operationId
if(mode == DeployMode.BG_DEPLOY && action != Action.NONE) {
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-13 17:05:55 +02:00
lock(getLockIdentifier(config)) {
2019-12-13 17:05:55 +02:00
withCredentials([usernamePassword(
credentialsId: config.credentialsId,
passwordVariable: 'PASSWORD',
usernameVariable: 'USERNAME')]) {
2019-12-13 17:05:55 +02:00
dockerExecute([script: this].plus(config.docker)) {
xsDeployStdout = sh returnStdout: true, script: """#!/bin/bash
./piper ${parameters.verbose ? '--verbose' : ''} xsDeploy --defaultConfig ${configFiles} --user \${USERNAME} --password \${PASSWORD} ${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) {
def _e = ''
if(prefix.length() > 0) {
_e += prefix
_e += '/'
}
_e += e
_l << '"' + _e + '"'
}
_l.join(' ')
}