fix: fsync cache writes and tolerate corrupt network-config.json - #7080
Open
pujitha24 wants to merge 1 commit into
Open
fix: fsync cache writes and tolerate corrupt network-config.json#7080pujitha24 wants to merge 1 commit into
pujitha24 wants to merge 1 commit into
Conversation
atomic_helper._write_file() wrote a tempfile and renamed it into place without flushing or fsyncing first. On filesystems with delayed allocation (e.g. ext4), a sudden crash or power loss right after cloud-init writes an instance cache file could leave the renamed file present but empty (0 bytes), since the rename's directory metadata can reach the journal before the file's data is written back from the page cache. On the next boot, Init._write_network_config_json() read that cached network-config.json with no exception handling around the JSON parse, so a 0-byte or otherwise corrupt cache raised an unhandled JSONDecodeError/TypeError and cloud-init failed to initialize networking. This adds tf.flush() + os.fsync() (best-effort; a failing fsync is not treated as fatal, matching this function's existing handling of other non-critical OSErrors) before the rename in atomic_helper._write_file(), and wraps the network-config.json read in stages.py in a try/except for JSONDecodeError, TypeError, and ValueError, logging a warning and rewriting the cache instead of crashing. The except tuple mirrors the same util.load_json(util.load_text_file(...)) pattern already used elsewhere in stages.py. Validation: added tests/unittests/test_atomic_helper.py cases asserting os.fsync() is called before rename and that write_file() still succeeds if fsync() raises OSError, and a parametrized tests/unittests/test_stages.py case covering an empty cache file, truncated JSON, and syntactically-valid-but-wrong-type JSON (e.g. "null"). All three stages.py cases were confirmed to fail with an unhandled exception on the pre-fix code and pass after the fix. Ran pytest across tests/unittests/ (tox's py3 target): no new failures versus main; the only failures present (5, in test_all_stages.py, test_azure.py, and test_dump.py) are macOS-specific environment issues (AF_UNIX path length, a passlib/crypt platform quirk, and GNU-date detection) that reproduce identically on an unmodified checkout and are unrelated to this change. Also ran ruff, black --check, isort --check-only, pylint, and mypy against the changed files with no findings. This was not validated against an actual crash/power-loss on real hardware, since that cannot be reproduced in this environment; the fix is a standard flush+fsync-before-rename durability pattern and the JSON-handling fix is proven by the failing-then-passing tests above. Report: canonical#7078 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Assisted-by: claude-sonnet-5 (via Claude Code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed Commit Message
Additional Context
Report: #7078
Test Steps
tox -e py3(orpytest tests/unittests/test_atomic_helper.py tests/unittests/test_stages.py -v)instance's
network-config.jsoncache(
/var/lib/cloud/instance/network-config.json), then callInit()._write_network_config_json(some_netcfg_dict)— before this fix,this raises an unhandled
JSONDecodeError; after the fix it logs awarning and rewrites the cache.
Merge type
Fixes #7078