2020-03-30 14:31:24 +02:00
import com.sap.piper.BashUtils
2020-04-28 07:42:02 +02:00
import com.sap.piper.DebugReport
2020-03-30 14:31:24 +02:00
import com.sap.piper.DefaultValueCache
2020-03-17 09:19:09 +01:00
import com.sap.piper.JenkinsUtils
2020-04-24 10:41:49 +02:00
import com.sap.piper.MapUtils
2020-03-17 09:19:09 +01:00
import com.sap.piper.PiperGoUtils
import com.sap.piper.Utils
2021-03-10 16:00:53 +01:00
import com.sap.piper.analytics.InfluxData
2020-05-05 14:31:12 +02:00
import groovy.transform.Field
2020-03-17 09:19:09 +01:00
import static com . sap . piper . Prerequisites . checkScript
2020-05-05 14:31:12 +02:00
@Field String STEP_NAME = getClass ( ) . getName ( )
2020-06-24 17:04:58 +02:00
void call ( Map parameters = [ : ] , String stepName , String metadataFile , List credentialInfo , boolean failOnMissingReports = false , boolean failOnMissingLinks = false , boolean failOnError = false ) {
2020-03-17 09:19:09 +01:00
2021-01-11 10:49:33 +01:00
Map handlePipelineStepErrorsParameters = [ stepName: stepName , stepParameters: parameters ]
2020-04-30 10:39:27 +02:00
if ( failOnError ) {
handlePipelineStepErrorsParameters . failOnError = true
}
handlePipelineStepErrors ( handlePipelineStepErrorsParameters ) {
2020-06-24 17:04:58 +02:00
Script script = checkScript ( this , parameters ) ? : this
2020-03-17 09:19:09 +01:00
def jenkinsUtils = parameters . jenkinsUtilsStub ? : new JenkinsUtils ( )
2020-07-02 12:08:56 +02:00
def utils = parameters . juStabUtils ? : new Utils ( )
2020-06-24 17:04:58 +02:00
String piperGoPath = parameters . piperGoPath ? : './piper'
2020-05-19 16:49:21 +02:00
2020-07-02 12:08:56 +02:00
prepareExecution ( script , utils , parameters )
2020-06-24 17:04:58 +02:00
prepareMetadataResource ( script , metadataFile )
Map stepParameters = prepareStepParameters ( parameters )
2021-02-15 09:48:51 +01:00
echo "Step params $stepParameters"
2020-04-24 10:41:49 +02:00
2020-03-17 09:19:09 +01:00
withEnv ( [
"PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(stepParameters)}" ,
2020-04-28 07:42:02 +02:00
"PIPER_correlationID=${env.BUILD_URL}" ,
2020-03-17 09:19:09 +01:00
//ToDo: check if parameters make it into docker image on JaaS
] ) {
2020-03-31 13:10:02 +02:00
String defaultConfigArgs = getCustomDefaultConfigsArg ( )
String customConfigArg = getCustomConfigArg ( script )
2020-05-06 17:43:32 +02:00
echo "PIPER_parametersJSON: ${groovy.json.JsonOutput.toJson(stepParameters)}"
2020-03-17 09:19:09 +01:00
// get context configuration
2020-05-26 07:58:03 +02:00
Map config
handleErrorDetails ( stepName ) {
2020-06-24 17:04:58 +02:00
config = getStepContextConfig ( script , piperGoPath , metadataFile , defaultConfigArgs , customConfigArg )
2020-05-26 07:58:03 +02:00
echo "Context Config: ${config}"
}
2020-03-17 09:19:09 +01:00
2022-06-22 13:31:17 +02:00
//Add ANS credential information to the config
ansHookServiceKeyCredentialsId =
DefaultValueCache . getInstance ( ) . getDefaultValues ( ) . hooks ? . ans ? . serviceKeyCredentialsId
config + = [ "ansHookServiceKeyCredentialsId" : ansHookServiceKeyCredentialsId ]
2020-07-02 12:08:56 +02:00
// prepare stashes
2023-06-01 15:22:57 +02:00
// first eliminate non existing stashes
2020-07-02 12:08:56 +02:00
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' )
2020-07-06 13:22:36 +02:00
config . stashContent . add ( 'piper-bin' )
2021-04-15 07:45:06 +02:00
config . stashContent . add ( 'pipelineStepReports' )
2020-07-02 12:08:56 +02:00
}
2020-06-08 17:08:05 +02:00
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
}
2020-10-01 16:27:50 +02:00
dockerWrapper ( script , stepName , config ) {
2020-04-28 07:42:02 +02:00
handleErrorDetails ( stepName ) {
2021-06-15 14:34:56 +02:00
writePipelineEnv ( script: script , piperGoPath: piperGoPath )
2021-04-15 07:45:06 +02:00
utils . unstash ( 'pipelineStepReports' )
2021-03-12 09:42:19 +01:00
try {
2021-03-10 16:00:53 +01:00
try {
2021-03-12 09:42:19 +01:00
try {
credentialWrapper ( config , credentialInfo ) {
sh "${piperGoPath} ${stepName}${defaultConfigArgs}${customConfigArg}"
}
} finally {
jenkinsUtils . handleStepResults ( stepName , failOnMissingReports , failOnMissingLinks )
2021-03-10 16:00:53 +01:00
}
} finally {
2021-06-15 14:34:56 +02:00
readPipelineEnv ( script: script , piperGoPath: piperGoPath )
2020-08-05 13:20:29 +02:00
}
2021-03-12 09:42:19 +01:00
} finally {
2021-03-10 16:00:53 +01:00
InfluxData . readFromDisk ( script )
2023-07-07 14:35:14 +02:00
utils . stash name: 'pipelineStepReports' , includes: '.pipeline/stepReports/**' , allowEmpty: true
2020-04-28 07:42:02 +02:00
}
2020-03-17 09:19:09 +01:00
}
}
}
}
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-07-02 12:08:56 +02:00
static void prepareExecution ( Script script , Utils utils , Map parameters = [ : ] ) {
2020-06-24 17:04:58 +02:00
def piperGoUtils = parameters . piperGoUtils ? : new PiperGoUtils ( script , utils )
piperGoUtils . unstashPiperBin ( )
utils . unstash ( 'pipelineConfigAndTests' )
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-06-24 17:04:58 +02:00
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 )
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-06-24 17:04:58 +02:00
static void prepareMetadataResource ( Script script , String metadataFile ) {
script . writeFile ( file: ".pipeline/tmp/${metadataFile}" , text: script . libraryResource ( metadataFile ) )
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-06-24 17:04:58 +02:00
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}" ) )
}
2020-03-30 14:31:24 +02:00
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 ( ',' )
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-03-30 14:31:24 +02:00
static String getCustomDefaultConfigsArg ( ) {
String customDefaults = getCustomDefaultConfigs ( )
if ( customDefaults ) {
2020-05-14 10:50:58 +02:00
return " --defaultConfig ${customDefaults} --ignoreCustomDefaults"
2020-03-30 14:31:24 +02:00
}
return ''
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-03-30 14:31:24 +02:00
static String getCustomConfigArg ( def script ) {
if ( script ? . commonPipelineEnvironment ? . configurationFile
2020-03-31 15:16:18 +02:00
& & script . commonPipelineEnvironment . configurationFile ! = '.pipeline/config.yml'
2020-03-30 14:31:24 +02:00
& & script . commonPipelineEnvironment . configurationFile ! = '.pipeline/config.yaml' ) {
return " --customConfig ${BashUtils.quoteAndEscape(script.commonPipelineEnvironment.configurationFile)}"
}
return ''
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-10-01 16:27:50 +02:00
void dockerWrapper ( script , stepName , config , body ) {
2020-03-17 09:19:09 +01:00
if ( config . dockerImage ) {
2020-10-01 16:27:50 +02:00
echo "[INFO] executing pipeline step '${stepName}' with docker image '${config.dockerImage}'"
2020-07-02 12:08:56 +02:00
Map dockerExecuteParameters = [ : ] . plus ( config )
dockerExecuteParameters . script = script
dockerExecute ( dockerExecuteParameters ) {
2020-03-17 09:19:09 +01:00
body ( )
}
} else {
body ( )
}
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-03-17 09:19:09 +01:00
void credentialWrapper ( config , List credentialInfo , body ) {
2021-02-15 09:48:51 +01:00
credentialInfo = handleVaultCredentials ( config , credentialInfo )
2022-06-22 13:31:17 +02:00
credentialInfo = handleANSCredentials ( config , credentialInfo )
2020-03-17 09:19:09 +01:00
if ( credentialInfo . size ( ) > 0 ) {
def creds = [ ]
2020-04-03 16:34:40 +02:00
def sshCreds = [ ]
2020-03-17 09:19:09 +01:00
credentialInfo . each { cred - >
2020-12-07 08:54:49 +01:00
def credentialsId
if ( cred . resolveCredentialsId = = false ) {
credentialsId = cred . id
} else {
credentialsId = config [ cred . id ]
}
if ( credentialsId ) {
2021-02-15 09:48:51 +01:00
switch ( cred . type ) {
2020-12-07 08:54:49 +01:00
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 :
2021-02-15 09:48:51 +01:00
error ( "invalid credential type: ${cred.type}" )
2020-12-07 08:54:49 +01:00
}
2020-03-17 09:19:09 +01:00
}
}
2020-04-03 16:34:40 +02:00
2021-02-15 09:48:51 +01:00
// remove credentialIds that were probably defaulted and which are not present in jenkins
if ( containsVaultConfig ( config ) ) {
creds = removeMissingCredentials ( creds , config )
sshCreds = removeMissingCredentials ( sshCreds , config )
}
2020-04-03 16:34:40 +02:00
if ( sshCreds . size ( ) > 0 ) {
sshagent ( sshCreds ) {
withCredentials ( creds ) {
body ( )
}
}
} else {
withCredentials ( creds ) {
body ( )
}
2020-03-17 09:19:09 +01:00
}
} else {
body ( )
}
}
2020-04-28 07:42:02 +02:00
2021-02-15 09:48:51 +01:00
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
}
2022-06-22 13:31:17 +02:00
List handleANSCredentials ( config , List credentialInfo ) {
if ( config . containsKey ( 'ansHookServiceKeyCredentialsId' ) ) {
credentialInfo + = [ [ type: 'token' , id: 'ansHookServiceKeyCredentialsId' , env: [ 'PIPER_ansHookServiceKey' ] ] ]
}
return credentialInfo
}
2020-08-06 14:22:40 +02:00
// reused in sonarExecuteScan
2020-04-28 07:42:02 +02:00
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
}
2021-02-15 09:48:51 +01:00
error "[${stepName}] Step execution failed${errorCategory}. Error: ${errorDetails.error ?: errorDetails.message}"
2020-04-28 07:42:02 +02:00
}
2020-05-26 07:58:03 +02:00
error "[${stepName}] Step execution failed. Error: ${ex}, please see log file for more details."
2020-04-28 07:42:02 +02:00
}
}
2022-08-29 11:39:08 +02:00
// 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 ( )
2023-01-16 10:18:16 +01:00
def piperGoUtils = parameters . piperGoUtils ? : new PiperGoUtils ( script , utils )
2022-08-29 11:39:08 +02:00
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}"
2022-08-31 15:11:51 +02:00
flags + = getCustomDefaultConfigsArg ( )
flags + = getCustomConfigArg ( script )
2022-08-29 11:39:08 +02:00
piperGoUtils . unstashPiperBin ( )
def returnCode = script . sh ( returnStatus: true , script: "${piperGoPath} checkIfStepActive ${flags}" )
return ( returnCode = = 0 )
}