From f606866f592b308365869863583edb281974e964 Mon Sep 17 00:00:00 2001
From: strangelookingnerd
<49242855+strangelookingnerd@users.noreply.github.com>
Date: Fri, 11 Jul 2025 09:36:40 +0200
Subject: [PATCH 1/3] Replace docker-fixtures with testcontainers
---
pom.xml | 6 +-
.../plugins/workflow/ArtifactManagerTest.java | 59 ++++++-------------
.../workflow/ArtifactManagerTest/Dockerfile | 25 ++++++++
3 files changed, 47 insertions(+), 43 deletions(-)
create mode 100644 src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile
diff --git a/pom.xml b/pom.xml
index 89405fb0..a2cbfb66 100644
--- a/pom.xml
+++ b/pom.xml
@@ -123,9 +123,9 @@
test
- org.jenkins-ci.test
- docker-fixtures
- 200.v22a_e8766731c
+ org.testcontainers
+ testcontainers
+ 1.21.3
test
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java
index 141ac8e8..7713f7af 100644
--- a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java
+++ b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java
@@ -58,7 +58,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
-import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
@@ -74,14 +73,14 @@
import jenkins.util.VirtualFile;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.workflow.flow.StashManager;
-import org.jenkinsci.test.acceptance.docker.Docker;
-import org.jenkinsci.test.acceptance.docker.DockerImage;
-import org.jenkinsci.test.acceptance.docker.fixtures.JavaContainer;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.images.builder.ImageFromDockerfile;
/**
* {@link #artifactArchiveAndDelete} and variants allow an implementation of {@link ArtifactManager} plus {@link VirtualFile} to be run through a standard gantlet of tests.
@@ -90,21 +89,18 @@ public class ArtifactManagerTest {
@Rule public JenkinsRule r = new JenkinsRule();
@Rule public LoggerRule logging = new LoggerRule();
-
- private static DockerImage image;
-
- @BeforeClass public static void doPrepareImage() throws Exception {
- image = prepareImage();
+
+ private static GenericContainer> container;
+
+ @BeforeClass public static void doPrepareImage() {
+ container = prepareImage();
}
- /**
- * @deprecated Not actually used externally.
- */
- @Deprecated
- public static @CheckForNull DockerImage prepareImage() throws Exception {
- Docker docker = new Docker();
- if (!Functions.isWindows() && docker.isAvailable()) { // TODO: Windows agents on ci.jenkins.io have Docker, but cannot build the image.
- return docker.build(JavaContainer.class);
+ public static @CheckForNull GenericContainer> prepareImage() {
+ if (!Functions.isWindows() && DockerClientFactory.instance().isDockerAvailable()) { // TODO: Windows agents on ci.jenkins.io have Docker, but cannot build the image.
+ return new GenericContainer<>(new ImageFromDockerfile("java17-ssh", false)
+ .withFileFromClasspath("Dockerfile", ArtifactManagerTest.class.getName().replace('.', '/') + "/Dockerfile"))
+ .withExposedPorts(22);
} else {
System.err.println("No Docker support; falling back to running tests against an agent in a process on the same machine.");
return null;
@@ -119,14 +115,13 @@ private static void wrapInContainer(@NonNull JenkinsRule r, @CheckForNull Artifa
if (factory != null) {
ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(factory);
}
- JavaContainer runningContainer = null;
try {
DumbSlave agent;
- if (image != null) {
- runningContainer = image.start(JavaContainer.class).start();
+ if (container != null) {
+ container.start();
StandardUsernameCredentials creds = new UsernamePasswordCredentialsImpl(CredentialsScope.SYSTEM, "test", "desc", "test", "test");
CredentialsProvider.lookupStores(Jenkins.get()).iterator().next().addCredentials(Domain.global(), creds);
- agent = new DumbSlave("test-agent", "/home/test/slave", new SSHLauncher(runningContainer.ipBound(22), runningContainer.port(22), "test"));
+ agent = new DumbSlave("test-agent", "/home/test/slave", new SSHLauncher(container.getHost(), container.getMappedPort(22), "test"));
Jenkins.get().addNode(agent);
r.waitOnline(agent);
} else {
@@ -142,8 +137,8 @@ private static void wrapInContainer(@NonNull JenkinsRule r, @CheckForNull Artifa
FreeStyleBuild b = r.buildAndAssertSuccess(p);
f.apply(agent, p, b, ws);
} finally {
- if (runningContainer != null) {
- runningContainer.close();
+ if (container != null) {
+ container.stop();
}
}
}
@@ -161,10 +156,6 @@ public static void artifactArchive(@NonNull JenkinsRule r, @CheckForNull Artifac
assertTrue(b.getArtifactManager().root().child("file").isFile());
});
}
- @Deprecated
- public static void artifactArchive(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception {
- artifactArchive(r, factory, weirdCharacters);
- }
/**
* Test artifact archiving in a plain manager.
@@ -180,10 +171,6 @@ public static void artifactArchiveAndDelete(@NonNull JenkinsRule r, @CheckForNul
assertFalse(b.getArtifactManager().delete());
});
}
- @Deprecated
- public static void artifactArchiveAndDelete(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception {
- artifactArchiveAndDelete(r, factory, weirdCharacters);
- }
/**
* Test stashing and unstashing with a {@link StashManager.StashAwareArtifactManager} that does not honor deletion requests.
@@ -197,10 +184,6 @@ public static void artifactStash(@NonNull JenkinsRule r, @CheckForNull ArtifactM
assertTrue(ws.child("file").exists());
}));
}
- @Deprecated
- public static void artifactStash(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception {
- artifactStash(r, factory, weirdCharacters);
- }
/**
* Test stashing and unstashing with a {@link StashManager.StashAwareArtifactManager} with standard behavior.
@@ -218,10 +201,6 @@ public static void artifactStashAndDelete(@NonNull JenkinsRule r, @CheckForNull
assertFalse(ws.child("file").exists());
}));
}
- @Deprecated
- public static void artifactStashAndDelete(@NonNull JenkinsRule r, @CheckForNull ArtifactManagerFactory factory, boolean weirdCharacters, @CheckForNull DockerImage image) throws Exception {
- artifactStashAndDelete(r, factory, weirdCharacters);
- }
/**
* Creates a variety of files in a directory structure designed to exercise interesting aspects of {@link VirtualFile}.
@@ -471,7 +450,7 @@ private static void assertNonexistent(VirtualFile f) throws IOException {
@Test public void standard() throws Exception {
logging.record(StandardArtifactManager.class, Level.FINE);
// Who knows about weird characters on NTFS; also case-sensitivity could confuse things
- artifactArchiveAndDelete(r, null, !Functions.isWindows(), image);
+ artifactArchiveAndDelete(r, null, !Functions.isWindows());
}
}
diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile b/src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile
new file mode 100644
index 00000000..dfcf9c21
--- /dev/null
+++ b/src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile
@@ -0,0 +1,25 @@
+FROM ubuntu:noble
+
+# install requirements
+RUN apt-get update -y && \
+ apt-get install --no-install-recommends -y \
+ openjdk-17-jdk-headless \
+ openssh-server \
+ locales
+
+RUN mkdir -p /var/run/sshd
+
+# create a test user
+RUN useradd test -d /home/test && \
+ mkdir -p /home/test/.ssh && \
+ chown -R test:test /home/test && \
+ echo "test:test" | chpasswd
+
+# https://stackoverflow.com/a/38553499/12916
+RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
+ dpkg-reconfigure --frontend=noninteractive locales && \
+ update-locale LANG=en_US.UTF-8
+ENV LANG en_US.UTF-8
+
+# run SSHD in the foreground with error messages to stderr
+ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"]
From 9b957b52bc74928df4e66a94b86178e173605820 Mon Sep 17 00:00:00 2001
From: Jesse Glick
Date: Mon, 14 Jul 2025 13:42:13 -0400
Subject: [PATCH 2/3] Using `OutboundAgent`
---
pom.xml | 25 ++++++++-
.../plugins/workflow/ArtifactManagerTest.java | 51 +++----------------
.../workflow/ArtifactManagerTest/Dockerfile | 25 ---------
3 files changed, 30 insertions(+), 71 deletions(-)
delete mode 100644 src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile
diff --git a/pom.xml b/pom.xml
index a2cbfb66..10d93ef1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,10 +74,22 @@
io.jenkins.tools.bom
bom-${jenkins.baseline}.x
- 4948.vcf1d17350668
+ 5015.vb_52d36583443
import
pom
+
+
+ org.jenkins-ci.plugins
+ ssh-slaves
+ 3.1069.v30991b_9a_4f68
+
+
+ org.jenkins-ci.plugins
+ ssh-slaves
+ tests
+ 3.1069.v30991b_9a_4f68
+
@@ -133,6 +145,17 @@
ssh-slaves
test
+
+ org.jenkins-ci.plugins
+ ssh-slaves
+ tests
+ test
+
+
+ io.jenkins.plugins.mina-sshd-api
+ mina-sshd-api-common
+ test
+
org.jenkins-ci.plugins
apache-httpcomponents-client-4-api
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java
index 7713f7af..c8d4f8b6 100644
--- a/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java
+++ b/src/test/java/org/jenkinsci/plugins/workflow/ArtifactManagerTest.java
@@ -1,4 +1,4 @@
-/*
+/*d
* The MIT License
*
* Copyright 2018 CloudBees, Inc.
@@ -35,11 +35,6 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import com.cloudbees.plugins.credentials.CredentialsProvider;
-import com.cloudbees.plugins.credentials.CredentialsScope;
-import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
-import com.cloudbees.plugins.credentials.domains.Domain;
-import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import hudson.AbortException;
import hudson.EnvVars;
import hudson.ExtensionList;
@@ -50,7 +45,6 @@
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.TaskListener;
-import hudson.plugins.sshslaves.SSHLauncher;
import hudson.remoting.Callable;
import hudson.slaves.DumbSlave;
import hudson.tasks.ArtifactArchiver;
@@ -67,20 +61,16 @@
import jenkins.model.ArtifactManager;
import jenkins.model.ArtifactManagerConfiguration;
import jenkins.model.ArtifactManagerFactory;
-import jenkins.model.Jenkins;
import jenkins.model.StandardArtifactManager;
import jenkins.security.MasterToSlaveCallable;
import jenkins.util.VirtualFile;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.workflow.flow.StashManager;
-import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;
-import org.testcontainers.DockerClientFactory;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.images.builder.ImageFromDockerfile;
+import test.ssh_agent.OutboundAgent;
/**
* {@link #artifactArchiveAndDelete} and variants allow an implementation of {@link ArtifactManager} plus {@link VirtualFile} to be run through a standard gantlet of tests.
@@ -90,23 +80,6 @@ public class ArtifactManagerTest {
@Rule public JenkinsRule r = new JenkinsRule();
@Rule public LoggerRule logging = new LoggerRule();
- private static GenericContainer> container;
-
- @BeforeClass public static void doPrepareImage() {
- container = prepareImage();
- }
-
- public static @CheckForNull GenericContainer> prepareImage() {
- if (!Functions.isWindows() && DockerClientFactory.instance().isDockerAvailable()) { // TODO: Windows agents on ci.jenkins.io have Docker, but cannot build the image.
- return new GenericContainer<>(new ImageFromDockerfile("java17-ssh", false)
- .withFileFromClasspath("Dockerfile", ArtifactManagerTest.class.getName().replace('.', '/') + "/Dockerfile"))
- .withExposedPorts(22);
- } else {
- System.err.println("No Docker support; falling back to running tests against an agent in a process on the same machine.");
- return null;
- }
- }
-
/**
* Creates an agent, in a Docker container when possible, calls {@link #setUpWorkspace}, then runs some tests.
*/
@@ -115,18 +88,10 @@ private static void wrapInContainer(@NonNull JenkinsRule r, @CheckForNull Artifa
if (factory != null) {
ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(factory);
}
- try {
- DumbSlave agent;
- if (container != null) {
- container.start();
- StandardUsernameCredentials creds = new UsernamePasswordCredentialsImpl(CredentialsScope.SYSTEM, "test", "desc", "test", "test");
- CredentialsProvider.lookupStores(Jenkins.get()).iterator().next().addCredentials(Domain.global(), creds);
- agent = new DumbSlave("test-agent", "/home/test/slave", new SSHLauncher(container.getHost(), container.getMappedPort(22), "test"));
- Jenkins.get().addNode(agent);
- r.waitOnline(agent);
- } else {
- agent = r.createOnlineSlave();
- }
+ try (var outboundAgent = new OutboundAgent()) {
+ OutboundAgent.createAgent(r, "remote", outboundAgent.start());
+ var agent = (DumbSlave) r.jenkins.getNode("remote");
+ r.waitOnline(agent);
FreeStyleProject p = r.createFreeStyleProject();
p.setAssignedNode(agent);
FilePath ws = agent.getWorkspaceFor(p);
@@ -136,10 +101,6 @@ private static void wrapInContainer(@NonNull JenkinsRule r, @CheckForNull Artifa
p.getPublishersList().add(aa);
FreeStyleBuild b = r.buildAndAssertSuccess(p);
f.apply(agent, p, b, ws);
- } finally {
- if (container != null) {
- container.stop();
- }
}
}
diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile b/src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile
deleted file mode 100644
index dfcf9c21..00000000
--- a/src/test/resources/org/jenkinsci/plugins/workflow/ArtifactManagerTest/Dockerfile
+++ /dev/null
@@ -1,25 +0,0 @@
-FROM ubuntu:noble
-
-# install requirements
-RUN apt-get update -y && \
- apt-get install --no-install-recommends -y \
- openjdk-17-jdk-headless \
- openssh-server \
- locales
-
-RUN mkdir -p /var/run/sshd
-
-# create a test user
-RUN useradd test -d /home/test && \
- mkdir -p /home/test/.ssh && \
- chown -R test:test /home/test && \
- echo "test:test" | chpasswd
-
-# https://stackoverflow.com/a/38553499/12916
-RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
- dpkg-reconfigure --frontend=noninteractive locales && \
- update-locale LANG=en_US.UTF-8
-ENV LANG en_US.UTF-8
-
-# run SSHD in the foreground with error messages to stderr
-ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"]
From ae85c3f66a8e6a8030a20fa49dc61cf82f4d7d4f Mon Sep 17 00:00:00 2001
From: Jesse Glick
Date: Fri, 18 Jul 2025 11:58:38 -0400
Subject: [PATCH 3/3] Dep released
---
pom.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pom.xml b/pom.xml
index 10d93ef1..a81594b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,17 +78,17 @@
import
pom
-
+
org.jenkins-ci.plugins
ssh-slaves
- 3.1069.v30991b_9a_4f68
+ 3.1071.v0d059c7b_c555
org.jenkins-ci.plugins
ssh-slaves
tests
- 3.1069.v30991b_9a_4f68
+ 3.1071.v0d059c7b_c555