mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Delete layer temp file when layer creation fails
When ImageBuildpack.ExportedLayers creates a layer file, the copy from the source tar can fail, for example when a malformed entry name is rejected. The temporary layer file created before the copy was not deleted in that case and was left orphaned in the system temp directory. Delete the layer temp file when creating it fails, suppressing any secondary failure from the delete so the original exception is propagated. Signed-off-by: Junhwan Choi <devjunsday@gmail.com> See gh-51519
This commit is contained in:
committed by
Andy Wilkinson
parent
a833fe58f9
commit
5d6e417134
+28
-15
@@ -49,6 +49,7 @@ import org.springframework.util.StreamUtils;
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
* @author Junhwan Choi
|
||||
*/
|
||||
final class ImageBuildpack implements Buildpack {
|
||||
|
||||
@@ -137,24 +138,36 @@ final class ImageBuildpack implements Buildpack {
|
||||
tarArchive.writeTo(out);
|
||||
}
|
||||
Path layerFile = Files.createTempFile("create-builder-scratch-", null);
|
||||
try (TarArchiveOutputStream out = new TarArchiveOutputStream(Files.newOutputStream(layerFile))) {
|
||||
try (TarArchiveInputStream in = new TarArchiveInputStream(Files.newInputStream(sourceTarFile))) {
|
||||
out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
||||
TarArchiveEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
String entryName = entry.getName();
|
||||
Path entryPath = Path.of(entryName);
|
||||
Assert.state(entryPath.toAbsolutePath().equals(entryPath.toAbsolutePath().normalize()),
|
||||
() -> "Malformed zip entry name '%s'".formatted(entryName));
|
||||
out.putArchiveEntry(entry);
|
||||
StreamUtils.copy(in, out);
|
||||
out.closeArchiveEntry();
|
||||
entry = in.getNextEntry();
|
||||
try {
|
||||
try (TarArchiveOutputStream out = new TarArchiveOutputStream(Files.newOutputStream(layerFile))) {
|
||||
try (TarArchiveInputStream in = new TarArchiveInputStream(
|
||||
Files.newInputStream(sourceTarFile))) {
|
||||
out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
||||
TarArchiveEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
String entryName = entry.getName();
|
||||
Path entryPath = Path.of(entryName);
|
||||
Assert.state(entryPath.toAbsolutePath().equals(entryPath.toAbsolutePath().normalize()),
|
||||
() -> "Malformed zip entry name '%s'".formatted(entryName));
|
||||
out.putArchiveEntry(entry);
|
||||
StreamUtils.copy(in, out);
|
||||
out.closeArchiveEntry();
|
||||
entry = in.getNextEntry();
|
||||
}
|
||||
out.finish();
|
||||
}
|
||||
out.finish();
|
||||
}
|
||||
return layerFile;
|
||||
}
|
||||
catch (IOException | RuntimeException ex) {
|
||||
try {
|
||||
Files.deleteIfExists(layerFile);
|
||||
}
|
||||
catch (IOException suppressed) {
|
||||
ex.addSuppressed(suppressed);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
return layerFile;
|
||||
}
|
||||
finally {
|
||||
Files.deleteIfExists(sourceTarFile);
|
||||
|
||||
+20
@@ -61,6 +61,7 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
* @author Junhwan Choi
|
||||
*/
|
||||
class ImageBuildpackTests extends AbstractJsonTests {
|
||||
|
||||
@@ -200,6 +201,25 @@ class ImageBuildpackTests extends AbstractJsonTests {
|
||||
.withMessage("Malformed zip entry name '../cnb/'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLayerCreationFailsDeletesLayerTempFiles() throws Exception {
|
||||
File tempDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
Set<String> tempsBefore = listTempFileNames(tempDir, "create-builder-scratch-");
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
ImageReference imageReference = ImageReference.of("example/buildpack1:latest");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata()).willReturn(BuildpackLayersMetadata.fromJson("{}"));
|
||||
given(resolverContext.fetchImage(eq(imageReference), eq(ImageType.BUILDPACK))).willReturn(image);
|
||||
willAnswer((invocation) -> withMockLayers(invocation, "..")).given(resolverContext)
|
||||
.exportImageLayers(eq(imageReference), any());
|
||||
BuildpackReference reference = BuildpackReference.of("example/buildpack1");
|
||||
assertThatIllegalStateException().isThrownBy(() -> ImageBuildpack.resolve(resolverContext, reference))
|
||||
.withMessage("Malformed zip entry name '../cnb/'");
|
||||
Set<String> tempsAfter = new HashSet<>(listTempFileNames(tempDir, "create-builder-scratch-"));
|
||||
tempsAfter.removeAll(tempsBefore);
|
||||
assertThat(tempsAfter).as("layer temp files must be deleted when layer creation fails").isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDeletesIntermediateSourceTarTempFiles() throws Exception {
|
||||
File tempDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
Reference in New Issue
Block a user