Open ml-james opened 1 month ago
The problem arises because for some reason in MountableFile
the code strips the internal path from the resource within the jar. This leads to the file being written directly at /tmp/.testcontainers-tmp-8883751548680426941
. When that file path gets sent to docker-java
it fails, because docker-java
will recursively search for files within the parent directory, in this case /tmp
. Absolutely anything could be in /tmp
, for me I have things owned by root
which throws the exception shown.
The code that does that is here, where String destinationName = entry.getName().replaceFirst(fromRoot, "");
is the line of interest, where the fromRoot
is the internal path in question, i.e. the path of the resource within the provided jar. Stripping this will leave destinationName
as "" and therefore the contents of the resource is just written directly to the toRoot
path, which is the /tmp/.testcontainers-tmp-8883751548680426941
file, where of course .testcontainers-tmp-8883751548680426941
is generated each time.
@SuppressWarnings("ResultOfMethodCallIgnored")
private void copyFromJarToLocation(
final JarFile jarFile,
final JarEntry entry,
final String fromRoot,
final File toRoot
) throws IOException {
String destinationName = entry.getName().replaceFirst(fromRoot, "");
File newFile = new File(toRoot, destinationName);
log.debug("Copying resource {} from JAR file {}", fromRoot, jarFile.getName());
if (!entry.isDirectory()) {
// Create parent directories
Path parent = newFile.getAbsoluteFile().toPath().getParent();
parent.toFile().mkdirs();
newFile.deleteOnExit();
try (InputStream is = jarFile.getInputStream(entry)) {
Files.copy(is, newFile.toPath());
} catch (IOException e) {
log.error(
"Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(),
e
);
throw e;
}
}
}
I'm not sure why we're stripping the internal path from the file path when we copy to the temp directory. My proposed solution is as follows, which allows my test to pass.
Index: core/src/main/java/org/testcontainers/utility/MountableFile.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java
--- a/core/src/main/java/org/testcontainers/utility/MountableFile.java (revision cd29df97aa06e744ed4d8c17ca33b7983ef7f338)
+++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java (date 1729554209095)
@@ -264,7 +264,7 @@
deleteOnExit(tmpLocation.toPath());
try {
- return tmpLocation.getCanonicalPath();
+ return tmpLocation.getCanonicalPath() + "/" + internalPath;
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -288,7 +288,7 @@
final String fromRoot,
final File toRoot
) throws IOException {
- String destinationName = entry.getName().replaceFirst(fromRoot, "");
+ String destinationName = entry.getName();
File newFile = new File(toRoot, destinationName);
log.debug("Copying resource {} from JAR file {}", fromRoot, jarFile.getName());
Let me know if this looks sensible and I can raise a PR.
Module
Core
Testcontainers version
1.20.2
Using the latest Testcontainers version?
Yes
Host OS
Linux
Host Arch
x86
Docker version
[jamesm@wt1devps05 solana4j]$ docker --version Docker version 26.1.4, build 5650f9b
What happened?
Unable to build from a Dockerfile read from a Jar.
In fact, loading files from the classpath from jars in general looks a bit broken to me. I raised an issue a few days ago that became unwieldy, so I tried to recreate the issue in as simple a way as possible. That previous issue I closed.
I have created a unit test that proves the problem. Just apply the patch in the "Additional Information" section.
Question I have simply is am I doing something wrong, or is this indeed a bug, or is this simply not supported? It certainly looks like the code is trying quite hard to support what I'm trying to do.
I'd be happy to devote some time to raise a PR if you agree that this is not expected behaviour.
Thanks.
Relevant log output
Caused by: com.github.dockerjava.api.exception.DockerClientException: Failed to read build context directory: /tmp at org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile$ScannedResult.addFilesInDirectory(Dockerfile.java:201) at org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile$ScannedResult.addFilesInDirectory(Dockerfile.java:207) at org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile$ScannedResult.(Dockerfile.java:186)
at org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile.parse(Dockerfile.java:111)
at org.testcontainers.shaded.com.github.dockerjava.core.command.BuildImageCmdImpl.withDockerfile(BuildImageCmdImpl.java:344)
at org.testcontainers.shaded.com.github.dockerjava.core.command.BuildImageCmdImpl.withDockerfile(BuildImageCmdImpl.java:22)
at org.testcontainers.images.builder.ImageFromDockerfile.lambda$configure$0(ImageFromDockerfile.java:177)
at java.base/java.util.Optional.ifPresent(Optional.java:178)
at org.testcontainers.images.builder.ImageFromDockerfile.configure(ImageFromDockerfile.java:176)
at org.testcontainers.images.builder.ImageFromDockerfile.resolve(ImageFromDockerfile.java:124)
at org.testcontainers.images.builder.ImageFromDockerfile.resolve(ImageFromDockerfile.java:43)
at org.testcontainers.utility.LazyFuture.getResolvedValue(LazyFuture.java:20)
at org.testcontainers.utility.LazyFuture.get(LazyFuture.java:41)
at org.testcontainers.shaded.com.google.common.util.concurrent.Futures$1.get(Futures.java:538)
at org.testcontainers.images.RemoteDockerImage.getImageName(RemoteDockerImage.java:172)
at org.testcontainers.images.RemoteDockerImage.resolve(RemoteDockerImage.java:76)
at org.testcontainers.images.RemoteDockerImage.resolve(RemoteDockerImage.java:35)
at org.testcontainers.utility.LazyFuture.getResolvedValue(LazyFuture.java:20)
at org.testcontainers.utility.LazyFuture.get(LazyFuture.java:41)
at org.testcontainers.containers.GenericContainer.getDockerImageName(GenericContainer.java:1354)
... 54 more
Failed to read build context directory: /tmp com.github.dockerjava.api.exception.DockerClientException: Failed to read build context directory: /tmp at app//org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile$ScannedResult.addFilesInDirectory(Dockerfile.java:201) at app//org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile$ScannedResult.addFilesInDirectory(Dockerfile.java:207) at app//org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile$ScannedResult.(Dockerfile.java:186)
at app//org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.Dockerfile.parse(Dockerfile.java:111)
at app//org.testcontainers.shaded.com.github.dockerjava.core.command.BuildImageCmdImpl.withDockerfile(BuildImageCmdImpl.java:344)
at app//org.testcontainers.shaded.com.github.dockerjava.core.command.BuildImageCmdImpl.withDockerfile(BuildImageCmdImpl.java:22)
at app//org.testcontainers.images.builder.ImageFromDockerfile.lambda$configure$0(ImageFromDockerfile.java:177)
at java.base@17.0.11/java.util.Optional.ifPresent(Optional.java:178)
at app//org.testcontainers.images.builder.ImageFromDockerfile.configure(ImageFromDockerfile.java:176)
at app//org.testcontainers.images.builder.ImageFromDockerfile.resolve(ImageFromDockerfile.java:124)
at app//org.testcontainers.images.builder.ImageFromDockerfile.resolve(ImageFromDockerfile.java:43)
at app//org.testcontainers.utility.LazyFuture.getResolvedValue(LazyFuture.java:20)
at app//org.testcontainers.utility.LazyFuture.get(LazyFuture.java:41)
at app//org.testcontainers.shaded.com.google.common.util.concurrent.Futures$1.get(Futures.java:538)
at app//org.testcontainers.images.RemoteDockerImage.getImageName(RemoteDockerImage.java:172)
at app//org.testcontainers.images.RemoteDockerImage.resolve(RemoteDockerImage.java:76)
at app//org.testcontainers.images.RemoteDockerImage.resolve(RemoteDockerImage.java:35)
at app//org.testcontainers.utility.LazyFuture.getResolvedValue(LazyFuture.java:20)
at app//org.testcontainers.utility.LazyFuture.get(LazyFuture.java:41)
at app//org.testcontainers.containers.GenericContainer.getDockerImageName(GenericContainer.java:1354)
at app//org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:351)
at app//org.testcontainers.containers.GenericContainer.start(GenericContainer.java:322)
at app//org.testcontainers.images.builder.DockerfileBuildTest.performTest(DockerfileBuildTest.java:96)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at app//org.junit.runners.Suite.runChild(Suite.java:128)
at app//org.junit.runners.Suite.runChild(Suite.java:27)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Additional Information
Run
core/testlib/create_fakejar.sh
and then run the test in the above patch, should replicate the issue.