Draft: Add executor abstraction for provision step#4574
Conversation
When running prepare shell scripts on bootc guests, the commands need to be collected into a Containerfile and applied via image rebuild rather than executed immediately on the live (immutable) system. This change adds a `make_changes` parameter to the `execute()` method: - `make_changes=False` (default): Command executes immediately (probes, checks) - `make_changes=True`: Command may be collected for bootc Containerfile For bootc guests, commands with `make_changes=True` are collected and later applied by rebuilding the container image and rebooting. This allows prepare shell scripts to install packages and configure the system on immutable bootc systems. Key changes: - Add `make_changes` parameter to Guest.execute() with default False - Add GuestCommandCollector mixin for bootc command collection - Add is_bootc_guest property for detecting bootc systems - Set make_changes=True in prepare/shell.py for prepare scripts - Add on_step_complete() hook for flushing collected commands - Add integration test for prepare/shell on bootc guests Fixes: #4495 Assisted-by: Claude Code Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
When building bootc container images, scripts executed via RUN directives need access to files in the tmt working directory. This adds a volume mount to the podman build command that makes the run workdir available at the same path during build time. Changes: - Add -v option to podman build with :Z for SELinux private label - Change build context from step_workdir to run_workdir so the entire tmt run directory is available - Fix docstring to say "container image building" instead of "Containerfile execution" Assisted-by: Claude Code
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Replace the hacky is_bootc_guest property with a proper is_bootc fact in the GuestFacts class. The detection uses bootc status to check if the system is booted from a bootc image, following the same pattern as other guest facts (is_ostree, is_container, is_toolbox). Assisted-by: Claude Code
Based on review feedback, rename the `make_changes` parameter to `immediately` for better clarity. The new parameter has inverted semantics: - `immediately=True` (default): Execute command right away - `immediately=False`: Command may be collected for batch execution on bootc guests This naming better conveys the intent: commands are executed immediately by default, and only when explicitly marked with `immediately=False` can they be batched for later execution. Assisted-by Claude Code
1. rename fact `is_bootc` to `is_image_mode` 2. Rename GuestCommandCollector mixing to CommandCollector 3. remove bootc helper functions 4. rather name the things being done on image mode systems, and use less bootc to not confuse with bootc provisioner plugin Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Implemented nicely as a generic function in CommandCollector. Defined as no-op in Guest class. Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
Summary of ChangesHello @thrix, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new executor/execution_driver abstraction to cleanly separate command execution logic from guest connectivity. This refactoring enables more robust and flexible support for various provision types, particularly enhancing bootc/image-mode capabilities. The changes improve separation of concerns, increase composability and extensibility for future execution strategies, and facilitate independent unit testing of execution components, all while maintaining backward compatibility for the external Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
This implements an executor/execution_driver abstraction that separates command execution logic from guest connectivity, enabling cleaner bootc/image-mode support across different provision types. Key changes: - Add ExecutionDriver and DeferrableExecutor base classes - Implement SSHExecutor, LocalExecutor, ContainerExecutor, MockExecutor - Add BootcContainerfileExecutor and DeferrableSSHExecutor for bootc - Integrate executors with Guest classes via _create_executor() method - Remove CommandCollector mixin (replaced by executor pattern) The executor pattern provides: - Separation of concerns between transport and execution mode - Composability for adding new execution strategies - Backward compatibility with existing Guest.execute() API Addresses feedback from issue #4570 regarding bootc integration approach and code organization. Assisted-by: Claude Code
89909c9 to
6389135
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a significant refactoring by abstracting command execution into an ExecutionDriver, separating guest connectivity from execution logic to better support bootc and image-mode guests, and utilizes DeferrableExecutor for command batching. However, the implementation of the bootc executor and package manager contains critical security issues, including unquoted paths in tmt/package_managers/bootc.py and a lack of newline sanitization in tmt/steps/provision/executor/bootc.py, which could lead to command and directive injection vulnerabilities. Furthermore, the refactoring is incomplete, as GuestLocal, GuestContainer, and GuestMock have not been fully updated to use the new executor pattern, resulting in code duplication and inconsistencies in their execute methods.
I am having trouble creating individual review comments. Click here to see my feedback.
tmt/steps/provision/local.py (123)
The immediately parameter is added, but the method body is not updated to delegate to self.executor. The current implementation duplicates the logic from LocalExecutor and does not use the new executor pattern. This should be refactored to call self.executor.execute(...) to avoid code duplication and make the refactoring complete.
tmt/steps/provision/mock.py (543)
GuestMock inherits from GuestSsh, but its execute method overrides the parent's implementation without adopting the new executor pattern. The immediately parameter is added but unused.
This method should be refactored to delegate to self.executor for immediate execution and handle deferred execution (e.g., by calling super().execute(...) when immediately=False), similar to GuestSsh.execute. The current implementation duplicates logic from MockExecutor and prevents deferred execution from working on mock guests in image mode.
tmt/steps/provision/podman.py (437)
The immediately parameter is added, but the execute method body is not updated to delegate to self.executor. The current implementation duplicates the logic from ContainerExecutor and does not use the new executor pattern. This should be refactored to call self.executor.execute(...) to avoid code duplication and make the refactoring complete.
tmt/package_managers/bootc.py (280)
The podman build command constructed on line 280 uses self.guest.run_workdir multiple times without shell quoting. If the workdir path (which can be derived from the plan name in the test metadata) contains shell metacharacters such as spaces, semicolons, or backticks, it could lead to command injection or command failure on the guest. Given that tmt is often used to run tests from untrusted sources (e.g., pull quotes), this poses a security risk.
Recommendation: Use shlex.quote() for all path variables inserted into shell command strings, or preferably, use the Command class to build the command as a list of arguments to avoid shell interpretation issues.
tmt/steps/provision/executor/bootc.py (144)
In BootcContainerfileExecutor.defer, the collected_command is appended to a RUN directive in the Containerfile without sanitizing for newlines. If collected_command contains newlines (which is common for multi-line shell scripts provided in test metadata), it will terminate the RUN directive and the subsequent lines will be interpreted as new Containerfile directives. A malicious metadata author could use this to inject arbitrary directives such as COPY, USER, or ENV, potentially manipulating the image build process, accessing unauthorized files from the build context, or escalating privileges within the container.
Recommendation: Sanitize collected_command to ensure it does not contain unescaped newlines before appending it to the RUN directive, or use backslashes to escape newlines so the entire script remains part of the RUN directive.
Summary
Relates to #4570
This implements an executor/execution_driver abstraction that separates command execution logic from guest connectivity, enabling cleaner bootc/image-mode support across different provision types.
Key changes:
ExecutionDriverandDeferrableExecutorbase classes intmt/steps/provision/executor/SSHExecutor,LocalExecutor,ContainerExecutor,MockExecutorBootcContainerfileExecutorandDeferrableSSHExecutorfor bootc support_create_executor()methodCommandCollectormixin (replaced by executor pattern)Benefits:
Guest.execute()API unchangedArchitecture
Test plan
tests/prepare/bootc/test.sh- all 6 assertions pass🤖 Generated with Claude Code
Assisted-by: Claude Code