-
Notifications
You must be signed in to change notification settings - Fork 149
composefs: Refactoring to use storage #1718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a significant and valuable refactoring to eliminate global state by introducing a Storage struct. This change greatly improves the code's testability and maintainability by making dependencies explicit and using file-descriptor-relative operations. The refactoring has been applied consistently across the composefs and ostree paths.
My review focuses on the correctness of this refactoring. I've found one logical issue in the command-line interface where backend-specific logic is not correctly dispatched, and a minor opportunity for code simplification. Overall, this is a great step forward for the codebase.
441ccf9 to
bfff92d
Compare
| switch_ostree(opts, storage, &booted_ostree).await | ||
| } | ||
| BootedStorageKind::Composefs(booted_cfs) => { | ||
| if opts.mutate_in_place { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For composefs we would require a hard reboot for any switch right? I'm not sure if we can switch without rebooting. Maybe we could for Type1 booted systems, but we will definitely need to reboot for UKIs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I think the same principle applies - yes a new UKI would have a new rootfs digest always and hence be different, but I think what we want here is to check the kernel version, and if that's the same, then we can do a soft reboot.
|
So
fails. This test is supposed to be run inside a container and should be expected to fail. I'm not sure how it was passing until now |
|
My bad, the issue was not the test failing, but the fact that previously we didn't error out if the system wasn't deployed via bootc, but now we do. |
|
@cgwalters Could you review my changes? |
|
Hmmm. Yes this is complicated. I am not quite happy with having storage call back into the CLI code, we should clean that up. I think we need to have a higher level wrapper here that returns |
|
Also this shows another bug, we definitely need to do the "re-exec self" dance even in the composefs path. |
Distinguishing isn't useful and we want to get away from having ostree repos in images anyways. Signed-off-by: Colin Walters <[email protected]>
We detect things later regardless, and this is prep for composefs. Signed-off-by: Colin Walters <[email protected]>
Use implicit return expressions instead of explicit `return` statements for the final expression in functions, following Rust conventions. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
Refactor the storage layer to introduce new types that better encapsulate boot context: - Add BootedStorage wrapper that detects ostree vs composefs boot - Add BootedOstree struct holding sysroot reference and deployment - Add BootedComposefs struct holding repository and cmdline - Add BootedStorageKind enum to discriminate between backends This allows passing boot context as a cohesive unit rather than separate parameters. Update the upgrade code path to use these new types: - Change get_status() to accept &BootedOstree instead of separate params - Update handle_staged_soft_reboot() similarly - Update upgrade() to use structs throughout Add comprehensive documentation to all new types and methods in store/mod.rs. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
Apply the same pattern used in upgrade() to switch, rollback, and edit commands. This eliminates redundant composefs_booted() calls from the main CLI code paths by consistently using storage.kind() for dispatch. Changes: - Split switch/rollback/edit into dispatcher + _ostree() helper functions - Update get_status_require_booted() to return BootedOstree instead of separate deployment/sysroot components - Update soft_reboot_rollback() to accept &BootedOstree - Update deploy::rollback() and install_reset() for new types - All command verbs now follow consistent pattern: storage.kind()? → match BootedStorageKind → call helper with &BootedOstree This change eliminates all composefs_booted() calls from crates/lib/src/cli.rs main code paths, passing boot context data down from BootedStorage instead. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
…state Update composefs command functions to accept and use Storage and BootedComposefs parameters instead of calling composefs_deployment_status() which scrapes global state. Changes: - Add get_composefs_status(storage, booted_cfs) helper in status.rs - Update upgrade_composefs() to use passed composefs.repo and get_composefs_status() - Update switch_composefs() to accept and use storage/booted_cfs parameters - Update composefs_rollback() to use storage.physical_root for opening directories - Update delete_composefs_deployment() to accept and pass through parameters - Update CLI dispatchers to pass storage and booted_cfs to all composefs functions This eliminates composefs_deployment_status() calls from command functions, completing the pattern established with BootedOstree for ostree commands. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
…tedComposefs Update composefs_gc(), composefs_backend_finalize(), and get_etc_diff() to accept Storage and BootedComposefs parameters instead of calling composefs_deployment_status() and opening hardcoded paths. Changes: - composefs_gc() now accepts storage and booted_cfs parameters - composefs_backend_finalize() uses storage.physical_root instead of hardcoded "/sysroot" - get_etc_diff() uses storage.physical_root instead of hardcoded "/sysroot" - Update CLI dispatchers to use BootedStorageKind pattern for finalize and config-diff - Remove unused imports from finalize.rs (open_dir, CWD) This eliminates remaining composefs_deployment_status() calls from gc.rs and finalize.rs, and removes several hardcoded "/sysroot" paths. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
… helper Add a `cwd: Option<&Dir>` parameter to `run_findmnt()` to support running findmnt with a working directory context. This allows inspecting filesystems using a Dir fd rather than requiring absolute paths. Also add `inspect_filesystem_of_dir()` as a convenience wrapper that inspects the filesystem of a Dir by calling findmnt on "." with the directory as cwd. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
Refactor `get_sysroot_parent_dev()` to accept a `physical_root: &Dir` parameter instead of hardcoding access to `/sysroot`. This continues the ongoing work to eliminate global state access in the composefs backend, following the same pattern as the recent BootedComposefs and BootedOstree refactoring. The function now uses `inspect_filesystem_of_dir()` which operates on a Dir fd. All callers updated to pass `&storage.physical_root` or open a Dir for the upgrade code paths. Helper functions like `list_bootloader_entries()` and `delete_depl_boot_entries()` also updated to accept and pass down the physical_root parameter. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
There were a few checks that asserted ostree booted system even for systems booted via composefs. Move the checks in `Storage::new` Signed-off-by: Pragyan Poudyal <[email protected]> Signed-off-by: Colin Walters <[email protected]>
Check for verity inside the json returned by `bootc status --json` and compare it with the compsefs digest from kernel cmdline Signed-off-by: Pragyan Poudyal <[email protected]> Signed-off-by: Colin Walters <[email protected]>
For ostree systems, before making any changes to the repo/boot entries we call `prepare_for_write`. Before this we were calling it unconditionally even if the command was `bootc status`, which was causing test failures. We pass in a flag now to `BootedStorage::new` which will conditionally call `prepare_for_write`. We also need this to make sure we keep the API more or less similar to what we had before Signed-off-by: Pragyan Poudyal <[email protected]> Signed-off-by: Colin Walters <[email protected]>
54d5d30 to
361795b
Compare
|
OK i tried to clean this up even more. There's a few prep commits here and then I added a final one which uses |
Change `BootedStorage::new()` to return `Result<Option<Self>>` instead of taking a `prep_for_write` boolean parameter. This makes the API more idiomatic and clearer about when the system is not booted via bootc. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
361795b to
4e07f74
Compare
|
LGTM |
PR bootc-dev#1718 introduced a regression where /sysroot was left writable after running `bootc status`. This occurred because BootedStorage::new() unconditionally calls set_mount_namespace_in_use(), which tells ostree it can safely remount /sysroot as writable. When sysroot.load() is called without actually being in a mount namespace, it leaves the global /sysroot writable. Fix by introducing an Environment enum that detects the runtime environment (ostree, composefs, container, or other) early in the execution flow. Callers now detect the environment and call prepare_for_write() if needed before constructing BootedStorage. This ensures a single call to prepare_for_write() per execution path and eliminates the previous layering violation where storage code called into CLI code. The Environment abstraction also makes it clearer when mount namespace setup is required and provides a foundation for future environment-specific behavior. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
PR bootc-dev#1718 introduced a regression where /sysroot was left writable after running `bootc status`. This occurred because BootedStorage::new() unconditionally calls set_mount_namespace_in_use(), which tells ostree it can safely remount /sysroot as writable. When sysroot.load() is called without actually being in a mount namespace, it leaves the global /sysroot writable. Fix by introducing an Environment enum that detects the runtime environment (ostree, composefs, container, or other) early in the execution flow. Callers now detect the environment and call prepare_for_write() if needed before constructing BootedStorage. This ensures a single call to prepare_for_write() per execution path and eliminates the previous layering violation where storage code called into CLI code. The Environment abstraction also makes it clearer when mount namespace setup is required and provides a foundation for future environment-specific behavior. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
PR bootc-dev#1718 introduced a regression where /sysroot was left writable after running `bootc status`. This occurred because BootedStorage::new() unconditionally calls set_mount_namespace_in_use(), which tells ostree it can safely remount /sysroot as writable. When sysroot.load() is called without actually being in a mount namespace, it leaves the global /sysroot writable. Fix by introducing an Environment enum that detects the runtime environment (ostree, composefs, container, or other) early in the execution flow. Callers now detect the environment and call prepare_for_write() if needed before constructing BootedStorage. This ensures a single call to prepare_for_write() per execution path and eliminates the previous layering violation where storage code called into CLI code. The Environment abstraction also makes it clearer when mount namespace setup is required and provides a foundation for future environment-specific behavior. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
PR bootc-dev#1718 introduced a regression where /sysroot was left writable after running `bootc status`. This occurred because BootedStorage::new() unconditionally calls set_mount_namespace_in_use(), which tells ostree it can safely remount /sysroot as writable. When sysroot.load() is called without actually being in a mount namespace, it leaves the global /sysroot writable. Fix by introducing an Environment enum that detects the runtime environment (ostree, composefs, container, or other) early in the execution flow. Callers now detect the environment and call prepare_for_write() if needed before constructing BootedStorage. This ensures a single call to prepare_for_write() per execution path and eliminates the previous layering violation where storage code called into CLI code. The Environment abstraction also makes it clearer when mount namespace setup is required and provides a foundation for future environment-specific behavior. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
PR #1718 introduced a regression where /sysroot was left writable after running `bootc status`. This occurred because BootedStorage::new() unconditionally calls set_mount_namespace_in_use(), which tells ostree it can safely remount /sysroot as writable. When sysroot.load() is called without actually being in a mount namespace, it leaves the global /sysroot writable. Fix by introducing an Environment enum that detects the runtime environment (ostree, composefs, container, or other) early in the execution flow. Callers now detect the environment and call prepare_for_write() if needed before constructing BootedStorage. This ensures a single call to prepare_for_write() per execution path and eliminates the previous layering violation where storage code called into CLI code. The Environment abstraction also makes it clearer when mount namespace setup is required and provides a foundation for future environment-specific behavior. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
A key goal here is to avoid global state and have as much as possible be fd-relative as
Storageencourages, so that things can work consistently in the install and update paths.