Skip to content

Commit 748b13a

Browse files
committed
support agent jdk injection
1 parent 94883c5 commit 748b13a

6 files changed

Lines changed: 112 additions & 5 deletions

File tree

src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ protected static MessageDigest getLabelDigestFunction() {
192192

193193
private boolean agentInjection;
194194

195+
private boolean agentJdkInjection;
196+
195197
/**
196198
* Persisted yaml fragment
197199
*/
@@ -649,6 +651,15 @@ public void setAgentInjection(boolean agentInjection) {
649651
this.agentInjection = agentInjection;
650652
}
651653

654+
public boolean isAgentJdkInjection() {
655+
return agentJdkInjection;
656+
}
657+
658+
@DataBoundSetter
659+
public void setAgentJdkInjection(boolean agentJdkInjection) {
660+
this.agentJdkInjection = agentJdkInjection;
661+
}
662+
652663
public List<TemplateEnvVar> getEnvVars() {
653664
if (envVars == null) {
654665
return Collections.emptyList();
@@ -1199,6 +1210,7 @@ public String toString() {
11991210
+ (!unwrapped ? "" : ", unwrapped=" + unwrapped)
12001211
+ (agentContainer == null ? "" : ", agentContainer='" + agentContainer + '\'')
12011212
+ (!agentInjection ? "" : ", agentInjection=" + agentInjection)
1213+
+ (!agentJdkInjection ? "" : ", agentJdkInjection=" + agentJdkInjection)
12021214
+ '}';
12031215
}
12041216
}

src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateBuilder.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,15 @@ public Pod build() {
342342
var agentVolumeMountBuilder =
343343
new VolumeMountBuilder().withName("jenkins-agent").withMountPath("/jenkins-agent");
344344
var oldInitContainers = pod.getSpec().getInitContainers();
345+
String initCommand = "cp $(command -v jenkins-agent) " + JENKINS_AGENT + "/jenkins-agent" + ";"
346+
+ "cp -R /usr/share/jenkins/. " + JENKINS_AGENT;
347+
if (template.isAgentJdkInjection()) {
348+
initCommand += ";" + "cp -R /opt/java/openjdk " + JENKINS_AGENT + "/jdk";
349+
}
345350
var jenkinsAgentInitContainer = new ContainerBuilder()
346351
.withName("set-up-jenkins-agent")
347352
.withImage(agentImage)
348-
.withCommand(
349-
"/bin/sh",
350-
"-c",
351-
"cp $(command -v jenkins-agent) " + JENKINS_AGENT + "/jenkins-agent" + ";"
352-
+ "cp -R /usr/share/jenkins/. " + JENKINS_AGENT)
353+
.withCommand("/bin/sh", "-c", initCommand)
353354
.withVolumeMounts(agentVolumeMountBuilder.build())
354355
.build();
355356
if (oldInitContainers != null) {
@@ -390,6 +391,14 @@ public Pod build() {
390391
.withName(JENKINS_AGENT_FILE_ENVVAR)
391392
.withValue(JENKINS_AGENT + "/agent.jar")
392393
.build());
394+
if (template.isAgentJdkInjection()) {
395+
envVars.put(
396+
"JENKINS_JAVA_BIN",
397+
new EnvVarBuilder()
398+
.withName("JENKINS_JAVA_BIN")
399+
.withValue(JENKINS_AGENT + "/jdk/bin/java")
400+
.build());
401+
}
393402
}
394403
agentContainer.setEnv(new ArrayList<>(envVars.values()));
395404
if (agentContainer.getResources() == null) {

src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ public static PodTemplate combine(PodTemplate parent, PodTemplate template) {
503503
podTemplate.setSupplementalGroups(h.resolve(PodTemplate::getSupplementalGroups, Objects::isNull));
504504
podTemplate.setAgentContainer(h.resolve(PodTemplate::getAgentContainer, PodTemplateUtils::isNullOrEmpty));
505505
podTemplate.setAgentInjection(h.resolve(PodTemplate::isAgentInjection, v -> !v));
506+
podTemplate.setAgentJdkInjection(h.resolve(PodTemplate::isAgentJdkInjection, v -> !v));
506507
if (template.isHostNetworkSet()) {
507508
podTemplate.setHostNetwork(template.isHostNetwork());
508509
} else if (parent.isHostNetworkSet()) {

src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/config.jelly

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ THE SOFTWARE.
5353
<f:checkbox/>
5454
</f:entry>
5555

56+
<f:entry field="agentJdkInjection" title="${%Inject JDK from agent image}">
57+
<f:checkbox/>
58+
</f:entry>
59+
5660
<f:entry field="containers" title="${%Containers}" description="${%List of container in the agent pod}">
5761
<f:repeatableHeteroProperty field="containers" hasHeader="true" addCaption="${%Add Container}"
5862
deleteCaption="${%Delete Container}" />
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<p>
2+
Injects the JDK from the agent injection image into the agent container when <strong>Inject Jenkins agent in agent container</strong> is enabled.
3+
</p>
4+
5+
<p>
6+
This is useful when your agent container is a minimal toolchain image (python, node, golang) that doesn't include Java,
7+
but the Jenkins agent needs Java to run.
8+
</p>
9+
10+
<p>
11+
When enabled, the init container copies the JDK from <code>/opt/java/openjdk</code> to <code>/jenkins-agent/jdk</code>
12+
and sets <code>JENKINS_JAVA_BIN=/jenkins-agent/jdk/bin/java</code>.
13+
</p>
14+
15+
<p><strong>Example use case:</strong></p>
16+
<pre>Agent container: python:3.11-slim (no Java)
17+
Agent injection image: jenkins/inbound-agent:alpine-jdk21 (has Java)
18+
Result: Agent runs with injected JDK</pre>
19+
20+
<p><strong>Trade-offs:</strong></p>
21+
<ul>
22+
<li>Adds 10-20 seconds to pod startup (copying ~100-200MB JDK)</li>
23+
<li>Requires additional volume space (~200MB per pod)</li>
24+
</ul>
25+
26+
<p>
27+
<strong>Note:</strong> Only applies when agent injection is enabled.
28+
If your agent container already has Java installed, leave this disabled.
29+
</p>

src/test/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateBuilderTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,58 @@ public void testValidateDockerRegistryPrefixOverrideForInitContainer(boolean dir
262262
assertThat(pod.getMetadata().getLabels(), hasEntry("jenkins", "slave"));
263263
}
264264

265+
@Test
266+
public void testAgentJdkInjection() throws Exception {
267+
PodTemplate template = new PodTemplate();
268+
template.setAgentInjection(true);
269+
template.setAgentJdkInjection(true);
270+
template.setYaml(loadYamlFile("pod-busybox.yaml"));
271+
setupStubs();
272+
273+
Pod pod = new PodTemplateBuilder(template, slave).build();
274+
275+
// Verify init container command includes JDK copy
276+
Container initContainer = pod.getSpec().getInitContainers().get(0);
277+
String command = String.join(" ", initContainer.getCommand());
278+
assertThat(command, containsString("cp -R /opt/java/openjdk /jenkins-agent/jdk"));
279+
280+
// Verify JENKINS_JAVA_BIN environment variable is set
281+
Container jnlpContainer = pod.getSpec().getContainers().stream()
282+
.filter(c -> "jnlp".equals(c.getName()))
283+
.findFirst()
284+
.orElseThrow();
285+
EnvVar javaBindEnvVar = jnlpContainer.getEnv().stream()
286+
.filter(env -> "JENKINS_JAVA_BIN".equals(env.getName()))
287+
.findFirst()
288+
.orElseThrow();
289+
assertEquals("/jenkins-agent/jdk/bin/java", javaBindEnvVar.getValue());
290+
}
291+
292+
@Test
293+
public void testAgentJdkInjectionDisabled() throws Exception {
294+
PodTemplate template = new PodTemplate();
295+
template.setAgentInjection(true);
296+
template.setAgentJdkInjection(false);
297+
template.setYaml(loadYamlFile("pod-busybox.yaml"));
298+
setupStubs();
299+
300+
Pod pod = new PodTemplateBuilder(template, slave).build();
301+
302+
// Verify init container command does NOT include JDK copy
303+
Container initContainer = pod.getSpec().getInitContainers().get(0);
304+
String command = String.join(" ", initContainer.getCommand());
305+
assertThat(command, not(containsString("cp -R /opt/java/openjdk")));
306+
307+
// Verify JENKINS_JAVA_BIN is NOT set
308+
Container jnlpContainer = pod.getSpec().getContainers().stream()
309+
.filter(c -> "jnlp".equals(c.getName()))
310+
.findFirst()
311+
.orElseThrow();
312+
boolean hasJavaBinEnv = jnlpContainer.getEnv().stream()
313+
.anyMatch(env -> "JENKINS_JAVA_BIN".equals(env.getName()));
314+
assertFalse(hasJavaBinEnv);
315+
}
316+
265317
@Test
266318
@Issue("JENKINS-50525")
267319
public void testBuildWithCustomWorkspaceVolume() throws Exception {

0 commit comments

Comments
 (0)