mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
500c42860f
We should use Utils.stash instead of native steps.stash calls (Jenkins) since important logging is missing. The default Jenkins stash step does not log any metadata like stash name, patterns, etc.
315 lines
13 KiB
Groovy
315 lines
13 KiB
Groovy
import com.sap.piper.BashUtils
|
|
import com.sap.piper.DebugReport
|
|
import com.sap.piper.DefaultValueCache
|
|
import com.sap.piper.JenkinsUtils
|
|
import com.sap.piper.MapUtils
|
|
import com.sap.piper.PiperGoUtils
|
|
import com.sap.piper.Utils
|
|
import com.sap.piper.analytics.InfluxData
|
|
import groovy.transform.Field
|
|
|
|
import static com.sap.piper.Prerequisites.checkScript
|
|
|
|
@Field String STEP_NAME = getClass().getName()
|
|
|
|
void call(Map parameters = [:], String stepName, String metadataFile, List credentialInfo, boolean failOnMissingReports = false, boolean failOnMissingLinks = false, boolean failOnError = false) {
|
|
|
|
Map handlePipelineStepErrorsParameters = [stepName: stepName, stepParameters: parameters]
|
|
if (failOnError) {
|
|
handlePipelineStepErrorsParameters.failOnError = true
|
|
}
|
|
|
|
handlePipelineStepErrors(handlePipelineStepErrorsParameters) {
|
|
Script script = checkScript(this, parameters) ?: this
|
|
def jenkinsUtils = parameters.jenkinsUtilsStub ?: new JenkinsUtils()
|
|
def utils = parameters.juStabUtils ?: new Utils()
|
|
|
|
String piperGoPath = parameters.piperGoPath ?: './piper'
|
|
|
|
prepareExecution(script, utils, parameters)
|
|
prepareMetadataResource(script, metadataFile)
|
|
Map stepParameters = prepareStepParameters(parameters)
|
|
echo "Step params $stepParameters"
|
|
|
|
withEnv([
|
|
"PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(stepParameters)}",
|
|
"PIPER_correlationID=${env.BUILD_URL}",
|
|
//ToDo: check if parameters make it into docker image on JaaS
|
|
]) {
|
|
String defaultConfigArgs = getCustomDefaultConfigsArg()
|
|
String customConfigArg = getCustomConfigArg(script)
|
|
|
|
echo "PIPER_parametersJSON: ${groovy.json.JsonOutput.toJson(stepParameters)}"
|
|
|
|
// get context configuration
|
|
Map config
|
|
handleErrorDetails(stepName) {
|
|
config = getStepContextConfig(script, piperGoPath, metadataFile, defaultConfigArgs, customConfigArg)
|
|
echo "Context Config: ${config}"
|
|
}
|
|
|
|
//Add ANS credential information to the config
|
|
ansHookServiceKeyCredentialsId =
|
|
DefaultValueCache.getInstance().getDefaultValues().hooks?.ans?.serviceKeyCredentialsId
|
|
config += ["ansHookServiceKeyCredentialsId": ansHookServiceKeyCredentialsId]
|
|
|
|
// prepare stashes
|
|
// first eliminate non existing stashes
|
|
config.stashContent = utils.unstashAll(config.stashContent)
|
|
// then make sure that commonPipelineEnvironment, config, ... is also available when step stashing is active
|
|
if (config.stashContent?.size() > 0) {
|
|
config.stashContent.add('pipelineConfigAndTests')
|
|
config.stashContent.add('piper-bin')
|
|
config.stashContent.add('pipelineStepReports')
|
|
}
|
|
|
|
if (parameters.stashNoDefaultExcludes) {
|
|
// Merge this parameter which is only relevant in Jenkins context
|
|
// (for dockerExecuteOnKubernetes step) and go binary doesn't know about
|
|
config.stashNoDefaultExcludes = parameters.stashNoDefaultExcludes
|
|
}
|
|
|
|
dockerWrapper(script, stepName, config) {
|
|
handleErrorDetails(stepName) {
|
|
writePipelineEnv(script: script, piperGoPath: piperGoPath)
|
|
utils.unstash('pipelineStepReports')
|
|
try {
|
|
try {
|
|
try {
|
|
credentialWrapper(config, credentialInfo) {
|
|
sh "${piperGoPath} ${stepName}${defaultConfigArgs}${customConfigArg}"
|
|
}
|
|
} finally {
|
|
jenkinsUtils.handleStepResults(stepName, failOnMissingReports, failOnMissingLinks)
|
|
}
|
|
} finally {
|
|
readPipelineEnv(script: script, piperGoPath: piperGoPath)
|
|
}
|
|
} finally {
|
|
InfluxData.readFromDisk(script)
|
|
utils.stash name: 'pipelineStepReports', includes: '.pipeline/stepReports/**', allowEmpty: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
static void prepareExecution(Script script, Utils utils, Map parameters = [:]) {
|
|
def piperGoUtils = parameters.piperGoUtils ?: new PiperGoUtils(script, utils)
|
|
piperGoUtils.unstashPiperBin()
|
|
utils.unstash('pipelineConfigAndTests')
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
static Map prepareStepParameters(Map parameters) {
|
|
Map stepParameters = [:].plus(parameters)
|
|
|
|
stepParameters.remove('script')
|
|
stepParameters.remove('jenkinsUtilsStub')
|
|
stepParameters.remove('piperGoPath')
|
|
stepParameters.remove('juStabUtils')
|
|
stepParameters.remove('piperGoUtils')
|
|
|
|
// When converting to JSON and back again, entries which had a 'null' value will now have a value
|
|
// of type 'net.sf.json.JSONNull', for which the Groovy Truth resolves to 'true' in for example if-conditions
|
|
return MapUtils.pruneNulls(stepParameters)
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
static void prepareMetadataResource(Script script, String metadataFile) {
|
|
script.writeFile(file: ".pipeline/tmp/${metadataFile}", text: script.libraryResource(metadataFile))
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
static Map getStepContextConfig(Script script, String piperGoPath, String metadataFile, String defaultConfigArgs, String customConfigArg) {
|
|
return script.readJSON(text: script.sh(returnStdout: true, script: "${piperGoPath} getConfig --contextConfig --stepMetadata '.pipeline/tmp/${metadataFile}'${defaultConfigArgs}${customConfigArg}"))
|
|
}
|
|
|
|
static String getCustomDefaultConfigs() {
|
|
// The default config files were extracted from merged library
|
|
// resources by setupCommonPipelineEnvironment.groovy into .pipeline/.
|
|
List customDefaults = DefaultValueCache.getInstance().getCustomDefaults()
|
|
for (int i = 0; i < customDefaults.size(); i++) {
|
|
customDefaults[i] = BashUtils.quoteAndEscape(".pipeline/${customDefaults[i]}")
|
|
}
|
|
return customDefaults.join(',')
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
static String getCustomDefaultConfigsArg() {
|
|
String customDefaults = getCustomDefaultConfigs()
|
|
if (customDefaults) {
|
|
return " --defaultConfig ${customDefaults} --ignoreCustomDefaults"
|
|
}
|
|
return ''
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
static String getCustomConfigArg(def script) {
|
|
if (script?.commonPipelineEnvironment?.configurationFile
|
|
&& script.commonPipelineEnvironment.configurationFile != '.pipeline/config.yml'
|
|
&& script.commonPipelineEnvironment.configurationFile != '.pipeline/config.yaml') {
|
|
return " --customConfig ${BashUtils.quoteAndEscape(script.commonPipelineEnvironment.configurationFile)}"
|
|
}
|
|
return ''
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
void dockerWrapper(script, stepName, config, body) {
|
|
if (config.dockerImage) {
|
|
echo "[INFO] executing pipeline step '${stepName}' with docker image '${config.dockerImage}'"
|
|
Map dockerExecuteParameters = [:].plus(config)
|
|
dockerExecuteParameters.script = script
|
|
dockerExecute(dockerExecuteParameters) {
|
|
body()
|
|
}
|
|
} else {
|
|
body()
|
|
}
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
void credentialWrapper(config, List credentialInfo, body) {
|
|
credentialInfo = handleVaultCredentials(config, credentialInfo)
|
|
credentialInfo = handleANSCredentials(config, credentialInfo)
|
|
if (credentialInfo.size() > 0) {
|
|
def creds = []
|
|
def sshCreds = []
|
|
credentialInfo.each { cred ->
|
|
def credentialsId
|
|
if (cred.resolveCredentialsId == false) {
|
|
credentialsId = cred.id
|
|
} else {
|
|
credentialsId = config[cred.id]
|
|
}
|
|
if (credentialsId) {
|
|
switch (cred.type) {
|
|
case "file":
|
|
creds.add(file(credentialsId: credentialsId, variable: cred.env[0]))
|
|
break
|
|
case "token":
|
|
creds.add(string(credentialsId: credentialsId, variable: cred.env[0]))
|
|
break
|
|
case "usernamePassword":
|
|
creds.add(usernamePassword(credentialsId: credentialsId, usernameVariable: cred.env[0], passwordVariable: cred.env[1]))
|
|
break
|
|
case "ssh":
|
|
sshCreds.add(credentialsId)
|
|
break
|
|
default:
|
|
error("invalid credential type: ${cred.type}")
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove credentialIds that were probably defaulted and which are not present in jenkins
|
|
if (containsVaultConfig(config)) {
|
|
creds = removeMissingCredentials(creds, config)
|
|
sshCreds = removeMissingCredentials(sshCreds, config)
|
|
}
|
|
|
|
if (sshCreds.size() > 0) {
|
|
sshagent (sshCreds) {
|
|
withCredentials(creds) {
|
|
body()
|
|
}
|
|
}
|
|
} else {
|
|
withCredentials(creds) {
|
|
body()
|
|
}
|
|
}
|
|
} else {
|
|
body()
|
|
}
|
|
}
|
|
|
|
List removeMissingCredentials(List creds, Map config) {
|
|
return creds.findAll { credentialExists(it, config) }
|
|
}
|
|
|
|
boolean credentialExists(cred, Map config) {
|
|
try {
|
|
withCredentials([cred]) {
|
|
return true
|
|
}
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
boolean containsVaultConfig(Map config) {
|
|
def approleIsUsed = config.containsKey('vaultAppRoleTokenCredentialsId') && config.containsKey('vaultAppRoleSecretTokenCredentialsId')
|
|
def tokenIsUsed = config.containsKey('vaultTokenCredentialsId')
|
|
|
|
return approleIsUsed || tokenIsUsed
|
|
}
|
|
|
|
// Injects vaultCredentials if steps supports resolving parameters from vault
|
|
List handleVaultCredentials(config, List credentialInfo) {
|
|
if (config.containsKey('vaultAppRoleTokenCredentialsId') && config.containsKey('vaultAppRoleSecretTokenCredentialsId')) {
|
|
credentialInfo += [[type: 'token', id: 'vaultAppRoleTokenCredentialsId', env: ['PIPER_vaultAppRoleID']],
|
|
[type: 'token', id: 'vaultAppRoleSecretTokenCredentialsId', env: ['PIPER_vaultAppRoleSecretID']]]
|
|
}
|
|
|
|
if (config.containsKey('vaultTokenCredentialsId')) {
|
|
credentialInfo += [[type: 'token', id: 'vaultTokenCredentialsId', env: ['PIPER_vaultToken']]]
|
|
}
|
|
|
|
return credentialInfo
|
|
}
|
|
|
|
List handleANSCredentials(config, List credentialInfo){
|
|
if (config.containsKey('ansHookServiceKeyCredentialsId')) {
|
|
credentialInfo += [[type: 'token', id: 'ansHookServiceKeyCredentialsId', env: ['PIPER_ansHookServiceKey']]]
|
|
}
|
|
|
|
return credentialInfo
|
|
}
|
|
|
|
// reused in sonarExecuteScan
|
|
void handleErrorDetails(String stepName, Closure body) {
|
|
try {
|
|
body()
|
|
} catch (ex) {
|
|
def errorDetailsFileName = "${stepName}_errorDetails.json"
|
|
if (fileExists(file: errorDetailsFileName)) {
|
|
def errorDetails = readJSON(file: errorDetailsFileName)
|
|
def errorCategory = ""
|
|
if (errorDetails.category) {
|
|
errorCategory = " (category: ${errorDetails.category})"
|
|
DebugReport.instance.failedBuild.category = errorDetails.category
|
|
}
|
|
error "[${stepName}] Step execution failed${errorCategory}. Error: ${errorDetails.error ?: errorDetails.message}"
|
|
}
|
|
error "[${stepName}] Step execution failed. Error: ${ex}, please see log file for more details."
|
|
}
|
|
}
|
|
// it can be used in 2 ways:
|
|
// 1. use stage, step arguments and leave stepOutputFile "" and stageOutputFile "" empty to check if this step is active in this stage (true means step is active, false - not active or something went wrong)
|
|
// 2. use stageOutputFile and/or stepOutputFile and leave step "" and stage "" to write the whole map of steps in all stages into a file[s] (true means no errors, false - something went wrong)
|
|
// if both presented - priority is option 2
|
|
static boolean checkIfStepActive(Map parameters = [:], Script script, String piperGoPath, String stageConfig = "", String stepOutputFile = "", String stageOutputFile = "", String stage = "", String step = "") {
|
|
def utils = parameters.juStabUtils ?: new Utils()
|
|
def piperGoUtils = parameters.piperGoUtils ?: new PiperGoUtils(script, utils)
|
|
def flags = "--stageConfig ${stageConfig} --useV1"
|
|
if (stageOutputFile) {
|
|
flags += " --stageOutputFile ${stageOutputFile}"
|
|
}
|
|
if (stepOutputFile) {
|
|
flags += " --stepOutputFile ${stepOutputFile}"
|
|
}
|
|
if (!stage || !step) {
|
|
stage = "_"
|
|
step = "_"
|
|
}
|
|
flags += " --stage ${stage} --step ${step}"
|
|
flags += getCustomDefaultConfigsArg()
|
|
flags += getCustomConfigArg(script)
|
|
piperGoUtils.unstashPiperBin()
|
|
def returnCode = script.sh(returnStatus: true, script: "${piperGoPath} checkIfStepActive ${flags}")
|
|
return (returnCode == 0)
|
|
}
|