Warn when image building workspace directories cannot be deleted

When the `buildWorkspace` location in the
`spring-boot:build-image` Maven goal or `bootBuildImage` Gradle
task is configured to use a local bind source, the location
is passed to the CNB lifecycle without further processing by
Spring Boot. The lifecycle is in control of creating any files
in the specified location. Spring Boot tries to remove the
directories at the specified location after an image is
successfully created, but should not fail the image build
if the lifecycle has created files or directories with
permissions that keep them from being deleted successfully.

Fixes gh-40760
This commit is contained in:
Scott Frederick 2024-05-16 17:37:44 -05:00
parent 524424bc98
commit cd441130bd
5 changed files with 26 additions and 14 deletions

View File

@ -102,6 +102,17 @@ public abstract class AbstractBuildLog implements BuildLog {
log();
}
@Override
public void failedCleaningWorkDir(Cache cache, Exception exception) {
StringBuilder message = new StringBuilder("Warning: Working location " + cache + " could not be cleaned");
if (exception != null) {
message.append(": ").append(exception.getMessage());
}
log();
log(message.toString());
log();
}
private String getDigest(Image image) {
List<String> digests = image.getDigests();
return (digests.isEmpty() ? "" : digests.get(0));

View File

@ -114,6 +114,13 @@ public interface BuildLog {
*/
void taggedImage(ImageReference tag);
/**
* Log that a cache cleanup step was not completed successfully.
* @param cache the cache
* @param exception any exception that caused the failure
*/
void failedCleaningWorkDir(Cache cache, Exception exception);
/**
* Factory method that returns a {@link BuildLog} the outputs to {@link System#out}.
* @return a build log instance that logs to system out

View File

@ -311,7 +311,7 @@ class Lifecycle implements Closeable {
deleteVolume(cache.getVolume().getVolumeName());
}
if (cache.getBind() != null) {
deleteBind(cache.getBind().getSource());
deleteBind(cache.getBind());
}
}
@ -319,12 +319,12 @@ class Lifecycle implements Closeable {
this.docker.volume().delete(name, true);
}
private void deleteBind(String source) {
private void deleteBind(Cache.Bind bind) {
try {
FileSystemUtils.deleteRecursively(Path.of(source));
FileSystemUtils.deleteRecursively(Path.of(bind.getSource()));
}
catch (IOException ex) {
throw new IllegalStateException("Error cleaning bind mount directory '" + source + "'", ex);
this.log.failedCleaningWorkDir(bind, ex);
}
}

View File

@ -36,7 +36,6 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
@ -301,10 +300,8 @@ class BootBuildImageIntegrationTests {
}
@TestTemplate
@EnabledOnOs(value = OS.LINUX,
disabledReason = "Works with Docker Engine on Linux but is not reliable with "
+ "Docker Desktop on other OSs")
@Disabled("gh-40760")
@EnabledOnOs(value = OS.LINUX, disabledReason = "Works with Docker Engine on Linux but is not reliable with "
+ "Docker Desktop on other OSs")
void buildsImageWithBindCaches() throws IOException {
writeMainClass();
writeLongNameResource();

View File

@ -25,7 +25,6 @@ import java.time.OffsetDateTime;
import java.util.Random;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
@ -402,10 +401,8 @@ class BuildImageTests extends AbstractArchiveIntegrationTests {
}
@TestTemplate
@EnabledOnOs(value = OS.LINUX,
disabledReason = "Works with Docker Engine on Linux but is not reliable with "
+ "Docker Desktop on other OSs")
@Disabled("gh-40760")
@EnabledOnOs(value = OS.LINUX, disabledReason = "Works with Docker Engine on Linux but is not reliable with "
+ "Docker Desktop on other OSs")
void whenBuildImageIsInvokedWithBindCaches(MavenBuild mavenBuild) {
String testBuildId = randomString();
mavenBuild.project("build-image-bind-caches")