Skip to content

Commit

Permalink
[JENKINS-72592] Fix inbound agent connector NPE on Jenkins 2.437+ (#1049
Browse files Browse the repository at this point in the history
)

* Updating `DockerComputerJNLPConnector`

* Compilation error

* Spotless

* Add JNLP connector example to docs

---------

Co-authored-by: Mark Waite <[email protected]>
  • Loading branch information
jglick and MarkEWaite authored Feb 12, 2024
1 parent 84cce22 commit de7a806
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 51 deletions.
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,51 @@ and/or using the [JCasC plugin](https://plugins.jenkins.io/configuration-as-code

If you're unsure which method to use, use JCasC.

### JCasC plugin

Install the [configuration-as-code plugin](https://plugins.jenkins.io/configuration-as-code/) and follow [its example](https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos/docker).

As another alternative, a Docker daemon can listen to requests from remote hosts by following the [Docker documentation](https://docs.docker.com/config/daemon/remote-access/).
The following configuration as code example creates a cloud named "my-docker-cloud" that uses the docker daemon at port 2375 on dockerhost.example.com to run up to 3 containerized agents at a time.
Agents run the [Jenkins Alpine inbound agent container image](https://hub.docker.com/r/jenkins/inbound-agent) with Java 21.
Thay use an inbound connection and run as the user ID 1000 with the home directory "/home/jenkins/agent".

```yaml
jenkins:
clouds:
- docker:
containerCap: 3
dockerApi:
connectTimeout: 23
dockerHost:
uri: "tcp://dockerhost.example.com:2375"
readTimeout: 43
errorDuration: 313
name: "my-docker-cloud"
templates:
- connector:
jnlp:
jenkinsUrl: "https://jenkins.example.com/"
user: "1000"
dockerTemplateBase:
cpuPeriod: 0
cpuQuota: 0
image: "jenkins/inbound-agent:latest-alpine-jdk21"
labelString: "alpine jdk21 alpine-jdk21 git-2.43"
name: "alpine-jdk21"
pullTimeout: 171
remoteFs: "/home/jenkins/agent"
```
### Groovy script
For example, this
[configuration script](docs/attachments/docker-plugin-configuration-script.groovy)
could be run automatically upon
[Jenkins post-initialization](https://wiki.jenkins.io/display/JENKINS/Post-initialization+script)
[Jenkins post-initialization](https://www.jenkins.io/doc/book/managing/groovy-hook-scripts/)
or through the
[Jenkins script console](https://wiki.jenkins.io/display/JENKINS/Jenkins+Script+Console).
[Jenkins script console](https://www.jenkins.io/doc/book/managing/script-console/).
If run,
this script will configure the docker-plugin to look for a docker daemon running within the same OS as the Jenkins controller
(connecting to Docker service through `unix:///var/run/docker.sock`)
and with the containers connecting to Jenkins using the "attach" method.

### JCasC plugin

Install the [configuration-as-code plugin](https://plugins.jenkins.io/configuration-as-code/) and follow [its example](https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos/docker).
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
<properties>
<revision>1.6</revision>
<changelist>-SNAPSHOT</changelist>
<jenkins.version>2.414.3</jenkins.version>
<jenkins.version>2.440</jenkins.version>
<gitHubRepo>jenkinsci/docker-plugin</gitHubRepo>
<!-- Our unit-tests that talk to a real docker deamon aren't very stable -->
<surefire.rerunFailingTestsCount>3</surefire.rerunFailingTestsCount>
Expand All @@ -76,7 +76,7 @@
<dependencies>
<dependency>
<groupId>io.jenkins.tools.bom</groupId>
<artifactId>bom-2.414.x</artifactId>
<artifactId>bom-2.440.x</artifactId>
<version>2791.v707dc5a_1626d</version>
<type>pom</type>
<scope>import</scope>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.nirima.jenkins.plugins.docker.launcher;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.slaves.JNLPLauncher;
import io.jenkins.docker.connector.DockerComputerConnector;
import io.jenkins.docker.connector.DockerComputerJNLPConnector;

Expand All @@ -21,13 +20,11 @@
@Deprecated
public class DockerComputerJNLPLauncher extends DockerComputerLauncher {

protected JNLPLauncher jnlpLauncher;

protected String user;

@Override
public DockerComputerConnector convertToConnector() {
final DockerComputerJNLPConnector connector = new DockerComputerJNLPConnector(jnlpLauncher);
final DockerComputerJNLPConnector connector = new DockerComputerJNLPConnector();

Check warning on line 27 in src/main/java/com/nirima/jenkins/plugins/docker/launcher/DockerComputerJNLPLauncher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 27 is not covered by tests
connector.setUser(user);
return connector;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static com.nirima.jenkins.plugins.docker.utils.JenkinsUtils.bldToString;
import static com.nirima.jenkins.plugins.docker.utils.JenkinsUtils.endToString;
import static com.nirima.jenkins.plugins.docker.utils.JenkinsUtils.fixEmpty;
import static com.nirima.jenkins.plugins.docker.utils.JenkinsUtils.makeCopy;
import static com.nirima.jenkins.plugins.docker.utils.JenkinsUtils.splitAndFilterEmpty;
import static com.nirima.jenkins.plugins.docker.utils.JenkinsUtils.startToString;

Expand Down Expand Up @@ -43,23 +42,14 @@ public class DockerComputerJNLPConnector extends DockerComputerConnector {
@CheckForNull
private String user;

private final JNLPLauncher jnlpLauncher;

@CheckForNull
private String jenkinsUrl;

@CheckForNull
private String[] entryPointArguments;

@Restricted(NoExternalUse.class)
public DockerComputerJNLPConnector() {
this(new JNLPLauncher(false));
}

@DataBoundConstructor
public DockerComputerJNLPConnector(JNLPLauncher jnlpLauncher) {
this.jnlpLauncher = jnlpLauncher;
}
public DockerComputerJNLPConnector() {}

@CheckForNull
public String getUser() {
Expand Down Expand Up @@ -113,16 +103,12 @@ public DockerComputerJNLPConnector withEntryPointArguments(String... args) {
return this;
}

public JNLPLauncher getJnlpLauncher() {
return jnlpLauncher;
}

@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(entryPointArguments);
result = prime * result + Objects.hash(jenkinsUrl, jnlpLauncher, user);
result = prime * result + Objects.hash(jenkinsUrl, user);
return result;
}

Expand All @@ -137,15 +123,13 @@ public boolean equals(Object obj) {
DockerComputerJNLPConnector other = (DockerComputerJNLPConnector) obj;
return Arrays.equals(entryPointArguments, other.entryPointArguments)
&& Objects.equals(jenkinsUrl, other.jenkinsUrl)
&& Objects.equals(jnlpLauncher, other.jnlpLauncher)
&& Objects.equals(user, other.user);
}

@Override
public String toString() {
final StringBuilder sb = startToString(this);
bldToString(sb, "user", user);
bldToString(sb, "jnlpLauncher", jnlpLauncher);
bldToString(sb, "jenkinsUrl", jenkinsUrl);
bldToString(sb, "entryPointArguments", entryPointArguments);
endToString(sb);
Expand All @@ -156,7 +140,7 @@ public String toString() {
protected ComputerLauncher createLauncher(
final DockerAPI api, final String workdir, final InspectContainerResponse inspect, TaskListener listener)
throws IOException, InterruptedException {
return makeCopy(jnlpLauncher);
return new JNLPLauncher();
}

@Restricted(NoExternalUse.class)
Expand All @@ -165,11 +149,7 @@ enum ArgumentVariables {
Secret(
"JNLP_SECRET",
"The secret that must be passed to agent.jar's -secret argument to pass JNLP authentication."), //
JenkinsUrl("JENKINS_URL", "The Jenkins root URL."), //
TunnelArgument(
"TUNNEL_ARG",
"If a JNLP tunnel has been specified then this evaluates to '-tunnel', otherwise it evaluates to the empty string"), //
TunnelValue("TUNNEL", "The JNLP tunnel value");
JenkinsUrl("JENKINS_URL", "The Jenkins root URL.");
private final String name;
private final String description;

Expand All @@ -187,8 +167,7 @@ public String getDescription() {
}
}

private static final String DEFAULT_ENTRY_POINT_ARGUMENTS = "${" + ArgumentVariables.TunnelArgument.getName()
+ "}\n${" + ArgumentVariables.TunnelValue.getName() + "}\n-url\n${" + ArgumentVariables.JenkinsUrl.getName()
private static final String DEFAULT_ENTRY_POINT_ARGUMENTS = "-url\n${" + ArgumentVariables.JenkinsUrl.getName()
+ "}\n${" + ArgumentVariables.Secret.getName() + "}\n${" + ArgumentVariables.NodeName.getName() + "}";

@Override
Expand All @@ -198,8 +177,7 @@ public void beforeContainerCreated(DockerAPI api, String workdir, CreateContaine
StringUtils.isEmpty(jenkinsUrl) ? Jenkins.get().getRootUrl() : jenkinsUrl;
final String nodeName = DockerTemplate.getNodeNameFromContainerConfig(cmd);
final String secret = JnlpAgentReceiver.SLAVE_SECRET.mac(nodeName);
final EnvVars knownVariables =
calculateVariablesForVariableSubstitution(nodeName, secret, jnlpLauncher.tunnel, effectiveJenkinsUrl);
final EnvVars knownVariables = calculateVariablesForVariableSubstitution(nodeName, secret, effectiveJenkinsUrl);
final String configuredArgString = getEntryPointArgumentsString();
final String effectiveConfiguredArgString =
StringUtils.isNotBlank(configuredArgString) ? configuredArgString : DEFAULT_ENTRY_POINT_ARGUMENTS;
Expand All @@ -224,7 +202,7 @@ public void beforeContainerStarted(DockerAPI api, String workdir, DockerTransien
}

private static EnvVars calculateVariablesForVariableSubstitution(
final String nodeName, final String secret, final String jnlpTunnel, final String jenkinsUrl)
final String nodeName, final String secret, final String jenkinsUrl)
throws IOException, InterruptedException {
final EnvVars knownVariables = new EnvVars();
final Jenkins j = Jenkins.get();
Expand All @@ -237,12 +215,6 @@ private static EnvVars calculateVariablesForVariableSubstitution(
case JenkinsUrl:
argValue = jenkinsUrl;
break;
case TunnelArgument:
argValue = StringUtils.isNotBlank(jnlpTunnel) ? "-tunnel" : "";
break;
case TunnelValue:
argValue = jnlpTunnel;
break;
case Secret:
argValue = secret;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,4 @@
<f:expandableTextbox />
</f:entry>

<f:property field="jnlpLauncher"/>
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.nirima.jenkins.plugins.docker.DockerTemplate;
import com.nirima.jenkins.plugins.docker.DockerTemplateBase;
import hudson.slaves.JNLPLauncher;
import java.net.URI;
import jenkins.model.JenkinsLocationConfiguration;
import org.apache.commons.lang3.SystemUtils;
Expand Down Expand Up @@ -40,7 +39,7 @@ public void connectAgentViaJNLP() throws Exception {
JNLP_AGENT_IMAGE_IMAGENAME + ':' + getJenkinsDockerImageVersionForThisEnvironment();
final DockerTemplate template = new DockerTemplate(
new DockerTemplateBase(imagenameAndVersion),
new DockerComputerJNLPConnector(new JNLPLauncher(null))
new DockerComputerJNLPConnector()
.withUser(COMMON_IMAGE_USERNAME)
.withJenkinsUrl(uri.toString()),
getLabelForTemplate(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public String toString() {
return "attach";
}
};
final DockerComputerConnector jnlp = new DockerComputerJNLPConnector(null) {
final DockerComputerConnector jnlp = new DockerComputerJNLPConnector() {
@Override
public String toString() {
return "jnlpNotSerializable";
Expand Down

0 comments on commit de7a806

Please sign in to comment.