diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java index ce27ceaa4..4dc5752d0 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java @@ -36,9 +36,12 @@ import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -336,6 +339,8 @@ public String whoAmI() throws IOException, InterruptedException { } + private static final Pattern hostnameMount = Pattern.compile("/containers/([a-z0-9]{64})/hostname"); + /** * Checks if this {@link DockerClient} instance is running inside a container and returns the id of the container * if so. @@ -349,10 +354,26 @@ public Optional getContainerIdIfContainerized() throws IOException, Inte return Optional.absent(); } FilePath cgroupFile = node.createPath("/proc/self/cgroup"); - if (cgroupFile == null || !cgroupFile.exists()) { - return Optional.absent(); + if (cgroupFile != null && cgroupFile.exists()) { + Optional containerId = ControlGroup.getContainerId(cgroupFile); + if (containerId.isPresent()) { + return containerId; + } + } + // cgroup v2 + FilePath mountInfo = node.createPath("/proc/1/mountinfo"); + if (mountInfo != null && mountInfo.exists()) { + try (InputStream is = mountInfo.read(); Reader r = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(r)) { + String line; + while ((line = br.readLine()) != null) { + Matcher m = hostnameMount.matcher(line); + if (m.find()) { + return Optional.of(m.group(1)); + } + } + } } - return ControlGroup.getContainerId(cgroupFile); + return Optional.absent(); } public ContainerRecord getContainerRecord(@NonNull EnvVars launchEnv, String containerId) throws IOException, InterruptedException { diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java index f636631a4..a08b98cdf 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java @@ -32,9 +32,11 @@ import hudson.model.FileParameterValue; import hudson.model.Result; import hudson.model.TaskListener; +import hudson.slaves.DumbSlave; import hudson.tools.ToolProperty; import hudson.util.ArgumentListBuilder; import hudson.util.Secret; +import hudson.util.StreamTaskListener; import java.io.File; import java.util.Collection; import java.util.Collections; @@ -48,6 +50,7 @@ import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.FileUtils; import org.hamcrest.Matchers; +import static org.hamcrest.Matchers.is; import org.jenkinsci.lib.configprovider.ConfigProvider; import org.jenkinsci.lib.configprovider.model.Config; import org.jenkinsci.plugins.configfiles.custom.CustomConfig; @@ -66,6 +69,7 @@ import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution; import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; import org.junit.Assume; +import static org.junit.Assume.assumeTrue; import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Test; @@ -76,6 +80,7 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.SimpleCommandLauncher; import org.jvnet.hudson.test.TestExtension; import org.kohsuke.stapler.DataBoundConstructor; @@ -455,4 +460,35 @@ private static final class Execution extends SynchronousNonBlockingStepExecution } } + @Issue("JENKINS-64608") + @Test public void runningInsideContainer() throws Throwable { + story.then(r -> { + DockerTestUtil.assumeDocker(); + assumeTrue("have docker.sock", new File("/var/run/docker.sock").exists()); + TaskListener taskListener = StreamTaskListener.fromStderr(); + Launcher.LocalLauncher launcher = new Launcher.LocalLauncher(taskListener); + int status = launcher.launch().stdout(taskListener). + pwd(new File(WithContainerStepTest.class.getResource("agent-with-docker/Dockerfile").toURI()).getParentFile()). + cmds(DockerTool.getExecutable(null, null, null, null), "build", "-t", "agent-with-docker", "."). + start(). + joinWithTimeout(DockerClient.CLIENT_TIMEOUT, TimeUnit.SECONDS, taskListener); + Assume.assumeThat("Built agent-with-docker image", status, is(0)); + DumbSlave s = new DumbSlave("dockerized", "/home/jenkins/agent", + new SimpleCommandLauncher("docker run -i --rm --init -v /var/run/docker.sock:/var/run/docker.sock agent-with-docker java -jar /usr/share/jenkins/agent.jar")); + r.jenkins.addNode(s); + r.waitOnline(s); + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition( + "node('dockerized') {\n" + + " sh 'which docker && docker version'\n" + + " withDockerContainer('httpd:2.4.12') {\n" + + " sh 'cp /usr/local/apache2/conf/extra/httpd-userdir.conf .; ls -la'\n" + + " }\n" + + " sh 'ls -la; cat *.conf'\n" + + "}", true)); + WorkflowRun b = r.buildAndAssertSuccess(p); + r.assertLogContains("dockerized seems to be running inside container ", b); + }); + } + } diff --git a/src/test/resources/org/jenkinsci/plugins/docker/workflow/agent-with-docker/Dockerfile b/src/test/resources/org/jenkinsci/plugins/docker/workflow/agent-with-docker/Dockerfile new file mode 100644 index 000000000..e8438aeb2 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/docker/workflow/agent-with-docker/Dockerfile @@ -0,0 +1,8 @@ +FROM jenkins/agent:4.10-5-jdk11 +USER root +RUN cat /etc/os-release +# TODO https://github.com/moby/moby/issues/15717 alas; no curl or wget in image +ADD https://download.docker.com/linux/debian/dists/bullseye/pool/stable/amd64/docker-ce-cli_20.10.20~3-0~debian-bullseye_amd64.deb /tmp/docker.deb +RUN dpkg -i /tmp/docker.deb +RUN docker version || : +# Stay as root, so we can access docker.sock