Skip to content

Commit 87b8501

Browse files
authored
Fix problems with copying files and Docker-Compose on Windows (#514)
For docker socket mounting on Windows, the socket path must be prefixed with an additional "/". Also used Windows style paths when interacting with files for Docker for Windows compatibility.
1 parent f1ce2b1 commit 87b8501

File tree

6 files changed

+39
-23
lines changed

6 files changed

+39
-23
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.sh text eol=lf

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
33

44
## UNRELEASED
55
### Fixed
6+
- Problems with using container based docker-compose on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514))
7+
- Problems with copying files on Windows ([\#514](https://github.com/testcontainers/testcontainers-java/pull/514))
68
- Fixed regression in 1.4.3 when using Docker Compose on Windows ([\#439](https://github.com/testcontainers/testcontainers-java/issues/439))
79
- Fixed local Docker Compose executable name resolution on Windows (#416)
810
- Fixed TAR composition on Windows (#444)

core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,10 @@ default void validateFileList(List<File> composeFiles) {
395395
* Use Docker Compose container.
396396
*/
397397
class ContainerisedDockerCompose extends GenericContainer<ContainerisedDockerCompose> implements DockerCompose {
398+
399+
private static final String DOCKER_SOCKET_PATH = "/var/run/docker.sock";
400+
public static final char UNIX_PATH_SEPERATOR = ':';
401+
398402
public ContainerisedDockerCompose(List<File> composeFiles, String identifier) {
399403

400404
super(TestcontainersConfiguration.getInstance().getDockerComposeContainerImage());
@@ -405,14 +409,14 @@ public ContainerisedDockerCompose(List<File> composeFiles, String identifier) {
405409
// Map the docker compose file into the container
406410
final File dockerComposeBaseFile = composeFiles.get(0);
407411
final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath();
408-
final String containerPwd = MountableFile.forHostPath(pwd).getResolvedPath();
412+
final String containerPwd = MountableFile.forHostPath(pwd).getFilesystemPath();
409413

410414
final List<String> absoluteDockerComposeFiles = composeFiles.stream()
411415
.map(File::getAbsolutePath)
412416
.map(MountableFile::forHostPath)
413-
.map(MountableFile::getResolvedPath)
417+
.map(MountableFile::getFilesystemPath)
414418
.collect(toList());
415-
final String composeFileEnvVariableValue = Joiner.on(File.pathSeparator).join(absoluteDockerComposeFiles);
419+
final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator
416420
logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue);
417421
addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue);
418422
addFileSystemBind(pwd, containerPwd, READ_ONLY);
@@ -421,12 +425,18 @@ public ContainerisedDockerCompose(List<File> composeFiles, String identifier) {
421425
// as the docker daemon, just mapping the docker control socket is OK.
422426
// As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI)
423427
// we map the socket file outside of /var/run, as just /docker.sock
424-
addFileSystemBind("/var/run/docker.sock", "/docker.sock", READ_WRITE);
428+
addFileSystemBind(getDockerSocketHostPath(), "/docker.sock", READ_WRITE);
425429
addEnv("DOCKER_HOST", "unix:///docker.sock");
426430
setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy());
427431
setWorkingDirectory(containerPwd);
428432
}
429433

434+
private String getDockerSocketHostPath() {
435+
return SystemUtils.IS_OS_WINDOWS
436+
? "/" + DOCKER_SOCKET_PATH
437+
: DOCKER_SOCKET_PATH;
438+
}
439+
430440
@Override
431441
public void invoke() {
432442
super.start();

core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ public interface ClasspathTrait<SELF extends ClasspathTrait<SELF> & BuildContext
1313
default SELF withFileFromClasspath(String path, String resourcePath) {
1414
final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath);
1515

16-
return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getFilesystemPath()));
16+
return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getResolvedPath()));
1717
}
1818
}

core/src/main/java/org/testcontainers/utility/MountableFile.java

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package org.testcontainers.utility;
22

3-
import static lombok.AccessLevel.PACKAGE;
4-
import static org.testcontainers.utility.PathUtils.recursiveDeleteDir;
5-
63
import com.google.common.base.Charsets;
4+
import lombok.Getter;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
8+
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
9+
import org.apache.commons.lang.SystemUtils;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.testcontainers.images.builder.Transferable;
12+
713
import java.io.File;
814
import java.io.IOException;
915
import java.io.InputStream;
@@ -19,14 +25,9 @@
1925
import java.util.Set;
2026
import java.util.jar.JarEntry;
2127
import java.util.jar.JarFile;
22-
import lombok.Getter;
23-
import lombok.RequiredArgsConstructor;
24-
import lombok.extern.slf4j.Slf4j;
25-
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
26-
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
27-
import org.apache.commons.lang.SystemUtils;
28-
import org.jetbrains.annotations.NotNull;
29-
import org.testcontainers.images.builder.Transferable;
28+
29+
import static lombok.AccessLevel.PACKAGE;
30+
import static org.testcontainers.utility.PathUtils.recursiveDeleteDir;
3031

3132
/**
3233
* An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating
@@ -165,8 +166,8 @@ private static String unencodeResourceURIToFilePath(@NotNull final String resour
165166
private String resolvePath() {
166167
String result = getResourcePath();
167168

168-
if (SystemUtils.IS_OS_WINDOWS) {
169-
result = PathUtils.createMinGWPath(result);
169+
if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) {
170+
result = result.substring(1);
170171
}
171172

172173
return result;
@@ -177,13 +178,15 @@ private String resolvePath() {
177178
* into a container. If this is a classpath resource residing in a JAR, it will be extracted to
178179
* a temporary location so that the Docker daemon is able to access it.
179180
*
181+
* TODO: rename method accordingly and check if really needed like this
182+
*
180183
* @return
181184
*/
182185
private String resolveFilesystemPath() {
183186
String result = getResourcePath();
184187

185188
if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) {
186-
result = result.substring(1);
189+
result = PathUtils.createMinGWPath(result).substring(1);
187190
}
188191

189192
return result;
@@ -285,7 +288,7 @@ private void deleteOnExit(final Path path) {
285288
*/
286289
@Override
287290
public void transferTo(final TarArchiveOutputStream outputStream, String destinationPathInTar) {
288-
recursiveTar(destinationPathInTar, this.getFilesystemPath(), this.getFilesystemPath(), outputStream);
291+
recursiveTar(destinationPathInTar, this.getResolvedPath(), this.getResolvedPath(), outputStream);
289292
}
290293

291294
/*
@@ -325,7 +328,7 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath
325328
@Override
326329
public long getSize() {
327330

328-
final File file = new File(this.getFilesystemPath());
331+
final File file = new File(this.getResolvedPath());
329332
if (file.isFile()) {
330333
return file.length();
331334
} else {
@@ -340,7 +343,7 @@ public String getDescription() {
340343

341344
@Override
342345
public int getFileMode() {
343-
return getUnixFileMode(this.getFilesystemPath());
346+
return getUnixFileMode(this.getResolvedPath());
344347
}
345348

346349
private int getUnixFileMode(final String pathAsString) {

core/src/test/java/org/testcontainers/utility/MountableFileTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private Path createTempDir() throws IOException {
108108
}
109109

110110
private void performChecks(final MountableFile mountableFile) {
111-
final String mountablePath = mountableFile.getFilesystemPath();
111+
final String mountablePath = mountableFile.getResolvedPath();
112112
assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists());
113113
assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20"));
114114
}

0 commit comments

Comments
 (0)