2019-09-12 10:52:05 +02:00
import com.sap.piper.SidecarUtils
2018-09-21 16:55:31 +02:00
import static com . sap . piper . Prerequisites . checkScript
2018-08-21 15:45:59 +02:00
import com.sap.piper.ConfigurationHelper
2019-02-08 13:20:45 +02:00
import com.sap.piper.GenerateDocumentation
2018-08-21 15:45:59 +02:00
import com.sap.piper.JenkinsUtils
2018-11-05 12:24:25 +02:00
import com.sap.piper.Utils
2018-08-21 15:45:59 +02:00
import com.sap.piper.k8s.SystemEnv
2019-03-20 11:07:37 +02:00
import com.sap.piper.JsonUtils
2018-08-21 15:45:59 +02:00
import groovy.transform.Field
import hudson.AbortException
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_KUBERNETES = 'kubernetes'
2019-02-08 13:20:45 +02:00
2019-02-04 10:03:58 +02:00
@Field Set GENERAL_CONFIG_KEYS = [
2019-06-26 08:38:47 +02:00
'jenkinsKubernetes' ,
2020-04-08 19:54:06 +02:00
/ * *
* Jnlp agent Docker images which should be used to create new pods .
* @parentConfigKey jenkinsKubernetes
* /
'jnlpAgent' ,
/ * *
* Namespace that should be used to create a new pod
* @parentConfigKey jenkinsKubernetes
* /
'namespace' ,
/ * *
* Name of the pod template that should be inherited from .
* The pod template can be defined in the Jenkins UI
* @parentConfigKey jenkinsKubernetes
* /
'inheritFrom' ,
2020-10-02 12:56:16 +02:00
'additionalPodProperties' ,
2020-09-23 13:08:43 +02:00
'resources' ,
2021-11-02 11:00:38 +02:00
'annotations' ,
2021-01-13 11:48:48 +02:00
/ * *
* Set this to 'false' to bypass a docker image pull .
* Useful during development process . Allows testing of images which are available in the local registry only .
* /
'dockerPullImage' ,
/ * *
* Set this to 'false' to bypass a docker image pull .
* Useful during development process . Allows testing of images which are available in the local registry only .
* /
'sidecarPullImage' ,
2019-06-26 08:38:47 +02:00
/ * *
* Print more detailed information into the log .
* @possibleValues ` true ` , ` false `
* /
'verbose'
2019-02-04 10:03:58 +02:00
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS . plus ( [
2020-10-02 12:56:16 +02:00
/ * *
* Additional pod specific configuration . Map with the properties names
* as key and the corresponding value as value . The value can also be
* a nested structure .
* The properties will be added to the pod spec inside node ` spec ` at the
2023-07-31 11:15:39 +02:00
* same level like e . g . ` containers `
* for eg . , additionalPodProperties: [
* imagePullSecrets: [ 'secret-name' ]
* ]
2020-10-02 12:56:16 +02:00
* This property provides some kind of an expert mode . Any property
* which is not handled otherwise by the step can be set . It is not
* possible to overwrite e . g . the ` containers ` property or to
* overwrite the ` securityContext ` property .
* Alternate way for providing ` additionalPodProperties ` is via
* ` general /jenkinsKubernetes/ additionalPodProperties ` in the project configuration .
* Providing the resources map as parameter to the step call takes
* precedence .
* This freedom comes with great responsibility . The property
* ` additionalPodProperties ` should only be used in case you
* really know what you are doing .
* /
'additionalPodProperties' ,
2021-11-02 11:00:38 +02:00
/ * *
* Adds annotations in the metadata section of the PodSpec
* /
'annotations' ,
2019-02-08 13:20:45 +02:00
/ * *
* Allows to specify start command for container created with dockerImage parameter to overwrite Piper default ( ` /usr/ bin /tail -f / dev / null ` ) .
* /
'containerCommand' ,
/ * *
* Specifies start command for containers to overwrite Piper default ( ` /usr/ bin /tail -f / dev / null ` ) .
* If container 's defaultstart command should be used provide empty string like: `[' selenium / standalone - chrome ': ' ' ] ` .
* /
'containerCommands' ,
/ * *
* Specifies environment variables per container . If not provided ` dockerEnvVars ` will be used .
* /
'containerEnvVars' ,
/ * *
2020-09-24 07:41:06 +02:00
* A map of docker image to the name of the container . The pod will be created with all the images from this map and they are labelled based on the value field of each map entry .
2021-02-11 10:31:25 +02:00
* Example: ` [ 'maven:3.5-jdk-8-alpine' : 'mavenExecute' , 'selenium/standalone-chrome' : 'selenium' , 'famiko/jmeter-base' : 'checkJMeter' , 'ppiper/cf-cli:6' : 'cloudfoundry' ] `
2019-02-08 13:20:45 +02:00
* /
'containerMap' ,
/ * *
* Optional configuration in combination with containerMap to define the container where the commands should be executed in .
* /
'containerName' ,
/ * *
* Map which defines per docker image the port mappings , e . g . ` containerPortMappings: [ 'selenium/standalone-chrome' : [ [ name: 'selPort' , containerPort: 4444 , hostPort: 4444 ] ] ] ` .
* /
'containerPortMappings' ,
/ * *
* Specifies the pullImage flag per container .
* /
'containerPullImageFlags' ,
/ * *
* Allows to specify the shell to be executed for container with containerName .
* /
'containerShell' ,
/ * *
* Specifies a dedicated user home directory per container which will be passed as value for environment variable ` HOME ` . If not provided ` dockerWorkspace ` will be used .
* /
'containerWorkspaces' ,
/ * *
* Environment variables to set in the container , e . g . [ http_proxy: 'proxy:8080' ] .
* /
'dockerEnvVars' ,
/ * *
2020-04-17 10:31:04 +02:00
* Optional name of the docker image that should be used . If no docker image is provided , the closure will be executed in the jnlp agent container .
2019-02-08 13:20:45 +02:00
* /
2018-10-04 17:06:42 +02:00
'dockerImage' ,
2019-02-08 13:20:45 +02:00
/ * *
* Specifies a dedicated user home directory for the container which will be passed as value for environment variable ` HOME ` .
* /
2018-10-04 17:06:42 +02:00
'dockerWorkspace' ,
2019-09-12 10:52:05 +02:00
/ * *
* as ` dockerImage ` for the sidecar container
* /
'sidecarImage' ,
/ * *
* SideCar only:
* Name of the container in local network .
* /
'sidecarName' ,
/ * *
* Command executed inside the container which returns exit code 0 when the container is ready to be used .
* /
'sidecarReadyCommand' ,
/ * *
* as ` dockerEnvVars ` for the sidecar container
* /
'sidecarEnvVars' ,
/ * *
* as ` dockerWorkspace ` for the sidecar container
* /
'sidecarWorkspace' ,
2020-03-20 12:36:16 +02:00
2019-06-19 12:26:16 +02:00
/** Defines the Kubernetes nodeSelector as per [https://github.com/jenkinsci/kubernetes-plugin](https://github.com/jenkinsci/kubernetes-plugin).*/
'nodeSelector' ,
2019-04-02 14:23:19 +02:00
/ * *
* Kubernetes Security Context used for the pod .
* Can be used to specify uid and fsGroup .
* See: https: //kubernetes.io/docs/tasks/configure-pod-container/security-context/
* /
'securityContext' ,
2019-02-08 13:20:45 +02:00
/ * *
* Specific stashes that should be considered for the step execution .
* /
2019-02-04 10:03:58 +02:00
'stashContent' ,
2019-02-08 13:20:45 +02:00
/ * *
2019-08-14 16:44:12 +02:00
* In the Kubernetes case the workspace is only available to the respective Jenkins slave but not to the containers running inside the pod . < br / >
* This configuration defines exclude pattern for stashing from Jenkins workspace to working directory in container and back .
* Following excludes can be set:
2019-02-08 13:20:45 +02:00
*
2019-08-14 16:44:12 +02:00
* * ` workspace ` : Pattern for stashing towards container
* * ` stashBack ` : Pattern for bringing data from container back to Jenkins workspace . If not set: defaults to setting for ` workspace ` .
2019-02-08 13:20:45 +02:00
* /
2019-02-04 10:03:58 +02:00
'stashExcludes' ,
2019-02-08 13:20:45 +02:00
/ * *
2019-08-14 16:44:12 +02:00
* In the Kubernetes case the workspace is only available to the respective Jenkins slave but not to the containers running inside the pod . < br / >
* This configuration defines include pattern for stashing from Jenkins workspace to working directory in container and back .
* Following includes can be set:
2019-02-08 13:20:45 +02:00
*
2019-08-14 16:44:12 +02:00
* * ` workspace ` : Pattern for stashing towards container
* * ` stashBack ` : Pattern for bringing data from container back to Jenkins workspace . If not set: defaults to setting for ` workspace ` .
2019-02-08 13:20:45 +02:00
* /
2020-06-08 17:08:05 +02:00
'stashIncludes' ,
/ * *
* In the Kubernetes case the workspace is only available to the respective Jenkins slave but not to the containers running inside the pod . < br / >
* This configuration defines include pattern for stashing from Jenkins workspace to working directory in container and back .
* This flag controls whether the stashing does * not * use the default exclude patterns in addition to the patterns provided in ` stashExcludes ` .
* @possibleValues ` true ` , ` false `
* /
2020-09-23 13:08:43 +02:00
'stashNoDefaultExcludes' ,
/ * *
* A map containing the resources per container . The key is the
* container name . The value is a map defining valid resources .
* An entry with key ` DEFAULT ` can be used for defining resources
* for all containers which does not have resources specified otherwise .
* Alternate way for providing resources is via ` general /jenkinsKubernetes/ resources `
* in the project configuration . Providing the resources map as parameter
* to the step call takes precedence .
* /
'resources' ,
2021-04-29 11:39:34 +02:00
/ * *
* The path to which a volume should be mounted to . This volume will be available at the same
* mount path in each container of the provided containerMap . The volume is of type emptyDir
* and has the name 'volume' . With the additionalPodProperties parameter one can for example
* use this volume in an initContainer .
* /
'containerMountPath' ,
2021-06-01 09:59:49 +02:00
/ * *
* The docker image to run as initContainer .
* /
'initContainerImage' ,
/ * *
* Command executed inside the init container shell . Please enter command without providing any "sh -c" prefix . For example for an echo message , simply enter: echo ` HelloWorld `
* /
'initContainerCommand' ,
2019-02-04 10:03:58 +02:00
] )
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS . minus ( [
'stashIncludes' ,
'stashExcludes'
] )
2018-08-21 15:45:59 +02:00
2019-02-08 13:20:45 +02:00
/ * *
* Executes a closure inside a container in a kubernetes pod .
* Proxy environment variables defined on the Jenkins machine are also available in the container .
2019-07-17 12:01:24 +02:00
*
2020-06-15 07:03:53 +02:00
* By default jnlp agent defined for kubernetes - plugin will be used ( see [ https: //github.com/jenkinsci/kubernetes-plugin#pipeline-support](https://github.com/jenkinsci/kubernetes-plugin#pipeline-support)).
2019-07-17 12:01:24 +02:00
*
* It is possible to define a custom jnlp agent image by
*
* 1 . Defining the jnlp image via environment variable JENKINS_JNLP_IMAGE in the Kubernetes landscape
* 2 . Defining the image via config ( ` jenkinsKubernetes . jnlpAgent ` )
*
* Option 1 will take precedence over option 2 .
2019-02-08 13:20:45 +02:00
* /
@GenerateDocumentation
2018-08-21 15:45:59 +02:00
void call ( Map parameters = [ : ] , body ) {
2019-04-08 20:10:54 +02:00
handlePipelineStepErrors ( stepName: STEP_NAME , stepParameters: parameters , failOnError: true ) {
2018-09-21 16:55:31 +02:00
2018-10-31 09:40:12 +02:00
final script = checkScript ( this , parameters ) ? : this
2020-08-26 15:32:58 +02:00
def utils = parameters . juStabUtils ? : new Utils ( )
String stageName = parameters . stageName ? : env . STAGE_NAME
2018-09-21 16:55:31 +02:00
2018-08-21 15:45:59 +02:00
if ( ! JenkinsUtils . isPluginActive ( PLUGIN_ID_KUBERNETES ) ) {
error ( "[ERROR][${STEP_NAME}] not supported. Plugin '${PLUGIN_ID_KUBERNETES}' is not installed or not active." )
}
2020-06-08 17:08:05 +02:00
Map config = ConfigurationHelper . newInstance ( this )
2020-08-26 15:32:58 +02:00
. loadStepDefaults ( [ : ] , stageName )
2018-08-21 15:45:59 +02:00
. mixinGeneralConfig ( script . commonPipelineEnvironment , GENERAL_CONFIG_KEYS )
. mixinStepConfig ( script . commonPipelineEnvironment , STEP_CONFIG_KEYS )
2020-08-26 15:32:58 +02:00
. mixinStageConfig ( script . commonPipelineEnvironment , stageName , STEP_CONFIG_KEYS )
2018-08-21 15:45:59 +02:00
. mixin ( parameters , PARAMETER_KEYS )
. addIfEmpty ( 'uniqueId' , UUID . randomUUID ( ) . toString ( ) )
2020-06-08 17:08:05 +02:00
. use ( )
2018-08-21 15:45:59 +02:00
2020-04-17 10:31:04 +02:00
if ( ! config . containerMap & & config . dockerImage ) {
2018-10-08 11:54:13 +02:00
config . containerName = 'container-exec'
2019-08-21 15:04:20 +02:00
config . containerMap = [ ( config . get ( 'dockerImage' ) ) : config . containerName ]
config . containerCommands = config . containerCommand ? [ ( config . get ( 'dockerImage' ) ) : config . containerCommand ] : null
2018-08-21 15:45:59 +02:00
}
2019-09-12 10:52:05 +02:00
executeOnPod ( config , utils , body , script )
2018-08-21 15:45:59 +02:00
}
}
def getOptions ( config ) {
2019-03-20 11:07:37 +02:00
def namespace = config . jenkinsKubernetes . namespace
def options = [
2019-09-12 10:52:05 +02:00
name : 'dynamic-agent-' + config . uniqueId ,
label: config . uniqueId ,
yaml : generatePodSpec ( config )
2019-03-20 11:07:37 +02:00
]
if ( namespace ) {
options . namespace = namespace
}
2019-06-19 12:26:16 +02:00
if ( config . nodeSelector ) {
options . nodeSelector = config . nodeSelector
}
2019-06-26 08:38:47 +02:00
if ( ! config . verbose ) {
options . showRawYaml = false
}
2020-04-08 19:54:06 +02:00
if ( config . jenkinsKubernetes . inheritFrom ) {
options . inheritFrom = config . jenkinsKubernetes . inheritFrom
options . yamlMergeStrategy = merge ( )
}
2019-03-20 11:07:37 +02:00
return options
2018-08-21 15:45:59 +02:00
}
2019-09-12 10:52:05 +02:00
void executeOnPod ( Map config , utils , Closure body , Script script ) {
2018-08-21 15:45:59 +02:00
/ *
* There could be exceptions thrown by
- The podTemplate
- The container method
- The body
* We use nested exception handling in this case .
2018-10-08 11:54:13 +02:00
* In the first 2 cases , the 'container' stash is not created because the inner try / finally is not reached .
2018-10-05 10:51:01 +02:00
* However , the workspace has not been modified and don ' t need to be restored .
* In case third case , we need to create the 'container' stash to bring the modified content back to the host .
2018-08-21 15:45:59 +02:00
* /
try {
2019-09-12 10:52:05 +02:00
SidecarUtils sidecarUtils = new SidecarUtils ( script )
2019-04-03 13:31:07 +02:00
def stashContent = config . stashContent
2023-03-17 09:52:00 +02:00
boolean defaultStashCreated = false
2019-09-12 10:52:05 +02:00
if ( config . containerName & & stashContent . isEmpty ( ) ) {
2023-07-07 14:35:14 +02:00
stashContent = [ stashWorkspace ( config , utils , 'workspace' ) ]
2023-03-17 09:52:00 +02:00
defaultStashCreated = true
2018-11-05 12:24:25 +02:00
}
2018-08-21 15:45:59 +02:00
podTemplate ( getOptions ( config ) ) {
node ( config . uniqueId ) {
2019-09-12 10:52:05 +02:00
if ( config . sidecarReadyCommand ) {
sidecarUtils . waitForSidecarReadyOnKubernetes ( config . sidecarName , config . sidecarReadyCommand )
}
2018-10-08 11:54:13 +02:00
if ( config . containerName ) {
2019-01-08 20:44:28 +02:00
Map containerParams = [ name: config . containerName ]
if ( config . containerShell ) {
containerParams . shell = config . containerShell
}
2019-01-31 10:39:13 +02:00
echo "ContainerConfig: ${containerParams}"
2019-09-12 10:52:05 +02:00
container ( containerParams ) {
2018-10-08 11:54:13 +02:00
try {
2019-04-03 13:31:07 +02:00
utils . unstashAll ( stashContent )
2023-03-23 10:27:13 +02:00
if ( config . verbose ) {
lsDir ( 'Directory content before body execution' )
}
2023-03-17 09:52:00 +02:00
if ( defaultStashCreated ) {
2023-07-07 14:35:14 +02:00
invalidateStash ( config , 'workspace' , utils )
2023-03-17 09:52:00 +02:00
}
2023-03-27 09:42:04 +02:00
def result = body ( )
2023-03-23 10:27:13 +02:00
if ( config . verbose ) {
lsDir ( 'Directory content after body execution' )
}
2023-03-27 09:42:04 +02:00
return result
2018-10-08 11:54:13 +02:00
} finally {
2023-07-07 14:35:14 +02:00
stashWorkspace ( config , utils , 'container' , true , true )
2018-10-08 11:54:13 +02:00
}
2018-10-05 10:51:01 +02:00
}
2018-10-08 11:54:13 +02:00
} else {
body ( )
2018-08-21 15:45:59 +02:00
}
}
}
} finally {
2018-10-08 11:54:13 +02:00
if ( config . containerName )
2023-07-07 14:00:44 +02:00
unstashWorkspace ( config , utils , 'container' )
2018-08-21 15:45:59 +02:00
}
}
2023-03-23 10:27:13 +02:00
private void lsDir ( String message ) {
echo "[DEBUG] Begin of ${message}"
// some images might not contain the find command. In that case the build must not be aborted.
catchError ( message: 'Cannot list directory content' , buildResult: 'SUCCESS' , stageResult: 'SUCCESS' ) {
// no -ls option since this is not available for some images
sh 'find . -mindepth 1 -maxdepth 2'
}
echo "[DEBUG] End of ${message}"
}
2019-03-20 11:07:37 +02:00
private String generatePodSpec ( Map config ) {
def podSpec = [
apiVersion: "v1" ,
2019-09-12 10:52:05 +02:00
kind : "Pod" ,
metadata : [
2021-11-02 11:00:38 +02:00
lables: config . uniqueId ,
annotations: [ : ]
2019-03-20 11:07:37 +02:00
] ,
2020-10-02 12:56:16 +02:00
spec : [ : ]
2019-03-20 11:07:37 +02:00
]
2021-11-02 11:00:38 +02:00
podSpec . metadata . annotations = getAnnotations ( config )
2020-10-02 12:56:16 +02:00
podSpec . spec + = getAdditionalPodProperties ( config )
2021-06-01 09:59:49 +02:00
podSpec . spec . initContainers = getInitContainerList ( config )
2020-10-02 12:56:16 +02:00
podSpec . spec . containers = getContainerList ( config )
2019-03-20 11:07:37 +02:00
podSpec . spec . securityContext = getSecurityContext ( config )
2021-04-29 11:39:34 +02:00
if ( config . containerMountPath ) {
podSpec . spec . volumes = [ [
name : "volume" ,
emptyDir: [ : ]
] ]
}
2019-03-22 13:11:20 +02:00
return new JsonUtils ( ) . groovyObjectToPrettyJsonString ( podSpec )
2019-03-20 11:07:37 +02:00
}
2023-07-07 14:35:14 +02:00
private String stashWorkspace ( config , utils , prefix , boolean chown = false , boolean stashBack = false ) {
2018-11-05 12:24:25 +02:00
def stashName = "${prefix}-${config.uniqueId}"
2018-08-21 15:45:59 +02:00
try {
2019-09-12 10:52:05 +02:00
if ( chown ) {
2019-03-20 11:07:37 +02:00
def securityContext = getSecurityContext ( config )
def runAsUser = securityContext ? . runAsUser ? : 1000
def fsGroup = securityContext ? . fsGroup ? : 1000
2019-09-12 10:52:05 +02:00
sh "" " # ! $ { config . containerShell ? : '/bin/sh' }
2019-03-20 11:07:37 +02:00
chown - R $ { runAsUser } : $ { fsGroup } . "" "
2019-01-31 10:39:13 +02:00
}
2019-08-14 16:44:12 +02:00
def includes , excludes
2022-08-15 12:55:51 +02:00
if ( config . verbose ) {
echo "stashIncludes config: ${config.stashIncludes}"
echo "stashExcludes config: ${config.stashExcludes}"
}
2019-09-12 10:52:05 +02:00
if ( stashBack ) {
2019-08-14 16:44:12 +02:00
includes = config . stashIncludes . stashBack ? : config . stashIncludes . workspace
excludes = config . stashExcludes . stashBack ? : config . stashExcludes . workspace
} else {
includes = config . stashIncludes . workspace
excludes = config . stashExcludes . workspace
}
2022-08-15 12:55:51 +02:00
if ( config . verbose ) {
echo "stash effective (includes): ${includes}"
echo "stash effective (excludes): ${excludes}"
}
2023-07-07 14:35:14 +02:00
utils . stash (
2018-11-05 12:24:25 +02:00
name: stashName ,
2019-08-14 16:44:12 +02:00
includes: includes ,
2020-06-08 17:08:05 +02:00
excludes: excludes ,
// 'true' by default due to negative side-effects, but can be overwritten via parameters
// (as done by artifactPrepareVersion to preserve the .git folder)
useDefaultExcludes: ! config . stashNoDefaultExcludes ,
2022-08-15 12:55:51 +02:00
allowEmpty: true
2018-08-21 15:45:59 +02:00
)
2018-11-05 12:24:25 +02:00
return stashName
2018-08-21 15:45:59 +02:00
} catch ( AbortException | IOException e ) {
echo "${e.getMessage()}"
2020-03-06 15:05:14 +02:00
} catch ( Throwable e ) {
echo "Unstash workspace failed with throwable ${e.getMessage()}"
throw e
2018-08-21 15:45:59 +02:00
}
2019-12-18 15:53:38 +02:00
return null
2018-08-21 15:45:59 +02:00
}
2021-11-02 11:00:38 +02:00
private Map getAnnotations ( Map config ) {
return config . annotations ? : config . jenkinsKubernetes . annotations ? : [ : ]
}
2020-10-02 12:56:16 +02:00
private Map getAdditionalPodProperties ( Map config ) {
Map podProperties = config . additionalPodProperties ? : config . jenkinsKubernetes . additionalPodProperties ? : [ : ]
if ( podProperties ) {
echo "Additional pod properties found (${podProperties.keySet()})." +
' Providing additional pod properties is some kind of expert mode. In case of any problems caused by these' +
' additional properties only limited support can be provided.'
}
return podProperties
}
2019-03-20 11:07:37 +02:00
private Map getSecurityContext ( Map config ) {
return config . securityContext ? : config . jenkinsKubernetes . securityContext ? : [ : ]
}
2023-07-07 14:00:44 +02:00
private void unstashWorkspace ( config , utils , prefix ) {
2018-08-21 15:45:59 +02:00
try {
2023-07-07 14:00:44 +02:00
utils . unstash "${prefix}-${config.uniqueId}"
2018-08-21 15:45:59 +02:00
} catch ( AbortException | IOException e ) {
2021-10-06 10:25:50 +02:00
echo "${e.getMessage()}\n${e.getCause()}"
2020-03-06 15:05:14 +02:00
} catch ( Throwable e ) {
echo "Unstash workspace failed with throwable ${e.getMessage()}"
throw e
2021-10-06 10:25:50 +02:00
} finally {
2023-07-07 14:35:14 +02:00
invalidateStash ( config , prefix , utils )
2018-08-21 15:45:59 +02:00
}
}
2021-06-01 09:59:49 +02:00
private List getInitContainerList ( config ) {
def initContainerSpecList = [ ]
if ( config . initContainerImage & & config . containerMountPath ) {
// regex [\W_] matches any non-word character equivalent to [^a-zA-Z0-9_]
def initContainerName = config . initContainerImage . toLowerCase ( ) . replaceAll ( /[\W_]/ , "-" )
def initContainerSpec = [
name : initContainerName ,
image : config . initContainerImage
]
if ( config . containerMountPath ) {
initContainerSpec . volumeMounts = [ [ name: "volume" , mountPath: config . containerMountPath ] ]
}
if ( config . initContainerCommand = = null ) {
initContainerSpec [ 'command' ] = [
'/usr/bin/tail' ,
'-f' ,
'/dev/null'
]
} else {
initContainerSpec [ 'command' ] = [
'sh' ,
'-c' ,
config . initContainerCommand
]
}
initContainerSpecList . push ( initContainerSpec )
}
return initContainerSpecList
}
2018-08-21 15:45:59 +02:00
private List getContainerList ( config ) {
2019-07-17 12:01:24 +02:00
//If no custom jnlp agent provided as default jnlp agent (jenkins/jnlp-slave) as defined in the plugin, see https://github.com/jenkinsci/kubernetes-plugin#pipeline-support
def result = [ ]
//allow definition of jnlp image via environment variable JENKINS_JNLP_IMAGE in the Kubernetes landscape or via config as fallback
if ( env . JENKINS_JNLP_IMAGE | | config . jenkinsKubernetes . jnlpAgent ) {
2020-09-23 13:08:43 +02:00
def jnlpContainerName = 'jnlp'
def jnlpSpec = [
name : jnlpContainerName ,
2019-07-17 12:01:24 +02:00
image: env . JENKINS_JNLP_IMAGE ? : config . jenkinsKubernetes . jnlpAgent
2020-09-23 13:08:43 +02:00
]
def resources = getResources ( jnlpContainerName , config )
if ( resources ) {
jnlpSpec . resources = resources
}
result . push ( jnlpSpec )
2019-07-17 12:01:24 +02:00
}
2018-08-21 15:45:59 +02:00
config . containerMap . each { imageName , containerName - >
2019-02-06 09:48:33 +02:00
def containerPullImage = config . containerPullImageFlags ? . get ( imageName )
2019-09-12 10:52:05 +02:00
boolean pullImage = containerPullImage ! = null ? containerPullImage : config . dockerPullImage
2019-03-20 11:07:37 +02:00
def containerSpec = [
2019-09-12 10:52:05 +02:00
name : containerName . toLowerCase ( ) ,
image : imageName ,
imagePullPolicy: pullImage ? "Always" : "IfNotPresent" ,
2020-03-20 12:36:16 +02:00
env : getContainerEnvs ( config , imageName , config . dockerEnvVars , config . dockerWorkspace )
2018-10-04 17:06:42 +02:00
]
2021-04-29 11:39:34 +02:00
if ( config . containerMountPath ) {
containerSpec . volumeMounts = [ [ name: "volume" , mountPath: config . containerMountPath ] ]
}
2018-10-04 17:06:42 +02:00
2019-03-20 11:07:37 +02:00
def configuredCommand = config . containerCommands ? . get ( imageName )
def shell = config . containerShell ? : '/bin/sh'
if ( configuredCommand = = null ) {
containerSpec [ 'command' ] = [
'/usr/bin/tail' ,
'-f' ,
'/dev/null'
]
2019-09-12 10:52:05 +02:00
} else if ( configuredCommand ! = "" ) {
2019-03-20 11:07:37 +02:00
// apparently "" is used as a flag for not settings container commands !?
containerSpec [ 'command' ] =
2019-09-12 10:52:05 +02:00
( configuredCommand in List ) ? configuredCommand : [
shell ,
'-c' ,
configuredCommand
]
2018-10-04 17:06:42 +02:00
}
if ( config . containerPortMappings ? . get ( imageName ) ) {
def ports = [ ]
def portCounter = 0
2019-09-12 10:52:05 +02:00
config . containerPortMappings . get ( imageName ) . each { mapping - >
2019-04-02 14:23:19 +02:00
def name = "${containerName}${portCounter}" . toString ( )
2019-09-12 10:52:05 +02:00
if ( mapping . containerPort ! = mapping . hostPort ) {
echo ( "[WARNING][${STEP_NAME}]: containerPort and hostPort are different for container '${containerName}'. "
2019-04-02 14:23:19 +02:00
+ "The hostPort will be ignored." )
}
ports . add ( [ name: name , containerPort: mapping . containerPort ] )
2019-09-12 10:52:05 +02:00
portCounter + +
2018-10-04 17:06:42 +02:00
}
2019-03-20 11:07:37 +02:00
containerSpec . ports = ports
2018-10-04 17:06:42 +02:00
}
2020-09-23 13:08:43 +02:00
def resources = getResources ( containerName . toLowerCase ( ) , config )
if ( resources ) {
containerSpec . resources = resources
}
2019-03-20 11:07:37 +02:00
result . push ( containerSpec )
2018-08-21 15:45:59 +02:00
}
2019-09-12 10:52:05 +02:00
if ( config . sidecarImage ) {
2020-09-23 13:08:43 +02:00
def sideCarContainerName = config . sidecarName . toLowerCase ( )
2019-09-12 10:52:05 +02:00
def containerSpec = [
2020-09-23 13:08:43 +02:00
name : sideCarContainerName ,
2019-09-12 10:52:05 +02:00
image : config . sidecarImage ,
imagePullPolicy: config . sidecarPullImage ? "Always" : "IfNotPresent" ,
2020-03-20 12:36:16 +02:00
env : getContainerEnvs ( config , config . sidecarImage , config . sidecarEnvVars , config . sidecarWorkspace ) ,
2019-09-12 10:52:05 +02:00
command : [ ]
]
2020-09-23 13:08:43 +02:00
def resources = getResources ( sideCarContainerName , config )
2023-12-01 14:33:08 +02:00
if ( resources ) {
2020-09-23 13:08:43 +02:00
containerSpec . resources = resources
}
2023-12-01 14:33:08 +02:00
if ( config . containerMountPath ) {
containerSpec . volumeMounts = [ [ name: "volume" , mountPath: config . containerMountPath ] ]
}
2019-09-12 10:52:05 +02:00
result . push ( containerSpec )
}
2018-08-21 15:45:59 +02:00
return result
}
2020-09-23 13:08:43 +02:00
private Map getResources ( String containerName , Map config ) {
Map resources = config . resources
if ( resources = = null ) {
resources = config ? . jenkinsKubernetes . resources
}
if ( resources = = null ) {
return null
}
Map res = resources . get ( containerName )
if ( res = = null ) {
res = resources . get ( 'DEFAULT' )
}
return res
}
2019-02-08 13:20:45 +02:00
/ *
2018-08-21 15:45:59 +02:00
* Returns a list of envVar object consisting of set
* environment variables , params ( Parametrized Build ) and working directory .
* ( Kubernetes - Plugin only ! )
* @param config Map with configurations
* /
2019-09-12 10:52:05 +02:00
2020-03-20 12:36:16 +02:00
private List getContainerEnvs ( config , imageName , defaultEnvVars , defaultConfig ) {
2018-08-21 15:45:59 +02:00
def containerEnv = [ ]
2020-03-20 12:36:16 +02:00
def dockerEnvVars = config . containerEnvVars ? . get ( imageName ) ? : defaultEnvVars ? : [ : ]
def dockerWorkspace = config . containerWorkspaces ? . get ( imageName ) ! = null ? config . containerWorkspaces ? . get ( imageName ) : defaultConfig ? : ''
2018-08-21 15:45:59 +02:00
2019-03-20 11:07:37 +02:00
def envVar = { e - >
2019-09-12 10:52:05 +02:00
[ name: e . key , value: e . value ]
2019-03-20 11:07:37 +02:00
}
2018-08-21 15:45:59 +02:00
if ( dockerEnvVars ) {
2020-03-05 16:48:05 +02:00
dockerEnvVars . each {
k , v - >
containerEnv < < envVar ( key: k , value: v . toString ( ) )
2018-08-21 15:45:59 +02:00
}
}
if ( dockerWorkspace ) {
containerEnv < < envVar ( key: "HOME" , value: dockerWorkspace )
}
// Inherit the proxy information from the master to the container
SystemEnv systemEnv = new SystemEnv ( )
2020-03-05 16:48:05 +02:00
systemEnv . getEnv ( ) . each {
k , v - >
containerEnv < < envVar ( key: k , value: v )
2018-08-21 15:45:59 +02:00
}
return containerEnv
}
2023-06-26 09:56:18 +02:00
2023-07-07 14:35:14 +02:00
private void invalidateStash ( def config , String prefix , def utils ) {
String name = "${prefix}-${config.uniqueId}"
echo "invalidate stash ${name}"
utils . stash name: name , excludes: '**/*' , allowEmpty: true
2023-06-26 09:56:18 +02:00
}