Skip to content

Commit

Permalink
Introduce an abstraction over files and classpath resources. (#313)
Browse files Browse the repository at this point in the history
This encapsulates all the complexity of generating a path that the Docker daemon is about to create a volume mount for.

This should resolve a general problem with spaces in paths, which was seen in one particular form as #263.

Use recursive copy for docker context TAR to allow contents of directories to be used in Dockerfiles and Docker Compose contexts
  • Loading branch information
rnorth committed Apr 14, 2017
1 parent c2eae85 commit 7642f98
Show file tree
Hide file tree
Showing 19 changed files with 312 additions and 119 deletions.
14 changes: 14 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@
</exclusions>
</dependency>

<!-- Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest -->
<dependency>
<groupId>fakejar</groupId>
<artifactId>fakejar</artifactId>
<version>0</version>
<scope>test</scope>
</dependency>
</dependencies>

<!-- Prevent netty version conflicts with redisson, used for testing -->
Expand Down Expand Up @@ -232,4 +239,11 @@
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>testlib</id>
<url>file://${project.basedir}/testlib/repo</url>
</repository>
</repositories>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Uninterruptibles;
import org.apache.commons.lang.SystemUtils;
import org.junit.runner.Description;
import org.rnorth.ducttape.ratelimits.RateLimiter;
import org.rnorth.ducttape.ratelimits.RateLimiterBuilder;
Expand All @@ -32,6 +31,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.toList;
import static org.testcontainers.containers.BindMode.READ_ONLY;
import static org.testcontainers.containers.BindMode.READ_WRITE;

Expand Down Expand Up @@ -196,7 +196,7 @@ private List<Container> listChildContainers() {
.exec().stream()
.filter(container -> Arrays.stream(container.getNames()).anyMatch(name ->
name.startsWith("/" + identifier)))
.collect(Collectors.toList());
.collect(toList());
}

private void startAmbassadorContainers(Profiler profiler) {
Expand Down Expand Up @@ -394,15 +394,13 @@ public ContainerisedDockerCompose(List<File> composeFiles, String identifier) {
// Map the docker compose file into the container
final File dockerComposeBaseFile = composeFiles.get(0);
final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath();
final String containerPwd;
if (SystemUtils.IS_OS_WINDOWS) {
containerPwd = PathUtils.createMinGWPath(pwd).substring(1);
} else {
containerPwd = pwd;
}
final String containerPwd = MountableFile.forHostPath(pwd).getResolvedPath();

final List<String> absoluteDockerComposeFiles = composeFiles.stream().map(
file -> containerPwd + "/" + file.getAbsoluteFile().getName()).collect(Collectors.toList());
final List<String> absoluteDockerComposeFiles = composeFiles.stream()
.map(File::getAbsolutePath)
.map(MountableFile::forHostPath)
.map(MountableFile::getResolvedPath)
.collect(toList());
final String composeFileEnvVariableValue = Joiner.on(File.pathSeparator).join(absoluteDockerComposeFiles);
logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue);
addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import lombok.Cleanup;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -128,16 +127,12 @@ public void onNext(BuildResponseItem item) {
profiler.start("Send context as TAR");

try (TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(new GZIPOutputStream(out))) {
tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);

for (Map.Entry<String, Transferable> entry : transferables.entrySet()) {
TarArchiveEntry tarEntry = new TarArchiveEntry(entry.getKey());
Transferable transferable = entry.getValue();
tarEntry.setSize(transferable.getSize());
tarEntry.setMode(transferable.getFileMode());

tarArchive.putArchiveEntry(tarEntry);
transferable.transferTo(tarArchive);
tarArchive.closeArchiveEntry();

final String destination = entry.getKey();
transferable.transferTo(tarArchive, destination);
}
tarArchive.finish();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package org.testcontainers.images.builder;

import java.io.OutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.io.IOUtils;

import java.io.IOException;

public interface Transferable {

int DEFAULT_FILE_MODE = 0100644;
int DEFAULT_DIR_MODE = 040755;

/**
* Get file mode. Default is 0100644.
* @see Transferable#DEFAULT_FILE_MODE
*
* @return file mode
* @see Transferable#DEFAULT_FILE_MODE
*/
default int getFileMode() {
return DEFAULT_FILE_MODE;
Expand All @@ -26,7 +31,26 @@ default int getFileMode() {
/**
* transfer content of this Transferable to the output stream. <b>Must not</b> close the stream.
*
* @param outputStream stream to output
* @param tarArchiveOutputStream stream to output
* @param destination
*/
void transferTo(OutputStream outputStream);
default void transferTo(TarArchiveOutputStream tarArchiveOutputStream, final String destination) {
TarArchiveEntry tarEntry = new TarArchiveEntry(destination);
tarEntry.setSize(getSize());
tarEntry.setMode(getFileMode());

try {
tarArchiveOutputStream.putArchiveEntry(tarEntry);
IOUtils.write(getBytes(), tarArchiveOutputStream);
tarArchiveOutputStream.closeArchiveEntry();
} catch (IOException e) {
throw new RuntimeException("Can't transfer " + getDescription(), e);
}
}

default byte[] getBytes() {
return new byte[0];
}

String getDescription();
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package org.testcontainers.images.builder.traits;

import lombok.Getter;
import org.apache.commons.io.IOUtils;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;

import java.io.IOException;
import java.io.OutputStream;
import java.util.function.Consumer;

/**
Expand All @@ -33,12 +30,8 @@ public long getSize() {
}

@Override
public void transferTo(OutputStream outputStream) {
try {
IOUtils.write(getBytes(), outputStream);
} catch (IOException e) {
throw new RuntimeException("Can't transfer Dockerfile", e);
}
public String getDescription() {
return "Dockerfile: " + builder;
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package org.testcontainers.images.builder.traits;

import org.testcontainers.images.builder.Transferable;
import org.testcontainers.utility.MountableFile;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

/**
Expand All @@ -19,31 +16,7 @@ default SELF withFileFromFile(String path, File file) {
}

default SELF withFileFromPath(String path, Path filePath) {
return ((SELF) this).withFileFromTransferable(path, new Transferable() {

@Override
public long getSize() {
try {
return Files.size(filePath);
} catch (IOException e) {
throw new RuntimeException("Can't get size from " + filePath, e);
}
}

@Override
public int getFileMode() {
return DEFAULT_FILE_MODE | (Files.isExecutable(filePath) ? 0755 : 0);
}

@Override
public void transferTo(OutputStream outputStream) {
try {
Files.copy(filePath, outputStream);
} catch (IOException e) {
throw new RuntimeException("Can't transfer file " + filePath, e);
}
}

});
final MountableFile mountableFile = MountableFile.forHostPath(filePath);
return ((SELF) this).withFileFromTransferable(path, mountableFile);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package org.testcontainers.images.builder.traits;

import org.apache.commons.io.IOUtils;
import lombok.Getter;
import org.apache.commons.lang.StringUtils;
import org.testcontainers.images.builder.Transferable;

import java.io.IOException;
import java.io.OutputStream;

/**
* BuildContextBuilder's trait for String-based manipulations.
*
Expand All @@ -16,6 +13,7 @@ public interface StringsTrait<SELF extends StringsTrait<SELF> & BuildContextBuil
default SELF withFileFromString(String path, String content) {
return ((SELF) this).withFileFromTransferable(path, new Transferable() {

@Getter
byte[] bytes = content.getBytes();

@Override
Expand All @@ -24,14 +22,9 @@ public long getSize() {
}

@Override
public void transferTo(OutputStream outputStream) {
try {
IOUtils.write(bytes, outputStream);
} catch (IOException e) {
throw new RuntimeException("Can't transfer string " + StringUtils.abbreviate(content, 100), e);
}
public String getDescription() {
return "String: " + StringUtils.abbreviate(content, 100);
}

});
}
}
Loading

0 comments on commit 7642f98

Please sign in to comment.