2018-09-21 16:55:31 +02:00
import static com . sap . piper . Prerequisites . checkScript
2017-12-06 13:03:06 +02:00
import com.cloudbees.groovy.cps.NonCPS
2018-11-05 12:24:25 +02:00
2018-08-21 15:45:59 +02:00
import com.sap.piper.ConfigurationHelper
import com.sap.piper.JenkinsUtils
2018-11-05 12:24:25 +02:00
import com.sap.piper.Utils
import com.sap.piper.k8s.ContainerMap
2018-08-21 15:45:59 +02:00
import groovy.transform.Field
2018-06-07 13:58:32 +02:00
2018-11-29 10:54:05 +02:00
@Field def STEP_NAME = getClass ( ) . getName ( )
2018-08-21 15:45:59 +02:00
@Field def PLUGIN_ID_DOCKER_WORKFLOW = 'docker-workflow'
2017-12-13 11:58:22 +02:00
2019-02-04 10:03:58 +02:00
@Field Set GENERAL_CONFIG_KEYS = [
'jenkinsKubernetes'
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS . plus ( [
2018-10-04 17:06:42 +02:00
'containerPortMappings' ,
2019-01-14 15:43:07 +02:00
'containerCommand' ,
'containerShell' ,
2018-10-04 17:06:42 +02:00
'dockerEnvVars' ,
'dockerImage' ,
'dockerName' ,
'dockerOptions' ,
'dockerWorkspace' ,
'dockerVolumeBind' ,
'sidecarEnvVars' ,
'sidecarImage' ,
2018-10-24 10:13:28 +02:00
'sidecarName' ,
2018-10-04 17:06:42 +02:00
'sidecarOptions' ,
'sidecarWorkspace' ,
2018-11-05 12:24:25 +02:00
'sidecarVolumeBind' ,
'stashContent'
2019-02-04 10:03:58 +02:00
] )
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
2018-05-14 17:11:27 +02:00
2018-08-21 15:45:59 +02:00
void call ( Map parameters = [ : ] , body ) {
handlePipelineStepErrors ( stepName: STEP_NAME , stepParameters: parameters ) {
2018-09-21 16:55:31 +02:00
2018-10-31 09:40:12 +02:00
final script = checkScript ( this , parameters ) ? : this
2018-09-21 16:55:31 +02:00
2018-11-05 12:24:25 +02:00
def utils = parameters ? . juStabUtils ? : new Utils ( )
2018-11-05 14:30:43 +02:00
2018-10-17 11:05:20 +02:00
Map config = ConfigurationHelper . newInstance ( this )
2018-09-07 10:08:16 +02:00
. loadStepDefaults ( )
2018-08-21 15:45:59 +02:00
. mixinGeneralConfig ( script . commonPipelineEnvironment , GENERAL_CONFIG_KEYS )
. mixinStepConfig ( script . commonPipelineEnvironment , STEP_CONFIG_KEYS )
2018-08-29 10:31:01 +02:00
. mixinStageConfig ( script . commonPipelineEnvironment , parameters . stageName ? : env . STAGE_NAME , STEP_CONFIG_KEYS )
2018-08-21 15:45:59 +02:00
. mixin ( parameters , PARAMETER_KEYS )
. use ( )
if ( isKubernetes ( ) & & config . dockerImage ) {
if ( env . POD_NAME & & isContainerDefined ( config ) ) {
container ( getContainerDefined ( config ) ) {
2018-10-04 17:06:42 +02:00
echo "[INFO][${STEP_NAME}] Executing inside a Kubernetes Container."
2018-08-21 15:45:59 +02:00
body ( )
sh "chown -R 1000:1000 ."
}
} else {
2018-10-04 17:06:42 +02:00
if ( ! config . sidecarImage ) {
dockerExecuteOnKubernetes (
script: script ,
2019-01-14 15:43:07 +02:00
containerCommand: config . containerCommand ,
containerShell: config . containerShell ,
2018-10-04 17:06:42 +02:00
dockerImage: config . dockerImage ,
dockerEnvVars: config . dockerEnvVars ,
2018-11-05 12:24:25 +02:00
dockerWorkspace: config . dockerWorkspace ,
stashContent: config . stashContent
2018-10-04 17:06:42 +02:00
) {
echo "[INFO][${STEP_NAME}] Executing inside a Kubernetes Pod"
body ( )
}
} else {
Map paramMap = [
script: script ,
containerCommands: [ : ] ,
containerEnvVars: [ : ] ,
containerMap: [ : ] ,
containerName: config . dockerName ,
containerPortMappings: [ : ] ,
2018-11-05 12:24:25 +02:00
containerWorkspaces: [ : ] ,
stashContent: config . stashContent
2018-10-04 17:06:42 +02:00
]
paramMap . containerCommands [ config . sidecarImage ] = ''
paramMap . containerEnvVars [ config . dockerImage ] = config . dockerEnvVars
paramMap . containerEnvVars [ config . sidecarImage ] = config . sidecarEnvVars
paramMap . containerMap [ config . dockerImage ] = config . dockerName
paramMap . containerMap [ config . sidecarImage ] = config . sidecarName
paramMap . containerPortMappings = config . containerPortMappings
paramMap . containerWorkspaces [ config . dockerImage ] = config . dockerWorkspace
paramMap . containerWorkspaces [ config . sidecarImage ] = ''
dockerExecuteOnKubernetes ( paramMap ) {
echo "[INFO][${STEP_NAME}] Executing inside a Kubernetes Pod with sidecar container"
body ( )
}
2018-08-21 15:45:59 +02:00
}
}
} else {
boolean executeInsideDocker = true
if ( ! JenkinsUtils . isPluginActive ( PLUGIN_ID_DOCKER_WORKFLOW ) ) {
echo "[WARNING][${STEP_NAME}] Docker not supported. Plugin '${PLUGIN_ID_DOCKER_WORKFLOW}' is not installed or not active. Configured docker image '${config.dockerImage}' will not be used."
executeInsideDocker = false
2017-12-13 11:58:22 +02:00
}
2018-01-15 14:42:57 +02:00
returnCode = sh script: 'docker ps -q > /dev/null' , returnStatus: true
2018-08-21 15:45:59 +02:00
if ( returnCode ! = 0 ) {
echo "[WARNING][$STEP_NAME] Cannot connect to docker daemon (command 'docker ps' did not return with '0'). Configured docker image '${config.dockerImage}' will not be used."
executeInsideDocker = false
2018-01-15 14:42:57 +02:00
}
2018-08-21 15:45:59 +02:00
if ( executeInsideDocker & & config . dockerImage ) {
2018-11-05 12:24:25 +02:00
utils . unstashAll ( config . stashContent )
2018-08-21 15:45:59 +02:00
def image = docker . image ( config . dockerImage )
image . pull ( )
2018-10-04 17:06:42 +02:00
if ( ! config . sidecarImage ) {
image . inside ( getDockerOptions ( config . dockerEnvVars , config . dockerVolumeBind , config . dockerOptions ) ) {
body ( )
}
} else {
2018-10-24 10:13:28 +02:00
def networkName = "sidecar-${UUID.randomUUID()}"
sh "docker network create ${networkName}"
try {
def sidecarImage = docker . image ( config . sidecarImage )
sidecarImage . pull ( )
config . sidecarOptions = config . sidecarOptions ? : [ ]
if ( config . sidecarName )
config . sidecarOptions . add ( "--network-alias ${config.sidecarName}" )
config . sidecarOptions . add ( "--network ${networkName}" )
sidecarImage . withRun ( getDockerOptions ( config . sidecarEnvVars , config . sidecarVolumeBind , config . sidecarOptions ) ) { c - >
config . dockerOptions = config . dockerOptions ? : [ ]
if ( config . dockerName )
config . dockerOptions . add ( "--network-alias ${config.dockerName}" )
config . dockerOptions . add ( "--network ${networkName}" )
image . inside ( getDockerOptions ( config . dockerEnvVars , config . dockerVolumeBind , config . dockerOptions ) ) {
echo "[INFO][${STEP_NAME}] Running with sidecar container."
body ( )
}
2018-10-04 17:06:42 +02:00
}
2018-10-24 10:13:28 +02:00
} finally {
sh "docker network remove ${networkName}"
2018-10-04 17:06:42 +02:00
}
2018-08-21 15:45:59 +02:00
}
} else {
echo "[INFO][${STEP_NAME}] Running on local environment."
2017-12-06 13:03:06 +02:00
body ( )
}
}
}
}
2018-08-21 15:45:59 +02:00
2018-04-20 12:59:17 +02:00
2017-12-06 13:03:06 +02:00
/ * *
* Returns a string with docker options containing
* environment variables ( if set ) .
* Possible to extend with further options .
* @param dockerEnvVars Map with environment variables
* /
@NonCPS
private getDockerOptions ( Map dockerEnvVars , Map dockerVolumeBind , def dockerOptions ) {
2018-06-08 11:55:38 +02:00
def specialEnvironments = [
'http_proxy' ,
'https_proxy' ,
'no_proxy' ,
'HTTP_PROXY' ,
'HTTPS_PROXY' ,
'NO_PROXY'
]
2018-03-29 14:13:11 +02:00
def options = [ ]
2017-12-06 13:03:06 +02:00
if ( dockerEnvVars ) {
for ( String k : dockerEnvVars . keySet ( ) ) {
2018-03-29 14:13:11 +02:00
options . add ( "--env ${k}=${dockerEnvVars[k].toString()}" )
2017-12-06 13:03:06 +02:00
}
}
for ( String envVar : specialEnvironments ) {
if ( dockerEnvVars = = null | | ! dockerEnvVars . containsKey ( envVar ) ) {
2018-03-29 14:13:11 +02:00
options . add ( "--env ${envVar}" )
2017-12-06 13:03:06 +02:00
}
}
if ( dockerVolumeBind ) {
for ( String k : dockerVolumeBind . keySet ( ) ) {
2018-03-29 14:13:11 +02:00
options . add ( "--volume ${k}:${dockerVolumeBind[k].toString()}" )
2017-12-06 13:03:06 +02:00
}
}
2018-08-21 15:45:59 +02:00
if ( dockerOptions ) {
if ( dockerOptions instanceof CharSequence ) {
2019-01-18 15:43:57 +02:00
dockerOptions = [ dockerOptions ]
}
if ( dockerOptions instanceof List ) {
2018-08-21 15:45:59 +02:00
for ( String option : dockerOptions ) {
2019-01-18 14:40:15 +02:00
options < < escapeBlanks ( option )
2018-08-21 15:45:59 +02:00
}
} else {
throw new IllegalArgumentException ( "Unexpected type for dockerOptions. Expected was either a list or a string. Actual type was: '${dockerOptions.getClass()}'" )
2018-03-29 14:13:11 +02:00
}
2017-12-06 13:03:06 +02:00
}
2018-03-29 14:13:11 +02:00
return options . join ( ' ' )
2017-12-06 13:03:06 +02:00
}
2018-08-21 15:45:59 +02:00
boolean isContainerDefined ( config ) {
Map containerMap = ContainerMap . instance . getMap ( )
if ( ! containerMap . containsKey ( env . POD_NAME ) ) {
return false
}
return containerMap . get ( env . POD_NAME ) . containsKey ( config . dockerImage )
}
def getContainerDefined ( config ) {
return ContainerMap . instance . getMap ( ) . get ( env . POD_NAME ) . get ( config . dockerImage ) . toLowerCase ( )
}
boolean isKubernetes ( ) {
return Boolean . valueOf ( env . ON_K8S )
}
2019-01-18 14:40:15 +02:00
/ * *
* Escapes blanks for values in key / value pairs
* E . g . < code > description = Lorem ipsum < / code > is
* changed to < code > description = Lorem \ ipsum < / code > .
* /
@NonCPS
def escapeBlanks ( def s ) {
def EQ = '='
def parts = s . split ( EQ )
if ( parts . length = = 2 ) {
parts [ 1 ] = parts [ 1 ] . replaceAll ( ' ' , '\\\\ ' )
s = parts . join ( EQ )
}
return s
}