Skip to content

Commit

Permalink
Merge pull request #280 from jglick/runningInsideContainer-JENKINS-64608
Browse files Browse the repository at this point in the history
[JENKINS-64608] Fix cgroup v2 detection of Docker CLI running inside a container
  • Loading branch information
rsandell authored Oct 24, 2022
2 parents 1aadd20 + 69dde87 commit c508932
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -349,10 +354,26 @@ public Optional<String> 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<String> 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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);
});
}

}
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit c508932

Please sign in to comment.