github.com/jaylevin/jenkins-library@v1.230.4/vars/piperExecuteBin.groovy (about) 1 import com.sap.piper.BashUtils 2 import com.sap.piper.DebugReport 3 import com.sap.piper.DefaultValueCache 4 import com.sap.piper.JenkinsUtils 5 import com.sap.piper.MapUtils 6 import com.sap.piper.PiperGoUtils 7 import com.sap.piper.Utils 8 import com.sap.piper.analytics.InfluxData 9 import groovy.transform.Field 10 11 import static com.sap.piper.Prerequisites.checkScript 12 13 @Field String STEP_NAME = getClass().getName() 14 15 void call(Map parameters = [:], String stepName, String metadataFile, List credentialInfo, boolean failOnMissingReports = false, boolean failOnMissingLinks = false, boolean failOnError = false) { 16 17 Map handlePipelineStepErrorsParameters = [stepName: stepName, stepParameters: parameters] 18 if (failOnError) { 19 handlePipelineStepErrorsParameters.failOnError = true 20 } 21 22 handlePipelineStepErrors(handlePipelineStepErrorsParameters) { 23 Script script = checkScript(this, parameters) ?: this 24 def jenkinsUtils = parameters.jenkinsUtilsStub ?: new JenkinsUtils() 25 def utils = parameters.juStabUtils ?: new Utils() 26 27 String piperGoPath = parameters.piperGoPath ?: './piper' 28 29 prepareExecution(script, utils, parameters) 30 prepareMetadataResource(script, metadataFile) 31 Map stepParameters = prepareStepParameters(parameters) 32 echo "Step params $stepParameters" 33 34 withEnv([ 35 "PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(stepParameters)}", 36 "PIPER_correlationID=${env.BUILD_URL}", 37 //ToDo: check if parameters make it into docker image on JaaS 38 ]) { 39 String defaultConfigArgs = getCustomDefaultConfigsArg() 40 String customConfigArg = getCustomConfigArg(script) 41 42 echo "PIPER_parametersJSON: ${groovy.json.JsonOutput.toJson(stepParameters)}" 43 44 // get context configuration 45 Map config 46 handleErrorDetails(stepName) { 47 config = getStepContextConfig(script, piperGoPath, metadataFile, defaultConfigArgs, customConfigArg) 48 echo "Context Config: ${config}" 49 } 50 51 // prepare stashes 52 // first eliminate empty stashes 53 config.stashContent = utils.unstashAll(config.stashContent) 54 // then make sure that commonPipelineEnvironment, config, ... is also available when step stashing is active 55 if (config.stashContent?.size() > 0) { 56 config.stashContent.add('pipelineConfigAndTests') 57 config.stashContent.add('piper-bin') 58 config.stashContent.add('pipelineStepReports') 59 } 60 61 if (parameters.stashNoDefaultExcludes) { 62 // Merge this parameter which is only relevant in Jenkins context 63 // (for dockerExecuteOnKubernetes step) and go binary doesn't know about 64 config.stashNoDefaultExcludes = parameters.stashNoDefaultExcludes 65 } 66 67 dockerWrapper(script, stepName, config) { 68 handleErrorDetails(stepName) { 69 writePipelineEnv(script: script, piperGoPath: piperGoPath) 70 utils.unstash('pipelineStepReports') 71 try { 72 try { 73 try { 74 credentialWrapper(config, credentialInfo) { 75 sh "${piperGoPath} ${stepName}${defaultConfigArgs}${customConfigArg}" 76 } 77 } finally { 78 jenkinsUtils.handleStepResults(stepName, failOnMissingReports, failOnMissingLinks) 79 } 80 } finally { 81 readPipelineEnv(script: script, piperGoPath: piperGoPath) 82 } 83 } finally { 84 InfluxData.readFromDisk(script) 85 stash name: 'pipelineStepReports', includes: '.pipeline/stepReports/**', allowEmpty: true 86 } 87 } 88 } 89 } 90 } 91 } 92 93 // reused in sonarExecuteScan 94 static void prepareExecution(Script script, Utils utils, Map parameters = [:]) { 95 def piperGoUtils = parameters.piperGoUtils ?: new PiperGoUtils(script, utils) 96 piperGoUtils.unstashPiperBin() 97 utils.unstash('pipelineConfigAndTests') 98 } 99 100 // reused in sonarExecuteScan 101 static Map prepareStepParameters(Map parameters) { 102 Map stepParameters = [:].plus(parameters) 103 104 stepParameters.remove('script') 105 stepParameters.remove('jenkinsUtilsStub') 106 stepParameters.remove('piperGoPath') 107 stepParameters.remove('juStabUtils') 108 stepParameters.remove('piperGoUtils') 109 110 // When converting to JSON and back again, entries which had a 'null' value will now have a value 111 // of type 'net.sf.json.JSONNull', for which the Groovy Truth resolves to 'true' in for example if-conditions 112 return MapUtils.pruneNulls(stepParameters) 113 } 114 115 // reused in sonarExecuteScan 116 static void prepareMetadataResource(Script script, String metadataFile) { 117 script.writeFile(file: ".pipeline/tmp/${metadataFile}", text: script.libraryResource(metadataFile)) 118 } 119 120 // reused in sonarExecuteScan 121 static Map getStepContextConfig(Script script, String piperGoPath, String metadataFile, String defaultConfigArgs, String customConfigArg) { 122 return script.readJSON(text: script.sh(returnStdout: true, script: "${piperGoPath} getConfig --contextConfig --stepMetadata '.pipeline/tmp/${metadataFile}'${defaultConfigArgs}${customConfigArg}")) 123 } 124 125 static String getCustomDefaultConfigs() { 126 // The default config files were extracted from merged library 127 // resources by setupCommonPipelineEnvironment.groovy into .pipeline/. 128 List customDefaults = DefaultValueCache.getInstance().getCustomDefaults() 129 for (int i = 0; i < customDefaults.size(); i++) { 130 customDefaults[i] = BashUtils.quoteAndEscape(".pipeline/${customDefaults[i]}") 131 } 132 return customDefaults.join(',') 133 } 134 135 // reused in sonarExecuteScan 136 static String getCustomDefaultConfigsArg() { 137 String customDefaults = getCustomDefaultConfigs() 138 if (customDefaults) { 139 return " --defaultConfig ${customDefaults} --ignoreCustomDefaults" 140 } 141 return '' 142 } 143 144 // reused in sonarExecuteScan 145 static String getCustomConfigArg(def script) { 146 if (script?.commonPipelineEnvironment?.configurationFile 147 && script.commonPipelineEnvironment.configurationFile != '.pipeline/config.yml' 148 && script.commonPipelineEnvironment.configurationFile != '.pipeline/config.yaml') { 149 return " --customConfig ${BashUtils.quoteAndEscape(script.commonPipelineEnvironment.configurationFile)}" 150 } 151 return '' 152 } 153 154 // reused in sonarExecuteScan 155 void dockerWrapper(script, stepName, config, body) { 156 if (config.dockerImage) { 157 echo "[INFO] executing pipeline step '${stepName}' with docker image '${config.dockerImage}'" 158 Map dockerExecuteParameters = [:].plus(config) 159 dockerExecuteParameters.script = script 160 dockerExecute(dockerExecuteParameters) { 161 body() 162 } 163 } else { 164 body() 165 } 166 } 167 168 // reused in sonarExecuteScan 169 void credentialWrapper(config, List credentialInfo, body) { 170 credentialInfo = handleVaultCredentials(config, credentialInfo) 171 172 if (credentialInfo.size() > 0) { 173 def creds = [] 174 def sshCreds = [] 175 credentialInfo.each { cred -> 176 def credentialsId 177 if (cred.resolveCredentialsId == false) { 178 credentialsId = cred.id 179 } else { 180 credentialsId = config[cred.id] 181 } 182 if (credentialsId) { 183 switch (cred.type) { 184 case "file": 185 creds.add(file(credentialsId: credentialsId, variable: cred.env[0])) 186 break 187 case "token": 188 creds.add(string(credentialsId: credentialsId, variable: cred.env[0])) 189 break 190 case "usernamePassword": 191 creds.add(usernamePassword(credentialsId: credentialsId, usernameVariable: cred.env[0], passwordVariable: cred.env[1])) 192 break 193 case "ssh": 194 sshCreds.add(credentialsId) 195 break 196 default: 197 error("invalid credential type: ${cred.type}") 198 } 199 } 200 } 201 202 // remove credentialIds that were probably defaulted and which are not present in jenkins 203 if (containsVaultConfig(config)) { 204 creds = removeMissingCredentials(creds, config) 205 sshCreds = removeMissingCredentials(sshCreds, config) 206 } 207 208 if (sshCreds.size() > 0) { 209 sshagent (sshCreds) { 210 withCredentials(creds) { 211 body() 212 } 213 } 214 } else { 215 withCredentials(creds) { 216 body() 217 } 218 } 219 } else { 220 body() 221 } 222 } 223 224 List removeMissingCredentials(List creds, Map config) { 225 return creds.findAll { credentialExists(it, config) } 226 } 227 228 boolean credentialExists(cred, Map config) { 229 try { 230 withCredentials([cred]) { 231 return true 232 } 233 } catch (e) { 234 return false 235 } 236 } 237 238 boolean containsVaultConfig(Map config) { 239 def approleIsUsed = config.containsKey('vaultAppRoleTokenCredentialsId') && config.containsKey('vaultAppRoleSecretTokenCredentialsId') 240 def tokenIsUsed = config.containsKey('vaultTokenCredentialsId') 241 242 return approleIsUsed || tokenIsUsed 243 } 244 245 // Injects vaultCredentials if steps supports resolving parameters from vault 246 List handleVaultCredentials(config, List credentialInfo) { 247 if (config.containsKey('vaultAppRoleTokenCredentialsId') && config.containsKey('vaultAppRoleSecretTokenCredentialsId')) { 248 credentialInfo += [[type: 'token', id: 'vaultAppRoleTokenCredentialsId', env: ['PIPER_vaultAppRoleID']], 249 [type: 'token', id: 'vaultAppRoleSecretTokenCredentialsId', env: ['PIPER_vaultAppRoleSecretID']]] 250 } 251 252 if (config.containsKey('vaultTokenCredentialsId')) { 253 credentialInfo += [[type: 'token', id: 'vaultTokenCredentialsId', env: ['PIPER_vaultToken']]] 254 } 255 256 return credentialInfo 257 } 258 259 // reused in sonarExecuteScan 260 void handleErrorDetails(String stepName, Closure body) { 261 try { 262 body() 263 } catch (ex) { 264 def errorDetailsFileName = "${stepName}_errorDetails.json" 265 if (fileExists(file: errorDetailsFileName)) { 266 def errorDetails = readJSON(file: errorDetailsFileName) 267 def errorCategory = "" 268 if (errorDetails.category) { 269 errorCategory = " (category: ${errorDetails.category})" 270 DebugReport.instance.failedBuild.category = errorDetails.category 271 } 272 error "[${stepName}] Step execution failed${errorCategory}. Error: ${errorDetails.error ?: errorDetails.message}" 273 } 274 error "[${stepName}] Step execution failed. Error: ${ex}, please see log file for more details." 275 } 276 }