Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ public ContainerisedDockerCompose(
super(dockerImageName);
addEnv(ENV_PROJECT_NAME, identifier);

// Map the docker compose file into the container
final File dockerComposeBaseFile = composeFiles.get(0);
final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath();
// Docker Compose (running inside the container) needs one shared root directory to resolve
// every -f file against, so map the files in relative to the closest common ancestor of all
// of them rather than assuming they all live alongside the first file.
final File composeFileRoot = PathUtils.findCommonParent(composeFiles);
final String pwd = composeFileRoot.getAbsolutePath();
final String containerPwd = convertToUnixFilesystemPath(pwd);

final List<String> absoluteDockerComposeFiles = composeFiles
Expand All @@ -52,12 +54,14 @@ public ContainerisedDockerCompose(
logger().info("Copying all files in {} into the container", pwd);
withCopyFileToContainer(MountableFile.forHostPath(pwd), containerPwd);
} else {
// Always copy the compose file itself
logger().info("Copying docker compose file: {}", dockerComposeBaseFile.getAbsolutePath());
withCopyFileToContainer(
MountableFile.forHostPath(dockerComposeBaseFile.getAbsolutePath()),
convertToUnixFilesystemPath(dockerComposeBaseFile.getAbsolutePath())
);
// Always copy the compose files themselves, wherever they live under the common root
for (File composeFile : composeFiles) {
logger().info("Copying docker compose file: {}", composeFile.getAbsolutePath());
withCopyFileToContainer(
MountableFile.forHostPath(composeFile.getAbsolutePath()),
convertToUnixFilesystemPath(composeFile.getAbsolutePath())
);
}
for (String pathToCopy : fileCopyInclusions) {
String hostPath = pwd + "/" + pathToCopy;
logger().info("Copying inclusion file: {}", hostPath);
Expand Down
42 changes: 42 additions & 0 deletions core/src/main/java/org/testcontainers/utility/PathUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import lombok.NonNull;
import lombok.experimental.UtilityClass;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.stream.Collectors;

/**
* Filesystem operation utility methods.
Expand Down Expand Up @@ -75,4 +78,43 @@ public static String createMinGWPath(String path) {
mingwPath = mingwPath.replace(":", "");
return mingwPath;
}

/**
* Find the closest common ancestor directory of a set of files, e.g. so that a single directory tree can be
* used to resolve all of the files, even when they were supplied from different directories.
*
* @param files the files to find a common ancestor directory for
* @return the closest common ancestor directory of all the given files
*/
public static File findCommonParent(@NonNull List<File> files) {
List<Path> parentDirs = files
.stream()
.map(file -> file.getAbsoluteFile().toPath().normalize().getParent())
.collect(Collectors.toList());

Path commonParent = parentDirs.get(0);
for (Path parentDir : parentDirs) {
commonParent = commonAncestor(commonParent, parentDir);
}
return commonParent.toFile();
}

private static Path commonAncestor(Path a, Path b) {
if (!a.getRoot().equals(b.getRoot())) {
throw new IllegalArgumentException(
"Cannot determine a common parent directory for paths on different filesystem roots: " + a + " and " + b
);
}

Path commonAncestor = a.getRoot();
int sharedNameCount = Math.min(a.getNameCount(), b.getNameCount());
for (int i = 0; i < sharedNameCount; i++) {
Path aElement = a.getName(i);
if (!aElement.equals(b.getName(i))) {
break;
}
commonAncestor = commonAncestor.resolve(aElement);
}
return commonAncestor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.testcontainers.utility;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;

class PathUtilsTest {

@TempDir
Path tempDir;

@Test
void findCommonParentOfSingleFileIsItsOwnDirectory() throws IOException {
File composeFile = createFile(tempDir, "docker-compose.yml");

assertThat(PathUtils.findCommonParent(Collections.singletonList(composeFile))).isEqualTo(tempDir.toFile());
}

@Test
void findCommonParentOfFilesInTheSameDirectory() throws IOException {
File baseFile = createFile(tempDir, "docker-compose.yml");
File overrideFile = createFile(tempDir, "docker-compose.override.yml");

assertThat(PathUtils.findCommonParent(Arrays.asList(baseFile, overrideFile))).isEqualTo(tempDir.toFile());
}

@Test
void findCommonParentOfFilesInDifferentDirectories() throws IOException {
// Mirrors the scenario from testcontainers/testcontainers-java#1863: a base compose file
// nested in a subdirectory, and an override file one level up.
Path firstDir = tempDir.resolve("first");
Path nestedDir = firstDir.resolve("directory");
Files.createDirectories(nestedDir);

File baseFile = createFile(nestedDir, "docker-compose.yml");
File overrideFile = createFile(firstDir, "docker-compose.override.yml");

assertThat(PathUtils.findCommonParent(Arrays.asList(baseFile, overrideFile))).isEqualTo(firstDir.toFile());
}

@Test
void findCommonParentIsOrderIndependent() throws IOException {
Path firstDir = tempDir.resolve("first");
Path nestedDir = firstDir.resolve("directory");
Files.createDirectories(nestedDir);

File baseFile = createFile(nestedDir, "docker-compose.yml");
File overrideFile = createFile(firstDir, "docker-compose.override.yml");

assertThat(PathUtils.findCommonParent(Arrays.asList(overrideFile, baseFile))).isEqualTo(firstDir.toFile());
}

@Test
void findCommonParentOfFilesInSiblingDirectories() throws IOException {
Path branchA = tempDir.resolve("a");
Path branchB = tempDir.resolve("b");
Files.createDirectories(branchA);
Files.createDirectories(branchB);

File fileA = createFile(branchA, "docker-compose.yml");
File fileB = createFile(branchB, "docker-compose.override.yml");

assertThat(PathUtils.findCommonParent(Arrays.asList(fileA, fileB))).isEqualTo(tempDir.toFile());
}

private static File createFile(Path directory, String name) throws IOException {
Path file = directory.resolve(name);
Files.write(file, new byte[0]);
return file.toFile();
}
}