-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#446 'set permission when copying' #467
Changes from all commits
cd5de8b
7a453c3
7e08215
20f7d17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,11 @@ | |
import org.jetbrains.annotations.NotNull; | ||
import org.testcontainers.images.builder.Transferable; | ||
|
||
import java.io.*; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.UncheckedIOException; | ||
import java.io.UnsupportedEncodingException; | ||
import java.net.URL; | ||
import java.net.URLDecoder; | ||
import java.nio.file.Files; | ||
|
@@ -33,7 +37,11 @@ | |
@Slf4j | ||
public class MountableFile implements Transferable { | ||
|
||
private static final int BASE_FILE_MODE = 0100000; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Base taken from Transferable, probably could be some bit math on Transferable.DEFAULT_FILE_MODE with making last three digits to zero. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably unlikely to change, so 👍 |
||
private static final int BASE_DIR_MODE = 0040000; | ||
|
||
private final String path; | ||
private final int forcedFileMode; | ||
|
||
@Getter(lazy = true) | ||
private final String resolvedPath = resolvePath(); | ||
|
@@ -50,7 +58,7 @@ public class MountableFile implements Transferable { | |
* @return a {@link MountableFile} that may be used to obtain a mountable path | ||
*/ | ||
public static MountableFile forClasspathResource(@NotNull final String resourceName) { | ||
return new MountableFile(getClasspathResource(resourceName, new HashSet<>()).toString()); | ||
return forClasspathResource(resourceName, -1); | ||
} | ||
|
||
/** | ||
|
@@ -60,7 +68,7 @@ public static MountableFile forClasspathResource(@NotNull final String resourceN | |
* @return a {@link MountableFile} that may be used to obtain a mountable path | ||
*/ | ||
public static MountableFile forHostPath(@NotNull final String path) { | ||
return new MountableFile(new File(path).toURI().toString()); | ||
return forHostPath(path, -1); | ||
} | ||
|
||
/** | ||
|
@@ -70,9 +78,43 @@ public static MountableFile forHostPath(@NotNull final String path) { | |
* @return a {@link MountableFile} that may be used to obtain a mountable path | ||
*/ | ||
public static MountableFile forHostPath(final Path path) { | ||
return new MountableFile(path.toAbsolutePath().toString()); | ||
return forHostPath(path, -1); | ||
} | ||
|
||
/** | ||
* Obtains a {@link MountableFile} corresponding to a resource on the classpath (including resources in JAR files) | ||
* | ||
* @param resourceName the classpath path to the resource | ||
* @param mode octal value of posix file mode (000..777) | ||
* @return a {@link MountableFile} that may be used to obtain a mountable path | ||
*/ | ||
public static MountableFile forClasspathResource(@NotNull final String resourceName, int mode) { | ||
return new MountableFile(getClasspathResource(resourceName, new HashSet<>()).toString(), mode); | ||
} | ||
|
||
/** | ||
* Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem. | ||
* | ||
* @param path the path to the resource | ||
* @param mode octal value of posix file mode (000..777) | ||
* @return a {@link MountableFile} that may be used to obtain a mountable path | ||
*/ | ||
public static MountableFile forHostPath(@NotNull final String path, int mode) { | ||
return new MountableFile(new File(path).toURI().toString(), mode); | ||
} | ||
|
||
/** | ||
* Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem. | ||
* | ||
* @param path the path to the resource | ||
* @param mode octal value of posix file mode (000..777) | ||
* @return a {@link MountableFile} that may be used to obtain a mountable path | ||
*/ | ||
public static MountableFile forHostPath(final Path path, int mode) { | ||
return new MountableFile(path.toAbsolutePath().toString(), mode); | ||
} | ||
|
||
|
||
@NotNull | ||
private static URL getClasspathResource(@NotNull final String resourcePath, @NotNull final Set<ClassLoader> classLoaders) { | ||
|
||
|
@@ -291,6 +333,10 @@ public int getFileMode() { | |
|
||
private int getUnixFileMode(final String pathAsString) { | ||
final Path path = Paths.get(pathAsString); | ||
if (this.forcedFileMode > -1) { | ||
return this.getModeValue(path); | ||
} | ||
|
||
try { | ||
return (int) Files.getAttribute(path, "unix:mode"); | ||
} catch (IOException | UnsupportedOperationException e) { | ||
|
@@ -305,4 +351,9 @@ private int getUnixFileMode(final String pathAsString) { | |
return mode; | ||
} | ||
} | ||
|
||
private int getModeValue(final Path path) { | ||
int result = Files.isDirectory(path) ? BASE_DIR_MODE : BASE_FILE_MODE; | ||
return result | this.forcedFileMode; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package org.testcontainers.utility; | ||
|
||
import org.hamcrest.Description; | ||
import org.hamcrest.Matcher; | ||
import org.hamcrest.core.IsCollectionContaining; | ||
import org.junit.Test; | ||
import org.testcontainers.containers.GenericContainer; | ||
import org.testcontainers.containers.output.ToStringConsumer; | ||
|
@@ -8,9 +11,13 @@ | |
import org.testcontainers.images.builder.ImageFromDockerfile; | ||
|
||
import java.io.File; | ||
import java.util.Arrays; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
|
||
import static org.hamcrest.CoreMatchers.allOf; | ||
import static org.hamcrest.CoreMatchers.containsString; | ||
import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; | ||
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; | ||
|
||
public class DirectoryTarResourceTest { | ||
|
@@ -41,6 +48,35 @@ public void simpleRecursiveFileTest() throws TimeoutException { | |
assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest")); | ||
} | ||
|
||
@Test | ||
public void simpleRecursiveFileWithPermissionTest() throws TimeoutException { | ||
|
||
WaitingConsumer wait = new WaitingConsumer(); | ||
|
||
final ToStringConsumer toString = new ToStringConsumer(); | ||
|
||
GenericContainer container = new GenericContainer( | ||
new ImageFromDockerfile() | ||
.withDockerfileFromBuilder(builder -> | ||
builder.from("alpine:3.3") | ||
.copy("/tmp/foo", "/foo") | ||
.cmd("ls", "-al", "/") | ||
.build() | ||
).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), | ||
0754)) | ||
.withStartupCheckStrategy(new OneShotStartupCheckStrategy()) | ||
.withLogConsumer(wait.andThen(toString)); | ||
|
||
container.start(); | ||
wait.waitUntilEnd(60, TimeUnit.SECONDS); | ||
|
||
String listing = toString.toUtf8String(); | ||
|
||
assertThat("Listing shows that file is copied with mode requested.", | ||
Arrays.asList(listing.split("\\n")), | ||
exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo")))); | ||
} | ||
|
||
@Test | ||
public void simpleRecursiveClasspathResourceTest() throws TimeoutException { | ||
// This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image | ||
|
@@ -68,4 +104,31 @@ public void simpleRecursiveClasspathResourceTest() throws TimeoutException { | |
// ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in | ||
assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt")); | ||
} | ||
|
||
public static <T> Matcher<Iterable<? super T>> exactlyNItems(final int n, Matcher<? super T> elementMatcher) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copied from stackoverflow. AssertJ has it built-in :\ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😬 sorry! |
||
return new IsCollectionContaining<T>(elementMatcher) { | ||
@Override | ||
protected boolean matchesSafely(Iterable<? super T> collection, Description mismatchDescription) { | ||
int count = 0; | ||
boolean isPastFirst = false; | ||
|
||
for (Object item : collection) { | ||
|
||
if (elementMatcher.matches(item)) { | ||
count++; | ||
} | ||
if (isPastFirst) { | ||
mismatchDescription.appendText(", "); | ||
} | ||
elementMatcher.describeMismatch(item, mismatchDescription); | ||
isPastFirst = true; | ||
} | ||
|
||
if (count != n) { | ||
mismatchDescription.appendText(". Expected exactly " + n + " but got " + count); | ||
} | ||
return count == n; | ||
} | ||
}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my intellij configured to not allow wildcard imports. Dunno what's your policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The policy is... clearly not well defined enough to be useful!
I've enabled a Codacy check to discourage wildcard imports and will check my IDE settings match for the future (I have no strong preference, but "shouldn't use" is easier to lint for than "should use").