diff --git a/CHANGES/6975.bugfix b/CHANGES/6975.bugfix new file mode 100644 index 00000000000..b5759be879f --- /dev/null +++ b/CHANGES/6975.bugfix @@ -0,0 +1 @@ +Clean up on-demand temporary file left over on an aborted download. diff --git a/pulpcore/content/handler.py b/pulpcore/content/handler.py index aca17943262..152d3076c27 100644 --- a/pulpcore/content/handler.py +++ b/pulpcore/content/handler.py @@ -1136,8 +1136,10 @@ async def handle_data(data): await original_handle_data(data) async def finalize(): + nonlocal failed_download if save_artifact and remote.policy != Remote.STREAMED: await original_finalize() + failed_download = False downloader = remote.get_downloader( remote_artifact=remote_artifact, @@ -1147,6 +1149,7 @@ async def finalize(): downloader.handle_data = handle_data original_finalize = downloader.finalize downloader.finalize = finalize + failed_download = True try: download_result = await downloader.run( extra_data={"disable_retry_list": (DigestValidationError,)} @@ -1169,6 +1172,17 @@ async def finalize(): "Learn more on " ) + finally: + if failed_download: + if downloader.path: + await sync_to_async(os.unlink)(downloader.path) + + # manually close the DownloadFactory's (HTTP-)session, the next artifact-download will + # create a new DownloadFactory anyway because it will use a new remote-object. + # Leaving it open also left open file-descriptors to /dev/urandom. Most likely these FDs + # are held by the underlying SSL library. + if hasattr(downloader, "session"): + await downloader.session.close() if content_length := response.headers.get("Content-Length"): response.headers["X-PULP-ARTIFACT-SIZE"] = content_length @@ -1177,12 +1191,6 @@ async def finalize(): response.headers["X-PULP-ARTIFACT-SIZE"] = str(size) artifacts_size_counter.add(size) - # manually close the DownloadFactory's (HTTP-)session, the next artifact-download will - # create a new DownloadFactory anyway because it will use a new remote-object. - # Leaving it open also left open file-descriptors to /dev/urandom. Most likely these FDs are - # held by the underlying SSL library. - await downloader.session.close() - if save_artifact and remote.policy != Remote.STREAMED: await asyncio.shield( sync_to_async(self._save_artifact)(download_result, remote_artifact, request)