fix: clean up temporary file after table maintenance job execution#245
fix: clean up temporary file after table maintenance job execution#245iemejia wants to merge 3 commits into
Conversation
The table maintenance command created a temporary file with NamedTemporaryFile(delete=False) to pass job configuration, but never removed it after the job completed. The temp file persisted in the system temp directory indefinitely on every invocation. Wrap the job execution in a try/finally block to ensure os.unlink() is called, removing the temp file even if the job fails. CWE-459: Incomplete Cleanup
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds robust cleanup for the temporary job config file used by fab tables opt, and introduces tests to ensure the temp file is removed in both success and failure scenarios.
Changes:
- Ensure the temp config file is always deleted via
try/finallyinexec_command. - Add tests validating temp-file cleanup on success and on error.
- Add a test validating the written temp config contents match expected job configuration.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/fabric_cli/commands/tables/fab_tables_opt.py |
Ensures temp config file is unlinked even when jobs.run_command raises. |
tests/test_core/test_fab_tables_opt.py |
Adds coverage for temp file cleanup and correct config serialization. |
| try: | ||
| jobs.run_command(args) | ||
| finally: | ||
| os.unlink(temp_file.name) |
There was a problem hiding this comment.
Fixed. Wrapped with contextlib.suppress(OSError) (broadened from FileNotFoundError to also cover PermissionError on Windows).
| try: | ||
| fab_tables_opt.exec_command(args) | ||
| except RuntimeError: | ||
| pass |
There was a problem hiding this comment.
Fixed. Replaced with pytest.raises(RuntimeError, match="Job failed") so the test fails if the exception is not thrown.
8709239 to
b5a3394
Compare
| args.configuration = None | ||
| args.input = temp_file.name | ||
| args.params = None | ||
| jobs.run_command(args) | ||
|
|
||
| try: | ||
| jobs.run_command(args) | ||
| finally: | ||
| with contextlib.suppress(FileNotFoundError): | ||
| os.unlink(temp_file.name) |
There was a problem hiding this comment.
Fixed. Moved job execution and cleanup outside the with tempfile.NamedTemporaryFile block so the file handle is closed before os.unlink. Captured the path as temp_path = temp_file.name before exiting the with block.
…suppress OSError Move job execution outside the NamedTemporaryFile with block so the file handle is closed before os.unlink, which is required on Windows. Broaden suppression to OSError (covers PermissionError on Windows too).
| args.input = temp_path | ||
| args.params = None | ||
|
|
||
| try: |
There was a problem hiding this comment.
Please move the try so it starts right after the temp file is created (before the open). Once NamedTemporaryFile(delete=False) returns, the file already exists on disk, so if open or json.dump raises (e.g. a PermissionError/OSError, or a non-serializable config), the file leaks before we ever reach the cleanup.
| from fabric_cli.commands.tables import fab_tables_opt | ||
|
|
||
|
|
||
| def test_exec_command_cleans_up_temp_file(): |
There was a problem hiding this comment.
each test's name should end with suffix _success/_failure depending on what the test is testing
| ), "Temp file should be cleaned up after job execution" | ||
|
|
||
|
|
||
| def test_exec_command_cleans_up_temp_file_on_error(): |
There was a problem hiding this comment.
missing cases like file open errors (lack permissions for example) or serialization error
| created_temp_files.append(a.input) | ||
| raise RuntimeError("Job failed") | ||
|
|
||
| with patch( |
There was a problem hiding this comment.
Consider creating a fixture and use it instead of defining it multiple time
| import pytest | ||
|
|
||
| from fabric_cli.commands.tables import fab_tables_opt | ||
|
|
There was a problem hiding this comment.
tests of command should be under tests/test_commands and not under tests/test_core.
please move it to the correct path and use the test_command guidelines.
Summary
The table maintenance command (
fab tables optimize/fab tables vacuum) created a temporary file viaNamedTemporaryFile(delete=False)to pass job configuration to the job runner, but never removed it after the job completed. The temp file persisted in the system temp directory indefinitely on every invocation.Problem
Each invocation of the table maintenance command left an orphaned file in the system temp directory (
/tmp/on Linux,%TEMP%on Windows). Over time, this accumulated files containing table maintenance job configuration (table names, schema names, maintenance parameters).CWE-459: Incomplete Cleanup
Fix
Wrap the job execution in a
try/finallyblock to ensureos.unlink()is called, removing the temp file even if the job fails with an exception.Tests
3 new tests:
test_exec_command_cleans_up_temp_file— verifies the temp file is removed after successful executiontest_exec_command_cleans_up_temp_file_on_error— verifies the temp file is removed even when the job raises an exceptiontest_exec_command_writes_correct_config_to_temp_file— verifies the config content (table name, schema, parameters) is written correctlyAll existing tests pass (488 total).