From 9bebb25cd89e93c0fa2bec0c5bfb08e982b4f828 Mon Sep 17 00:00:00 2001 From: Josh Cain Date: Tue, 2 May 2017 13:05:30 -0500 Subject: [PATCH 1/3] add container arguments to specify SELinux contexts for mounts --- .../testcontainers/containers/Container.java | 36 ++++++++++++++++--- .../containers/GenericContainer.java | 16 ++++++--- .../containers/SelinuxContext.java | 16 +++++++++ .../junit/GenericContainerRuleTest.java | 21 +++++++++++ 4 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/SelinuxContext.java diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index b3c0a5298da..b4a8b7e2449 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -80,11 +80,24 @@ public String getStderr() { * Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)} * for building a container in a fluent style. * - * @param hostPath the file system path on the host + * @param hostPath the file system path on the host * @param containerPath the file system path inside the container - * @param mode the bind mode + * @param mode the bind mode + */ + default void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode) { + addFileSystemBind(hostPath, containerPath, mode, SelinuxContext.NONE); + } + + /** + * Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)} + * for building a container in a fluent style. + * + * @param hostPath the file system path on the host + * @param containerPath the file system path inside the container + * @param mode the bind mode + * @param selinuxContext selinux context argument to use for this file */ - void addFileSystemBind(String hostPath, String containerPath, BindMode mode); + void addFileSystemBind(String hostPath, String containerPath, BindMode mode, SelinuxContext selinuxContext); /** * Add a link to another container. @@ -205,7 +218,22 @@ public String getStderr() { * @param mode access mode for the file * @return this */ - SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode); + default SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); + return self(); + } + + /** + * Map a resource (file or directory) on the classpath to a path inside the container. + * This will only work if you are running your tests outside a Docker container. + * + * @param resourcePath path to the resource on the classpath (relative to the classpath root; should not start with a leading slash) + * @param containerPath path this should be mapped to inside the container + * @param mode access mode for the file + * @param selinuxContext selinux context argument to use for this file + * @return this + */ + SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext); /** * Set the duration of waiting time until container treated as started. diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 167ea5864b9..90c5f8b8272 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -481,10 +481,10 @@ public void addEnv(String key, String value) { * {@inheritDoc} */ @Override - public void addFileSystemBind(String hostPath, String containerPath, BindMode mode) { + public void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { final MountableFile mountableFile = MountableFile.forHostPath(hostPath); - binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode)); + binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); } /** @@ -615,10 +615,18 @@ public SELF withNetworkMode(String networkMode) { * {@inheritDoc} */ @Override - public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode) { + public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + return withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); + } + + /** + * {@inheritDoc} + */ + @Override + public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); - this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode); + this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode, selinuxContext); return self(); } diff --git a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java new file mode 100644 index 00000000000..0feab512806 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java @@ -0,0 +1,16 @@ +package org.testcontainers.containers; + +import com.github.dockerjava.api.model.SELContext; + +/** + * Possible contexts for use with SELinux + */ +public enum SelinuxContext { + SHARED(SELContext.shared), SINGLE(SELContext.single), NONE(SELContext.none); + + public final SELContext selContext; + + SelinuxContext(final SELContext selContext) { + this.selContext = selContext; + } +} diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 59d9cb2b34f..5384d2aeb6b 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -11,6 +11,7 @@ import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.SelinuxContext; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; @@ -25,6 +26,8 @@ import static org.rnorth.visibleassertions.VisibleAssertions.*; import static org.testcontainers.containers.BindMode.READ_ONLY; +import static org.testcontainers.containers.BindMode.READ_WRITE; +import static org.testcontainers.containers.SelinuxContext.SHARED; /** * Tests for GenericContainerRules @@ -101,6 +104,15 @@ public static void setupContent() throws FileNotFoundException { .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_ONLY) .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + /** + * Map a file on the classpath to a file in the container, and then expose the content for testing. + */ + @ClassRule + public static GenericContainer alpineClasspathResourceSelinx = new GenericContainer("alpine:3.2") + .withExposedPorts(80) + .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED) + .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + /** * Create a container with an extra host entry and expose the content of /etc/hosts for testing. */ @@ -203,6 +215,15 @@ public void customClasspathResourceMappingTest() throws IOException { assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMapping", "FOOBAR", line); } + @Test + public void customClasspathResourceMappingWithSelinuxTest() throws IOException { + // Note: This functionality doesn't work if you are running your build inside a Docker container; + // in that case this test will fail. + String line = getReaderForContainerPort80(alpineClasspathResourceSelinx).readLine(); + + assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", "FOOBAR", line); + } + @Test public void exceptionThrownWhenMappedPortNotFound() throws IOException { assertThrows("When the requested port is not mapped, getMappedPort() throws an exception", From 38fad7b3e494282f7fcfb980e09fe8edc2d15cb9 Mon Sep 17 00:00:00 2001 From: Josh Cain Date: Thu, 4 May 2017 09:38:16 -0500 Subject: [PATCH 2/3] remove unused import in test --- .../java/org/testcontainers/junit/GenericContainerRuleTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 5384d2aeb6b..29826d5e0f3 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -11,7 +11,6 @@ import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.SelinuxContext; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; From 77debda907da80f45323174b27cfcee28e667050 Mon Sep 17 00:00:00 2001 From: Josh Cain Date: Wed, 31 May 2017 17:20:09 -0500 Subject: [PATCH 3/3] address review requests comments for formatting, lombok annotations, and clarity around SELinux --- .../org/testcontainers/containers/SelinuxContext.java | 9 +++++---- .../testcontainers/junit/GenericContainerRuleTest.java | 7 ++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java index 0feab512806..735d3e3a277 100644 --- a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java +++ b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java @@ -1,16 +1,17 @@ package org.testcontainers.containers; import com.github.dockerjava.api.model.SELContext; +import lombok.AllArgsConstructor; /** * Possible contexts for use with SELinux */ +@AllArgsConstructor public enum SelinuxContext { - SHARED(SELContext.shared), SINGLE(SELContext.single), NONE(SELContext.none); + SHARED(SELContext.shared), + SINGLE(SELContext.single), + NONE(SELContext.none); public final SELContext selContext; - SelinuxContext(final SELContext selContext) { - this.selContext = selContext; - } } diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 29826d5e0f3..45c1a78d10f 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -107,7 +107,7 @@ public static void setupContent() throws FileNotFoundException { * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResourceSelinx = new GenericContainer("alpine:3.2") + public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer("alpine:3.2") .withExposedPorts(80) .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED) .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); @@ -216,10 +216,7 @@ public void customClasspathResourceMappingTest() throws IOException { @Test public void customClasspathResourceMappingWithSelinuxTest() throws IOException { - // Note: This functionality doesn't work if you are running your build inside a Docker container; - // in that case this test will fail. - String line = getReaderForContainerPort80(alpineClasspathResourceSelinx).readLine(); - + String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine(); assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", "FOOBAR", line); }