24
24
25
25
package org .csanchez .jenkins .plugins .kubernetes ;
26
26
27
+ import static org .csanchez .jenkins .plugins .kubernetes .ContainerTemplate .DEFAULT_WORKING_DIR ;
27
28
import static org .csanchez .jenkins .plugins .kubernetes .KubernetesCloud .JNLP_NAME ;
28
29
import static org .csanchez .jenkins .plugins .kubernetes .PodTemplateUtils .combine ;
29
30
import static org .csanchez .jenkins .plugins .kubernetes .PodTemplateUtils .isNullOrEmpty ;
41
42
import io .fabric8 .kubernetes .api .model .ContainerBuilder ;
42
43
import io .fabric8 .kubernetes .api .model .ContainerPort ;
43
44
import io .fabric8 .kubernetes .api .model .EnvVar ;
45
+ import io .fabric8 .kubernetes .api .model .EnvVarBuilder ;
44
46
import io .fabric8 .kubernetes .api .model .ExecAction ;
45
47
import io .fabric8 .kubernetes .api .model .LocalObjectReference ;
46
48
import io .fabric8 .kubernetes .api .model .Pod ;
51
53
import io .fabric8 .kubernetes .api .model .ResourceRequirements ;
52
54
import io .fabric8 .kubernetes .api .model .ResourceRequirementsBuilder ;
53
55
import io .fabric8 .kubernetes .api .model .Volume ;
56
+ import io .fabric8 .kubernetes .api .model .VolumeBuilder ;
54
57
import io .fabric8 .kubernetes .api .model .VolumeMount ;
55
58
import io .fabric8 .kubernetes .api .model .VolumeMountBuilder ;
56
59
import io .fabric8 .kubernetes .client .utils .Serialization ;
@@ -101,6 +104,9 @@ public class PodTemplateBuilder {
101
104
public static final String LABEL_KUBERNETES_CONTROLLER = "kubernetes.jenkins.io/controller" ;
102
105
static final String NO_RECONNECT_AFTER_TIMEOUT =
103
106
SystemProperties .getString (PodTemplateBuilder .class .getName () + ".noReconnectAfter" , "1d" );
107
+ private static final String JENKINS_AGENT_FILE_ENVVAR = "JENKINS_AGENT_FILE" ;
108
+ private static final String JENKINS_AGENT_AGENT_JAR = "/jenkins-agent/agent.jar" ;
109
+ private static final String JENKINS_AGENT_LAUNCHER_SCRIPT_LOCATION = "/jenkins-agent/jenkins-agent" ;
104
110
105
111
@ SuppressFBWarnings (value = "MS_SHOULD_BE_FINAL" , justification = "tests" )
106
112
@ Restricted (NoExternalUse .class )
@@ -124,7 +130,7 @@ public class PodTemplateBuilder {
124
130
}
125
131
126
132
@ Restricted (NoExternalUse .class )
127
- static final String DEFAULT_JNLP_IMAGE =
133
+ static final String DEFAULT_AGENT_IMAGE =
128
134
System .getProperty (PodTemplateStepExecution .class .getName () + ".defaultImage" , defaultImageName );
129
135
130
136
static final String DEFAULT_JNLP_CONTAINER_MEMORY_REQUEST = System .getProperty (
@@ -309,39 +315,92 @@ public Pod build() {
309
315
}
310
316
}
311
317
312
- // default jnlp container
313
- Optional <Container > jnlpOpt = pod .getSpec ().getContainers ().stream ()
314
- .filter (c -> JNLP_NAME .equals (c .getName ()))
318
+ // default agent container
319
+ String agentContainerName = StringUtils .defaultString (template .getAgentContainer (), JNLP_NAME );
320
+ Optional <Container > agentOpt = pod .getSpec ().getContainers ().stream ()
321
+ .filter (c -> agentContainerName .equals (c .getName ()))
315
322
.findFirst ();
316
- Container jnlp = jnlpOpt .orElse (new ContainerBuilder ()
317
- .withName (JNLP_NAME )
318
- .withVolumeMounts (volumeMounts
319
- .values ()
320
- .toArray (new VolumeMount [volumeMounts .values ().size ()]))
323
+ Container agentContainer = agentOpt .orElse (new ContainerBuilder ()
324
+ .withName (agentContainerName )
325
+ .withVolumeMounts (volumeMounts .values ().toArray (VolumeMount []::new ))
321
326
.build ());
322
- if (! jnlpOpt . isPresent ()) {
323
- pod .getSpec ().getContainers ().add (jnlp );
327
+ if (agentOpt . isEmpty ()) {
328
+ pod .getSpec ().getContainers ().add (agentContainer );
324
329
}
330
+ var workingDir = agentContainer .getWorkingDir ();
325
331
pod .getSpec ().getContainers ().stream ()
326
332
.filter (c -> c .getWorkingDir () == null )
327
- .forEach (c -> c .setWorkingDir (jnlp . getWorkingDir () ));
328
- if (StringUtils .isBlank (jnlp .getImage ())) {
329
- String jnlpImage = DEFAULT_JNLP_IMAGE ;
333
+ .forEach (c -> c .setWorkingDir (workingDir ));
334
+ if (StringUtils .isBlank (agentContainer .getImage ())) {
335
+ String agentImage = DEFAULT_AGENT_IMAGE ;
330
336
if (cloud != null && StringUtils .isNotEmpty (cloud .getJnlpregistry ())) {
331
- jnlpImage = Util .ensureEndsWith (cloud .getJnlpregistry (), "/" ) + jnlpImage ;
337
+ agentImage = Util .ensureEndsWith (cloud .getJnlpregistry (), "/" ) + agentImage ;
332
338
} else if (StringUtils .isNotEmpty (DEFAULT_JNLP_DOCKER_REGISTRY_PREFIX )) {
333
- jnlpImage = Util .ensureEndsWith (DEFAULT_JNLP_DOCKER_REGISTRY_PREFIX , "/" ) + jnlpImage ;
339
+ agentImage = Util .ensureEndsWith (DEFAULT_JNLP_DOCKER_REGISTRY_PREFIX , "/" ) + agentImage ;
334
340
}
335
- jnlp .setImage (jnlpImage );
341
+ agentContainer .setImage (agentImage );
336
342
}
337
343
Map <String , EnvVar > envVars = new HashMap <>();
338
- envVars .putAll (jnlpEnvVars ( jnlp . getWorkingDir () ));
344
+ envVars .putAll (agentEnvVars ( workingDir ));
339
345
envVars .putAll (defaultEnvVars (template .getEnvVars ()));
340
- Optional .ofNullable (jnlp .getEnv ()).ifPresent (jnlpEnv -> {
341
- jnlpEnv .forEach (var -> envVars .put (var .getName (), var ));
346
+ Optional .ofNullable (agentContainer .getEnv ()).ifPresent (agentEnv -> {
347
+ agentEnv .forEach (var -> envVars .put (var .getName (), var ));
342
348
});
343
- jnlp .setEnv (new ArrayList <>(envVars .values ()));
344
- if (jnlp .getResources () == null ) {
349
+ if (template .isAgentInjection ()) {
350
+ var agentVolumeMountBuilder =
351
+ new VolumeMountBuilder ().withName ("jenkins-agent" ).withMountPath ("/jenkins-agent" );
352
+ var oldInitContainers = pod .getSpec ().getInitContainers ();
353
+ var jenkinsAgentInitContainer = new ContainerBuilder ()
354
+ .withName ("set-up-jenkins-agent" )
355
+ .withImage (DEFAULT_AGENT_IMAGE )
356
+ .withCommand (
357
+ "/bin/sh" ,
358
+ "-c" ,
359
+ "cp $(command -v jenkins-agent) " + JENKINS_AGENT_LAUNCHER_SCRIPT_LOCATION + ";"
360
+ + "cp /usr/share/jenkins/agent.jar " + JENKINS_AGENT_AGENT_JAR )
361
+ .withVolumeMounts (agentVolumeMountBuilder .build ())
362
+ .build ();
363
+ if (oldInitContainers != null ) {
364
+ var newInitContainers = new ArrayList <>(oldInitContainers );
365
+ newInitContainers .add (jenkinsAgentInitContainer );
366
+ pod .getSpec ().setInitContainers (newInitContainers );
367
+ } else {
368
+ pod .getSpec ().setInitContainers (List .of (jenkinsAgentInitContainer ));
369
+ }
370
+ var oldVolumes = pod .getSpec ().getVolumes ();
371
+ var jenkinsAgentSharedVolume = new VolumeBuilder ()
372
+ .withName ("jenkins-agent" )
373
+ .withNewEmptyDir ()
374
+ .and ()
375
+ .build ();
376
+ if (oldVolumes != null ) {
377
+ var newVolumes = new ArrayList <>(oldVolumes );
378
+ newVolumes .add (jenkinsAgentSharedVolume );
379
+ pod .getSpec ().setVolumes (newVolumes );
380
+ } else {
381
+ pod .getSpec ().setVolumes (List .of (jenkinsAgentSharedVolume ));
382
+ }
383
+ var existingVolumeMounts = agentContainer .getVolumeMounts ();
384
+ if (existingVolumeMounts != null ) {
385
+ var newVolumeMounts = new ArrayList <>(existingVolumeMounts );
386
+ newVolumeMounts .add (agentVolumeMountBuilder .withReadOnly ().build ());
387
+ agentContainer .setVolumeMounts (newVolumeMounts );
388
+ } else {
389
+ agentContainer .setVolumeMounts (
390
+ List .of (agentVolumeMountBuilder .withReadOnly ().build ()));
391
+ }
392
+ agentContainer .setWorkingDir (DEFAULT_WORKING_DIR );
393
+ agentContainer .setCommand (List .of (JENKINS_AGENT_LAUNCHER_SCRIPT_LOCATION ));
394
+ agentContainer .setArgs (List .of ());
395
+ envVars .put (
396
+ JENKINS_AGENT_FILE_ENVVAR ,
397
+ new EnvVarBuilder ()
398
+ .withName (JENKINS_AGENT_FILE_ENVVAR )
399
+ .withValue (JENKINS_AGENT_AGENT_JAR )
400
+ .build ());
401
+ }
402
+ agentContainer .setEnv (new ArrayList <>(envVars .values ()));
403
+ if (agentContainer .getResources () == null ) {
345
404
346
405
Map <String , Quantity > reqMap = new HashMap <>();
347
406
Map <String , Quantity > limMap = new HashMap <>();
@@ -361,7 +420,7 @@ public Pod build() {
361
420
.withLimits (limMap )
362
421
.build ();
363
422
364
- jnlp .setResources (reqs );
423
+ agentContainer .setResources (reqs );
365
424
}
366
425
if (cloud != null ) {
367
426
pod = PodDecorator .decorateAll (cloud , pod );
@@ -406,9 +465,9 @@ private Map<String, EnvVar> defaultEnvVars(Collection<TemplateEnvVar> globalEnvV
406
465
return envVarsMap ;
407
466
}
408
467
409
- private Map <String , EnvVar > jnlpEnvVars (String workingDir ) {
468
+ private Map <String , EnvVar > agentEnvVars (String workingDir ) {
410
469
if (workingDir == null ) {
411
- workingDir = ContainerTemplate . DEFAULT_WORKING_DIR ;
470
+ workingDir = DEFAULT_WORKING_DIR ;
412
471
}
413
472
// Last-write wins map of environment variable names to values
414
473
HashMap <String , String > env = new HashMap <>();
@@ -462,7 +521,7 @@ private Container createContainer(
462
521
Map <String , EnvVar > envVarsMap = new HashMap <>();
463
522
String workingDir = substituteEnv (containerTemplate .getWorkingDir ());
464
523
if (JNLP_NAME .equals (containerTemplate .getName ())) {
465
- envVarsMap .putAll (jnlpEnvVars (workingDir ));
524
+ envVarsMap .putAll (agentEnvVars (workingDir ));
466
525
}
467
526
envVarsMap .putAll (defaultEnvVars (globalEnvVars ));
468
527
@@ -541,7 +600,7 @@ private Container createContainer(
541
600
private VolumeMount getDefaultVolumeMount (@ CheckForNull String workingDir ) {
542
601
String wd = workingDir ;
543
602
if (wd == null ) {
544
- wd = ContainerTemplate . DEFAULT_WORKING_DIR ;
603
+ wd = DEFAULT_WORKING_DIR ;
545
604
LOGGER .log (Level .FINE , "Container workingDir is null, defaulting to {0}" , wd );
546
605
}
547
606
return new VolumeMountBuilder ()
0 commit comments