Skip to content

Commit 540f2ee

Browse files
glebstsrnorth
authored andcommitted
#444 'MountableFile path on Windows' - added separate filesystem path for file source (#445)
* #444 'MountableFile path on Windows' - added separate filesystem path for file source * #444 'MountableFile path on Windows' - made FS path only remove first slash on Windows; made recursiveTar work with file real FS paths on any platform; changed file attribute reading to work with FS path; minor renames and better exc message; * #444 'MountableFile path on Windows' - small changelog update
1 parent 76ef418 commit 540f2ee

File tree

4 files changed

+47
-24
lines changed

4 files changed

+47
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
44
## UNRELEASED
55
### Fixed
66
- Fixed local Docker Compose executable name resolution on Windows (#416)
7+
- Made tar composing work on Windows as well (#444)
78

89
### Changed
910
- Load `DockerClientProviderStrategy` via Service Loader (#434, #435)

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.getResolvedPath()));
16+
return ((SELF) this).withFileFromPath(path, Paths.get(mountableFile.getFilesystemPath()));
1717
}
1818
}

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

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public class MountableFile implements Transferable {
3838
@Getter(lazy = true)
3939
private final String resolvedPath = resolvePath();
4040

41+
@Getter(lazy = true)
42+
private final String filesystemPath = resolveFilesystemPath();
43+
44+
private String resourcePath;
45+
4146
/**
4247
* Obtains a {@link MountableFile} corresponding to a resource on the classpath (including resources in JAR files)
4348
*
@@ -115,12 +120,7 @@ private static String unencodeResourceURIToFilePath(@NotNull final String resour
115120
* @return a volume-mountable path.
116121
*/
117122
private String resolvePath() {
118-
String result;
119-
if (path.contains(".jar!")) {
120-
result = extractClassPathResourceToTempLocation(this.path);
121-
} else {
122-
result = unencodeResourceURIToFilePath(path);
123-
}
123+
String result = getResourcePath();
124124

125125
if (SystemUtils.IS_OS_WINDOWS) {
126126
result = PathUtils.createMinGWPath(result);
@@ -129,6 +129,32 @@ private String resolvePath() {
129129
return result;
130130
}
131131

132+
/**
133+
* Obtain a path in local filesystem that the Docker daemon should be able to use to volume mount a file/resource
134+
* into a container. If this is a classpath resource residing in a JAR, it will be extracted to
135+
* a temporary location so that the Docker daemon is able to access it.
136+
*
137+
* @return
138+
*/
139+
private String resolveFilesystemPath() {
140+
String result = getResourcePath();
141+
142+
if (SystemUtils.IS_OS_WINDOWS && result.startsWith("/")) {
143+
result = result.substring(1);
144+
}
145+
146+
return result;
147+
}
148+
149+
private String getResourcePath() {
150+
if (path.contains(".jar!")) {
151+
resourcePath = extractClassPathResourceToTempLocation(this.path);
152+
} else {
153+
resourcePath = unencodeResourceURIToFilePath(path);
154+
}
155+
return resourcePath;
156+
}
157+
132158
/**
133159
* Extract a file or directory tree from a JAR file to a temporary location.
134160
* This allows Docker to mount classpath resources as files.
@@ -205,22 +231,22 @@ private void deleteOnExit(final Path path) {
205231
*/
206232
@Override
207233
public void transferTo(final TarArchiveOutputStream outputStream, String destinationPathInTar) {
208-
recursiveTar(destinationPathInTar, this.getResolvedPath(), this.getResolvedPath(), outputStream);
234+
recursiveTar(destinationPathInTar, this.getFilesystemPath(), this.getFilesystemPath(), outputStream);
209235
}
210236

211237
/*
212238
* Recursively copies a file/directory into a TarArchiveOutputStream
213239
*/
214-
private void recursiveTar(String destination, String sourceRootDir, String sourceCurrentItem, TarArchiveOutputStream tarArchive) {
240+
private void recursiveTar(String entryFilename, String rootPath, String itemPath, TarArchiveOutputStream tarArchive) {
215241
try {
216-
final File sourceFile = new File(sourceCurrentItem).getCanonicalFile(); // e.g. /foo/bar/baz
217-
final File sourceRootFile = new File(sourceRootDir).getCanonicalFile(); // e.g. /foo
242+
final File sourceFile = new File(itemPath).getCanonicalFile(); // e.g. /foo/bar/baz
243+
final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo
218244
final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz
219245

220-
final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, destination + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz
246+
final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, entryFilename + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz
221247

222248
// TarArchiveEntry automatically sets the mode for file/directory, but we can update to ensure that the mode is set exactly (inc executable bits)
223-
tarEntry.setMode(getUnixFileMode(sourceCurrentItem));
249+
tarEntry.setMode(getUnixFileMode(itemPath));
224250
tarArchive.putArchiveEntry(tarEntry);
225251

226252
if (sourceFile.isFile()) {
@@ -233,19 +259,19 @@ private void recursiveTar(String destination, String sourceRootDir, String sourc
233259
if (children != null) {
234260
// recurse into child files/directories
235261
for (final File child : children) {
236-
recursiveTar(destination, sourceRootDir + File.separator, child.getCanonicalPath(), tarArchive);
262+
recursiveTar(entryFilename, sourceRootFile.getCanonicalPath(), child.getCanonicalPath(), tarArchive);
237263
}
238264
}
239265
} catch (IOException e) {
240-
log.error("Error when copying TAR file entry: {}", sourceCurrentItem, e);
266+
log.error("Error when copying TAR file entry: {}", itemPath, e);
241267
throw new UncheckedIOException(e); // fail fast
242268
}
243269
}
244270

245271
@Override
246272
public long getSize() {
247273

248-
final File file = new File(this.getResolvedPath());
274+
final File file = new File(this.getFilesystemPath());
249275
if (file.isFile()) {
250276
return file.length();
251277
} else {
@@ -260,7 +286,7 @@ public String getDescription() {
260286

261287
@Override
262288
public int getFileMode() {
263-
return getUnixFileMode(this.getResolvedPath());
289+
return getUnixFileMode(this.getFilesystemPath());
264290
}
265291

266292
private int getUnixFileMode(final String pathAsString) {

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@ public void forHostPathWithSpaces() throws Exception {
6060
assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ "));
6161
}
6262

63-
/*
64-
*
65-
*/
66-
6763
@SuppressWarnings("ResultOfMethodCallIgnored")
6864
@NotNull
6965
private Path createTempFile(final String name) throws IOException {
@@ -77,9 +73,9 @@ private Path createTempFile(final String name) throws IOException {
7773
}
7874

7975
private void performChecks(final MountableFile mountableFile) {
80-
final String mountablePath = mountableFile.getResolvedPath();
81-
assertTrue("The resolved path can be found", new File(mountablePath).exists());
82-
assertFalse("The resolved path does not contain any URL escaping", mountablePath.contains("%20"));
76+
final String mountablePath = mountableFile.getFilesystemPath();
77+
assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists());
78+
assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20"));
8379
}
8480

8581
}

0 commit comments

Comments
 (0)