Skip to content

Commit 1938b32

Browse files
authored
Add build args and Dockerfile path override to ImageFromDockerfile (testcontainers#1372)
1 parent 935b12f commit 1938b32

File tree

7 files changed

+122
-6
lines changed

7 files changed

+122
-6
lines changed

core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.io.PipedOutputStream;
2424
import java.util.HashMap;
2525
import java.util.Map;
26+
import java.util.Optional;
2627
import java.util.Set;
2728
import java.util.zip.GZIPOutputStream;
2829

@@ -60,6 +61,8 @@ public class ImageFromDockerfile extends LazyFuture<String> implements
6061
private boolean deleteOnExit = true;
6162

6263
private final Map<String, Transferable> transferables = new HashMap<>();
64+
private final Map<String, String> buildArgs = new HashMap<>();
65+
private Optional<String> dockerFilePath = Optional.empty();
6366

6467
public ImageFromDockerfile() {
6568
this("testcontainers/" + Base58.randomString(16).toLowerCase());
@@ -139,5 +142,22 @@ public void onNext(BuildResponseItem item) {
139142

140143
protected void configure(BuildImageCmd buildImageCmd) {
141144
buildImageCmd.withTag(this.getDockerImageName());
145+
this.dockerFilePath.ifPresent(buildImageCmd::withDockerfilePath);
146+
this.buildArgs.forEach(buildImageCmd::withBuildArg);
147+
}
148+
149+
public ImageFromDockerfile withBuildArg(final String key, final String value) {
150+
this.buildArgs.put(key, value);
151+
return this;
152+
}
153+
154+
public ImageFromDockerfile withBuildArgs(final Map<String, String> args) {
155+
this.buildArgs.putAll(args);
156+
return this;
157+
}
158+
159+
public ImageFromDockerfile withDockerfilePath(String relativePathFromBuildRoot) {
160+
this.dockerFilePath = Optional.of(relativePathFromBuildRoot);
161+
return this;
142162
}
143163
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.testcontainers.images.builder;
2+
3+
import org.junit.Test;
4+
import org.junit.runner.RunWith;
5+
import org.junit.runners.Parameterized;
6+
import org.testcontainers.containers.GenericContainer;
7+
import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
8+
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
12+
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
13+
14+
@RunWith(Parameterized.class)
15+
public class DockerfileBuildTest {
16+
17+
private static final Path RESOURCE_PATH = Paths.get("src/test/resources/dockerfile-build-test");
18+
19+
public String expectedFileContent;
20+
public ImageFromDockerfile image;
21+
22+
@Parameterized.Parameters
23+
public static Object[][] parameters() {
24+
return new Object[][]{
25+
// Dockerfile build without explicit per-file inclusion
26+
new Object[]{"test1234",
27+
// docsShowRecursiveFileInclusion {
28+
new ImageFromDockerfile()
29+
.withFileFromPath(".", RESOURCE_PATH)
30+
// }
31+
},
32+
33+
// Dockerfile build using a non-standard Dockerfile
34+
new Object[]{"test4567",
35+
new ImageFromDockerfile()
36+
.withFileFromPath(".", RESOURCE_PATH)
37+
.withDockerfilePath("./Dockerfile-alt")
38+
},
39+
40+
// Dockerfile build using build args
41+
new Object[]{"test7890",
42+
new ImageFromDockerfile()
43+
.withFileFromPath(".", RESOURCE_PATH)
44+
.withDockerfilePath("./Dockerfile-buildarg")
45+
.withBuildArg("CUSTOM_ARG", "test7890")
46+
},
47+
};
48+
}
49+
50+
public DockerfileBuildTest(String expectedFileContent, ImageFromDockerfile image) {
51+
this.expectedFileContent = expectedFileContent;
52+
this.image = image;
53+
}
54+
55+
@Test
56+
public void performTest() {
57+
try (final GenericContainer container = new GenericContainer(image)
58+
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())
59+
.withCommand("cat", "/test.txt")) {
60+
61+
container.start();
62+
63+
final String logs = container.getLogs();
64+
assertTrue("expected file content indicates that dockerfile build steps have been run", logs.contains(expectedFileContent));
65+
}
66+
}
67+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM alpine:3.2
2+
COPY localfile.txt /test.txt
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM alpine:3.2
2+
RUN echo "test4567" > /test.txt
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM alpine:3.2
2+
ARG CUSTOM_ARG
3+
RUN echo "${CUSTOM_ARG}" > /test.txt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test1234

docs/features/creating_images.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ image needs to be customized for specific test(s).
99
Simply pass a configured instance of `ImageFromDockerfile` as a constructor parameter to `GenericContainer`.
1010
Testcontainers will `docker build` a temporary container image, and will use it when creating the container.
1111

12-
13-
## Examples
14-
15-
### Dockerfile from String, file or classpath resource
12+
## Dockerfile from String, file or classpath resource
1613

1714
`ImageFromDockerfile` accepts arbitrary files, strings or classpath resources to be used as files in the build context.
1815
At least one of these needs to be a `Dockerfile`.
@@ -25,14 +22,27 @@ public GenericContainer dslContainer = new GenericContainer(
2522
.withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile"))
2623
```
2724

28-
The following methods may be used to provide the `Dockerfile` and any other required files:
25+
The following methods may be used to provide the `Dockerfile` and any other required build context files:
2926

3027
* `withFileFromString(buildContextPath, content)`
3128
* `withFileFromClasspath(buildContextPath, classpathPath)`
3229
* `withFileFromPath(buildContextPath, filesystemPath)`
3330
* `withFileFromFile(buildContextPath, filesystemFile)`
3431

35-
### Dockerfile DSL
32+
!!! info
33+
In older versions of Testcontainers (before 1.3.0) it was necessary to explicitly declare each file that needed to
34+
be present in the Docker build context.
35+
This can be replaced with the following syntax:
36+
<!--codeinclude-->
37+
[Passing an entire directory of files to the Dockerfile build context](../../core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java) inside_block:docsShowRecursiveFileInclusion
38+
<!--/codeinclude-->
39+
40+
Where `RESOURCE_PATH` is the path to a directory containing a `Dockerfile` and any files that it needs to refer to.
41+
Doing this is equivalent to `docker build RESOURCE_PATH` on the command line.
42+
43+
To mimic `docker build .`, `RESOURCE_PATH` would simply be set to `.` as well.
44+
45+
## Dockerfile DSL
3646

3747
If a static Dockerfile is not sufficient (e.g. your test needs to cover many variations that are best generated
3848
programmatically), there is a DSL available to allow Dockerfiles to be defined in code. e.g.:
@@ -64,3 +74,14 @@ By default the no-args constructor will use an image name of the form `testconta
6474
* `public ImageFromDockerfile()`
6575
* `public ImageFromDockerfile(String dockerImageName)`
6676
* `public ImageFromDockerfile(String dockerImageName, boolean deleteOnExit)`
77+
78+
## Alternative Dockerfiles
79+
80+
Normally Docker will automatically build an image from any `/Dockerfile` that it finds in the root of the build context.
81+
To override this behaviour, use `.withDockerfilePath("./Name-Of-Other-Dockerfile")`.
82+
83+
## Build Args
84+
85+
[Build Args](https://docs.docker.com/engine/reference/builder/#arg) may be used to allow lightweight parameterization.
86+
87+
To specify build args, use `.withBuildArg("varname", "value")` or provide a `Map` of args using `.withBuildArgs(map)`.

0 commit comments

Comments
 (0)