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