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 @@ -15,15 +15,15 @@
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

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.time.Duration;
import java.util.Comparator;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
* Testcontainers module for <a href="https://github.com/floci-io/floci">Floci</a> — a
Expand Down Expand Up @@ -174,18 +174,69 @@ public FlociContainer(DockerImageName dockerImageName) {

@Override
public void stop() {
preparePersistentStorageForCleanup();
super.stop();
deletePersistentStorage();
}

private void preparePersistentStorageForCleanup() {
if (storageConfig.getHostPersistentPath().isEmpty() || !isRunning()) {
return;
}

// Child containers can create root-owned files in Floci's bind-mounted data directory.
try {
ExecResult result = execInContainer("chmod", "-R", "a+rwX", "/app/data");
if (result.getExitCode() != 0) {
logger.warn("Failed to make Floci persistent storage deletable: {}", result.getStderr());
}
} catch (IOException e) {
logger.warn("Failed to make Floci persistent storage deletable", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Interrupted while making Floci persistent storage deletable", e);
}
}

// Cleanup persistent storage if a host path was configured
private void deletePersistentStorage() {
storageConfig.getHostPersistentPath().ifPresent(path -> {
try (Stream<Path> paths = Files.walk(path)) {
paths.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
try {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
deletePath(file);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFileFailed(Path file, IOException failure) {
logger.warn("Failed to visit Floci persistent storage path {}", file, failure);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path directory, IOException failure) {
if (failure != null) {
logger.warn("Failed to visit Floci persistent storage directory {}", directory, failure);
}
deletePath(directory);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
// Ignore exception silently
logger.warn("Failed to delete Floci persistent storage at {}", path, e);
}
});
}

private static void deletePath(Path path) {
try {
Files.deleteIfExists(path);
} catch (IOException e) {
logger.warn("Failed to delete Floci persistent storage path {}", path, e);
}
}
Comment thread
nineinchnick marked this conversation as resolved.

/**
* Returns the endpoint URL for connecting to Floci (e.g. {@code http://localhost:32781}).
*
Expand Down Expand Up @@ -2214,4 +2265,4 @@ private void configureEnvVars() {
private static String uniqueShortId() {
return UUID.randomUUID().toString().replace("-", "").substring(0, 8);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.images.builder.Transferable;

import java.nio.file.Path;

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

Expand Down Expand Up @@ -147,4 +149,21 @@ void shouldWaitForStartScriptsToComplete() throws Exception {
.getExitCode()).isZero();
}
}

@Test
void shouldDeleteContainerOwnedPersistentStorage() throws Exception {
FlociContainer container = new FlociContainer()
.withStorageConfig(config -> config.randomHostPersistentPath());
Path persistentStorage = container.getStorageConfig().getHostPersistentPath().orElseThrow();
try {
container.start();
var result = container.execInContainer(
"sh", "-c", "mkdir -p /app/data/restricted && touch /app/data/restricted/file && chmod 000 /app/data/restricted");
assertThat(result.getExitCode()).isZero();
} finally {
container.stop();
}

assertThat(persistentStorage).doesNotExist();
}
}