Skip to content

Close streams and delete intermediate temps in ImageBuildpack#50919

Open
SebTardif wants to merge 1 commit into
spring-projects:mainfrom
SebTardif:fix/image-buildpack-temp-and-stream-cleanup
Open

Close streams and delete intermediate temps in ImageBuildpack#50919
SebTardif wants to merge 1 commit into
spring-projects:mainfrom
SebTardif:fix/image-buildpack-temp-and-stream-cleanup

Conversation

@SebTardif

Copy link
Copy Markdown
Contributor

Summary

Close the layer InputStream and delete the intermediate source tar temp file in ImageBuildpack.ExportedLayers so image buildpack exports do not leak file handles or leave orphaned files under the system temp directory.

Problem

When resolving an image buildpack that is not already present in the builder, ExportedLayers materializes each exported layer as temp files:

  1. Intermediate source tar never deleted. createLayerFile writes create-builder-scratch-source-*, rewrites it into create-builder-scratch-*, then returns only the rebased file. The source temp is never deleted. Each OCI image buildpack export therefore leaves one orphaned source tar per layer in java.io.tmpdir.

  2. Layer stream not closed on apply. apply opens the rebased layer with Files.newInputStream and copies with StreamUtils.copy, which does not close either stream. The open handle can prevent Files.delete(path) from succeeding on Windows and keeps a file descriptor open until GC.

Sibling paths in the same module (TarGzipBuildpack, the try-with-resources block earlier in createLayerFile) already close streams correctly.

Failure scenario

  1. bootBuildImage (or the buildpack platform API) resolves a docker://… buildpack that is not already in the builder.
  2. exportImageLayers runs and createLayerFile creates both temp files per layer.
  3. After construction, only the rebased create-builder-scratch-* file is tracked; the create-builder-scratch-source-* file remains on disk indefinitely.
  4. On apply, the layer stream is left open after copy, which is unnecessary retention and can block deletion on Windows.

Observable impact: gradual growth of large tar files under the temp directory during repeated image builds, plus possible failed temp cleanup.

Change

  • Delete sourceTarFile in a finally block after the rebased layer file has been written (and its streams closed).
  • Use try-with-resources for the InputStream in apply before StreamUtils.copy.

Validation

Red step:  FAIL (expected) - resolveDeletesIntermediateSourceTarTempFiles without the production fix
  (asserted newly created create-builder-scratch-source-* files remain after resolve)

Green step: PASS - ./gradlew :buildpack:spring-boot-buildpack-platform:test \
  --tests "org.springframework.boot.buildpack.platform.build.ImageBuildpackTests"
  (11 tests)

Also: checkFormatMain, checkFormatTest, checkstyleMain, checkstyleTest for the module all pass.

Origin

  • Intermediate source temp pattern introduced while adding gzip / containerd-friendly layer export support (9e409702807e, Phillip Webb, 2024-04-23, "Support gzip compressed image layers").
  • Apply-path stream open pattern present since image-manifest-based layer export (7730eee43998, Scott Frederick, 2023-02-27, "Use image manifest when exporting layers").

Related prior cleanup in the same module: #50639 (close FileOutputStream delegate in InspectingOutputStream).

Related

ExportedLayers left the intermediate create-builder-scratch-source
temp file behind after rebased layer files were written, and opened
layer InputStreams without closing them when StreamUtils.copy does
not close either stream. Delete the source temp in a finally block
and use try-with-resources for the apply path so layer files can be
deleted reliably.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants